This is an extension of what @lurker has mentioned in the comments to the question. It does not matter whether you use a class variable or a class level instance variable, you will see the same behaviour.
However what happens behind the scenes is different when you use the two types of variables.
Case 1 : Class variable (i.e. @@var
)
The value of the class variable is being set during class declaration, i.e. when the Ruby source code is read and that happens only once.
There are two things to keep in mind here :
- Rails follows lazy loading, i.e. it loads up the definition of a class when it requires it for the first time.
- So when you hit
url_a
for the first time class A
is loaded, i.e. its source is parsed
class B
is not yet loaded. It gets loaded later when you hit url_b
.
- When a Ruby source file is parsed, all the code that is outside any function is executed right away. So the
Base.result =
method calls are executed when the two classes are loaded.
So the sequence of steps would be :
- In the call to
url_a
, class A
is parsed and Base.result = a
sets @@var
to a
- Then in the call to
url_b
, class b
is parsed and Base.result = b
sets @@var
to b
and it remains so for all subsequent calls.
The following code snippet might help understand the second point :
irb(main):033:0> class ParseTest
irb(main):034:1> @@time_now = Time.now
irb(main):035:1> def speak_time
irb(main):036:2> puts @@time_now.to_s
irb(main):037:2> end
irb(main):038:1> end
=> nil
irb(main):039:0> pt = ParseTest.new
=> #<ParseTest:0x007f80758514c8>
irb(main):040:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):041:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):042:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):043:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):044:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):045:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):046:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):047:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):048:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):049:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):050:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):051:0> class ParseTest2 < ParseTest
irb(main):052:1> @@time_now = Time.now
irb(main):053:1> end
=> "2014-09-18T23:16:41.911+05:30"
irb(main):054:0> pt.speak_time
2014-09-18 23:16:41 +0530
=> nil
irb(main):055:0>
As you can see, after the ParseTest
class definition was parsed once, the value of @@time_now
did not change in any of the subsequent puts
. The value of time was what it was when the source code was parsed.
However when I defined the ParseTest2
sub-class and its code was parsed, the same class variable was given a new value of time. This new value is reflected when I print it using the same old object of the base class.
This is what is happening in your code too.
Case 2 : Class level instance variable (i.e. @var
in class definition outside any instance function)
Now, if instead of a class variable you use an instance variable in the class definition (i.e. outside any function), that is a very different. This case might appear a little confusing, so read and re-read the following code snippet if it appears confusing to you in the first go.
irb(main):089:0> class Base
irb(main):090:1> @time_now = Time.now
irb(main):091:1>
irb(main):092:1* def self.time_now=(time)
irb(main):093:2> @time_now = time
irb(main):094:2> end
irb(main):095:1>
irb(main):096:1* def self.time_now
irb(main):097:2> puts @time_now.to_s
irb(main):098:2> end
irb(main):099:1> end
=> nil
irb(main):100:0> class A < Base
irb(main):101:1> Base.time_now = Time.now
irb(main):102:1> end
=> "2014-09-18T23:33:26.514+05:30"
irb(main):103:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):104:0> A.time_now
=> nil
irb(main):105:0> A.time_now = Time.now
=> "2014-09-18T23:34:27.093+05:30"
irb(main):106:0> A.time_now
2014-09-18 23:34:27 +0530
=> nil
irb(main):107:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):108:0>
The class level instance variable is private to that class
. It does not get passed on / shared during inheritance. So every class in the inheritance hierarchy has its own set of instance variables. However the methods do get passed on and those methods act on the instance variable of the class on which they were called. So depending on which class you call the time_now=
setter method, the corresponding instance variable is set.
In your case you are always referring to the instance variable of the Base
class. So the same set of steps happen as described in the previous case
- In the call to
url_a
, class A
is parsed and Base.result = a
sets @var
of Base
to a
- Then in the call to
url_b
, class b
is parsed and Base.result = b
sets @var
of Base
to b
and it remains so for all subsequent calls.