Ruby, class variables and inheritance ? super class ##var gets changed? - ruby

Any Ruby guru that explain this?
class Bar
##x = 10
def self.test
return ##x
end
end
class Foo < Bar
##x = 20
end
puts Bar.test # 20 why not 10?
puts Foo.test # 20
When i run this from TextMate. I would expect that
puts Bar.test returns 10
and
puts Foo.test returns 20
But for some reason (that i would love to know) ##x in Foo updates Bar as-well, which is the super class. What is it i'm missing?

This is to be expected. Class variables are shared within the hierarchy. See section in Wikipedia: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Class_Variables
Compare this to class instance variables, which are private to that class only.
class Bar
#x = 10
def self.test
return #x
end
end
class Foo < Bar
#x = 20
end
Bar.test # => 10
Foo.test # => 20

Related

Ruby - instance variable as class variable

I have a class:
class Foo
def self.test
#test
end
def foo
#test = 1
bar
end
private
def bar
#test = 2
end
end
object = Foo.new.foo
Foo.test
# => nil
The only way I could get it to output '2' is by making #test a class variable. Is there any other way around using the instance variable and being able to display it with Foo.test?
It's not really clear to me what you want to achieve, and why. Here's an example with a "class instance variable". It might be what you're looking for:
class Foo
class << self
attr_accessor :test
end
attr_accessor :test
def foo
#test = 1
bar
end
private
def bar
Foo.test = 2
end
end
foo = Foo.new
foo.foo
p foo.test
#=> 1
p Foo.test
#=> 2

Cache class method instance variable in Ruby

In this example code where I have child classes (Bar, Baz) inheriting the class methods of the parent (Foo), how might I ensure #foo is only created once across all children?
class Foo
def self.foo
# only want #foo to be set once across any child classes
# that may call this inherited method.
#foo ||= expensive_operation
end
end
class Bar < Foo
def self.bar
self.foo + 'bar'
end
end
class Baz < Foo
def self.baz
self.foo + 'baz'
end
end
Instead of depending on a class-specific instance variable being present, just reference it directly:
class Baz < Foo
def self.baz
Foo.foo + 'baz'
end
end
This is exactly what class instance variable is intended for - language mechanism to have one variable shared between classes which inherit from a class. It's generally not recommended to use because it behaves like you want to - and that's confusing for people coming from other languages.
class Foo
def self.foo
# notice ##
##foo ||= expensive_operation
end
def self.expensive_operation
puts "Expensive operation"
"cached value "
end
end
class Bar < Foo
def self.bar
self.foo + 'bar'
end
end
class Baz < Foo
def self.baz
self.foo + 'baz'
end
end
Foo.foo
Bar.bar
Baz.baz
This prints Expensive operation only once.

Ruby attr_accessor multiple classes

Sorry for the newbie question but how do I pass/access attar_accessor to and from multiple classes? in my example below the Foo class never is able to see the updated values from the Level class.
class Level
attr_accessor :level, :speed
end
class Foo
attr_reader :a, :b
def initialize
#x = Level.new()
#a = #x.level
#b = #x.speed
end
def reload
#a = #x.level
#b = #x.speed
end
end
#testa = Foo.new()
#testb = Level.new()
#testb.level = 5
#testb.speed = 55
puts "Value for attr_accessor is:"
puts "#{#testb.level}"
puts "#{#testb.speed}"
puts "#{#testa.a}"
puts "#{#testa.b}"
#testa.reload
puts "#{#testa.a}"
puts "#{#testa.b}"
Instance of Level class you declaring in Foo's constructor and #testb are two different objects.
You might want to edit your Foo class this way:
class Foo
def initialize(level)
#x = level # very strange name for such a thing.
#a = #x.level
#b = #x.speed
end
# rest of the class body is the same as yours
# so it is omitted
end
And then do your tests:
level = Level.new() # BTW: no need of instance variables here.
foo = Foo.new(level) # good job. Your foo has "captured" the level.
# et cetera

why there are class variables in ruby?

If creating a class variable is often dangerous and unpredictable why do we need them?
If solution is just to use class instance variable with the class level accessors:
class Foo
#variable = :something
def self.getvariable
#variable
end
def self.setvariable(value)
#variable = value
end
end
Then why do we need class variables???
Class variables have their use on occasion, but I agree that using the eigenclass is frequently more useful:
class Foo
#bar = 'bar'
class << self
attr_accessor :bar
end
end
puts Foo.bar # bar
puts Foo.bar = 'baz' # baz
The above is safe with inheritance, because it sets a variable in the Foo constant, rather than a class variable.
Foo.new.instance_eval { puts ##bar } # error
This has several causes:
It's syntactic sugar. You can always get a class variable (whether you are in class or instance scope) using ##var. This won't work for instance variables of the class.
Class variables persist for the singleton classes of the instances of this class. Example:
class Test
#instance_var = 0
##class_var = 0
def self.instance_var
#instance_var
end
def self.class_var
##class_var
end
end
Test.instance_var #=> 0
Test.class_var #=> 0
Test.new.singleton_class.instance_var #=> nil
Test.new.singleton_class.class_var #=> 0
Here is an example (think ActiveRecord):
class Base
def connect(connection)
##connection = connection
end
def connection
##connection
end
end
class User < Base
end
class SuperUser < User
end
Base.new.connect("A connection")
puts User.new.connection #=> A connection
puts SuperUser.new.connection #=> A connection
The trick here is that class variable is accessible from an instance method and is inherited. Try this:
class Base
def self.connect(connection)
#connection = connection
end
def self.connection
#connection
end
def connection
self.class.connection
end
end
class User < Base
end
Base.connect("A connection")
puts User.new.connection #=> nil
You will get nil as self.connection tries to access it's own class instance variable (from User class), and it is not inherited.
Added: And yes, it can be dangerous if you misuse it:
##a = "A"
class A
def self.a
##a
end
def a
##a
end
end
puts A.a #=> A
puts A.new.a #=> A

Calling Super Methods in Ruby

I am trying to define some classes in Ruby that have an inheritance hierarchy, but I want to use one of the methods in the base class in the derived class. The twist is that I don't want to call the exact method I'm in, I want to call a different one. The following doesn't work, but it's what I want to do (basically).
class A
def foo
puts 'A::foo'
end
end
class B < A
def foo
puts 'B::foo'
end
def bar
super.foo
end
end
Probably, this is what you want?
class A
def foo
puts 'A::foo'
end
end
class B < A
alias bar :foo
def foo
puts 'B::foo'
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo
A more general solution.
class A
def foo
puts "A::foo"
end
end
class B < A
def foo
puts "B::foo"
end
def bar
# slightly oddly ancestors includes the class itself
puts self.class.ancestors[1].instance_method(:foo).bind(self).call
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo

Resources