class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
var = "bar"
p var # print "bar"
end
p Bar.var # print nil
Why Bar.var not return "bar"?
how can i add getter/setter for class variables?
class Bar < Foo
var = "bar" # this is assignment to local variable, not the accessor
end
Use self to tell ruby that you want to call the method, not create local variable.
class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
self.var = "bar"
var # => "bar"
end
Bar.var # => "bar"
Related
Here is an external class which has its class method overwritten.
class Foo
def class
"fooo"
end
end
class Boo < Foo
end
class Moo < Foo
end
Now I have an instance of the subclass. Is it possible to find out which class it belongs to?
foo.class # currently returns 'fooo', I want get Boo or Moo.
You could use instance_method to grab the class method from somewhere safe (such as Object) as an UnboundMethod, bind that unbound method to your instance, and then call it. For example:
class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class>
class_method.bind(Boo.new).call
# Boo
class_method.bind(Moo.new).call
# Moo
class_method.bind(Foo.new).call
# Foo
Of course, if you've also replaced Object#class (or Kernel#class) then all bets are off and you're in a whole new world of pain and confusion.
An object's class also happens to be the superclass of its singleton_class:
Boo.new.singleton_class.superclass
#=> Boo
Moo.new.singleton_class.superclass
#=> Moo
Foo.new.singleton_class.superclass
#=> Boo
I prefer #Stefan’s and #muistooshort's solutions, but here's an alternative.
class Foo
def class
"foo"
end
end
class Boo < Foo
end
class Who < Boo
def class
"who"
end
end
boo = Boo.new
boo.method(:class).super_method.call
#=> Boo
who = Who.new
who.method(:class).super_method.call
#=> "foo"
who.method(:class).super_method.super_method.call
#=> Who
More generally:
def my_class(obj)
m = obj.method(:class)
until m.owner == Kernel do
m = m.super_method
end
m.call
end
my_class(boo)
#=> Boo
my_class(who)
#=> Who
See Method#super_method.
This solution is a little hacky, but overriding class is pretty hacky in itself, so when in Rome, do as Romans do:
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
boo = Boo.new
=> #<Boo:0x00007fd2361feba8>
boo.class
=> "fooo"
boo.inspect
=> "#<Boo:0x00007fd2361feba8>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
This also works for classes in a module:
module Bar
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
end
boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>
boo.class
=> "fooo"
boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
I have the following piece of code:
class Fish
# #message = "I can swim"
class << self
#message = "I can jump!"
define_method(:action) { #message }
end
end
Fish.action => nil
As soon as I uncomment the above #message variable, Fish.action returns I can swim. Why in both cases it is ignoring the I can jump message. Why is that? Why is Fish class being binded to the #message defined at the start but not inside the singleton class?
It's because class << self opens the class' singleton class context:
class Foo
p self # Foo
class << self
p self # #<Class:Foo>
define_method(:bar) { p self } # Foo
end
end
Foo.bar
You can verify that by:
Fish.singleton_class.instance_variable_get(:#action) # => "I can jump!"
Can someone explain why self.foo= works outside of the class << self, while foo= does not work within the class << self.
class A
class << self
attr_accessor :foo
foo = 'foo'
end
end
p A.foo # => "nil"
class A
class << self
attr_accessor :foo
end
self.foo = 'foo'
end
p A.foo # => "foo"
This is not the same question as When to use 'self' in Ruby
To be clear, I am not asking when to use self. I am asking why I cannot set the class variable inside a 'class << self' block, but I can outside of it.
your second example isn't a class variable, it's a class instance variable to this class the proof is that if you inherited like this you'll get nil
class A
class << self
attr_accessor :foo
end
self.foo = 'foo'
end
class B < A
end
B.foo # => nil
So if you want to add a class variable in class << self you can use ##
class A
class << self
##foo = :foo
def foo
##foo
end
end
end
When inheriting a class, I want to save class variables to the inheriting class instead of the parent class... like so...
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
##title = title
end
def self.title
##title
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title # returns `Baz` instead of `Bar`
Baz.title
This is a contrived example because after playing around with it, I more want to just understand than anything.
You need to change the class variable to instance variable.
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
#title = title
end
def self.title
#title
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title # => "Bar"
Baz.title # => "Baz"
##title class variable is a shared variable. Baz, Bar and Foo all are using the same class variable ##title. When you did class Baz < Foo ;end, then ##title has been updated to the name of Baz, thus all call to the getter method #title is giving currently updated value of the variable #title.
Follow the code below :-
class Foo
def self.inherited klass
klass.title = klass.to_s
end
def self.title= title
##title = title
end
def self.title
##title
end
end
class Bar < Foo
end
# see the output here
Bar.title # => "Bar"
class Baz < Foo
end
# here the output changed, as ##title value is holding the currently updated
#value
Bar.title # => "Baz"
Baz.title # => "Baz"
You can do that using Module#class_variable_get and Module#class_variable_set:
class Foo
def self.title= title
class_variable_set(:##title, title)
end
def self.title
class_variable_get(:##title)
end
end
class Bar < Foo
end
class Baz < Foo
end
Bar.title = 'Bar'
Baz.title = 'Baz'
Bar.title #=> "Bar"
Bar.title = "Cat"
Baz.title #=> "Baz"
Bar.class_variables #=> [:##title]
Baz.class_variables #=> [:##title]
Foo.class_variables #=> []
Foo.methods(false) #=> [:title=, :title]
Bar.methods(false) #=> []
Baz.methods(false) #=> []
If we were to execute Bar's a getter before its setter, we'd get:
Bar.title #=> NameError: uninitialized class variable ##title in Bar
If desired, the class variables can be initialized (to nil, say) by adding the following to the class definition:
def self.inherited klass
klass.class_variable_set(:##title, nil)
end
say I've got 2 Classes:
Class Foo
attr_accessor :bar
end
Class Baz < Foo
end
I'm creating an Instance of Foo and then want to have an Instance of Baz with the Data of the Foo Instance in it:
f = Foo.new(:bar => "Hi World")
# Doesnt work?
b = Baz.new(f)
How to do it?
an instance of Baz with the data of the Foo instance in it
Since your constructor already accepts attributes as a hash, you could create a method to return Foo's attributes as a hash:
class Foo
attr_accessor :bar
def initialize(attributes={})
#bar = attributes[:bar]
end
def attributes
{:bar => bar}
end
end
class Baz < Foo
end
Now you can create a Baz instance from these attributes:
f = Foo.new(:bar => "Hi World") #=> #<Foo:0x007fd09a8614c0 #bar="Hi World">
f.attributes #=> {:bar=>"Hi World"}
b = Baz.new(f.attributes) #=> #<Baz:0x007fd09a861268 #bar="Hi World">