I wrote some code to learn about the class_eval method. My understanding is that Foo.class_eval will set self to "Foo" and evaluate the block below in that context, just as saying "class Foo" will do. However, the code below shows that setting a class variable using class_eval will assign the class variable at the toplevel: to "Object". Why does this happen?
class Foo
end
class Foo
puts self
# => Foo
##class_var = "hello"
end
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
# => warning: class variable access from toplevel
end
puts Object.class_variables
# => ##class_var
As pointed out in comments a single # would do what you want, now if you were running the code from inside another class ## would work but it would set a class variable for both Foo, and whatever class you running from:
class Foo
puts self
# => Foo
##class_var = "hello"
end
class Bar
def self.main
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
end
end
end
Bar.main
p [Bar.class_variables]
#[[:##class_var]]
p [Foo.class_variables]
#[[:##class_var]]
That has to do with proc or block scope, I was expecting it to set only ##class_var only on Bar class but it actually sets on both, weird.
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
Suppose I have a setup like this:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new
end
end
class Bar
def bar_method
self.class # => Bar
whatever???.class # => Foo
end
end
foo = Foo.new
foo.bar.bar_method
I know that I can set up the method like this:
def bar_method(selfs_self)
selfs_self.class # => Foo
end
And call the method like this: foo.bar.bar_method(foo) to get what I want. But that seems pretty redundant. Is there any way, inside of bar_method, that I can get a reference to foo, without specifically passing in a reference to it?
No.
Usually this is done by passing a reference to the parent object when initializing child objects, like:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new(self)
end
end
class Bar
attr_reader :foo
def initialize(foo)
#foo = foo
end
def bar_method
self.class # => Bar
foo.class # => Foo
end
end
I've seen Ruby code with classes called like methods:
FactoryGirl(:factory_name) # => returns a factory instance
How do you write that 'method'?
All you need to do is to create a function with the same name as the class and that forwards its parameters to the class' new method. For instance:
class Foo
def initialize(x)
#arg=x
end
def to_s
#arg.to_s
end
end
def Foo(x)
Foo.new(x)
end
a = Foo.new(7)
a.class
=> Foo
puts a
=> 7
b = Foo(7)
b.class
=> Foo
puts b
=> 7
For completeness, it's defined in lib/factory_girl/syntax/vintage.rb at the bottom:
module FactoryGirl
module Syntax
module Vintage
# [other stuff elided]
# Shortcut for Factory.create.
#
# Example:
# Factory(:user, name: 'Joe')
def Factory(name, attrs = {})
ActiveSupport::Deprecation.warn 'Factory(:name) is deprecated; use FactoryGirl.create(:name) instead.', caller
FactoryGirl.create(name, attrs)
end
end
end
end
You can add this factory-function to the Object-class:
class Object
def FactoryGirl(symbol)
...
end
end
class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
some = 'some_data'
end
def get_some
puts self.inspect
some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test nil
Here above I could find self as Test itself but not returning the some_data as output.
But while I modified in following way it returns expected output
class Test
class << self
attr_accessor :some
def set_some
puts self.inspect
self.some = 'some_data'
end
def get_some
puts self.inspect
self.some
end
end
end
Test.set_some => Test
puts Test.get_some.inspect => Test some_data
What is the differences?
EDIT
Now in the first example if I set as get some method as
Test.some = 'new_data'
puts Test.some.inspect #=> new_data
Test.set_some
puts Test.get_some.inspect => new_data
Now it made me much more confused.
some = :foo makes ruby think it should create a new local variable with name some. If you want to call some=(), you have to use an explicit reciever - as in self.some = :foo. I once lost a bet on that... :-/
It's (local) variable in the first example
In the first example some is a local variable.
In the second one, some is a method of self. Why? Because attr_accessor :some is the same as:
def some= (val)
#some = val
end
def some
return #some
end
So, you have created the getter and setter methods for the instance variable #some (it's an instance variable of the object Test, as every class is also an object of class Class).
in the first method
def set_some
puts self.inspect
some = 'some_data'
end
some is a local variable.. its not the same as #some that is a instance variable (in this case a class instance variable) so the value disappears when the method ends.
if you want to call the setter method some or set #some to something then do this
#some = 'some_data'
or
self.some = 'some_data'
in the second method
def get_some
puts self.inspect
self.some
end
your calling the method some. which returns the instace variable #some.. and since at this point #some has no value.. returns nil..
Example 1 with no method override and no local variable
class Foo
def initialize
#foo = 'foo'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo, self.foo, and foo will access instance variable #foo within the instance method:
Foo.new.print_foo #=> foofoofoo
Example 2 with method override
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
print #foo
print self.foo
print foo
end
end
#foo will access the instance variable, but self.foo and foo will call the foo override method:
Foo.new.print_foo #=> foobarbar
Example 3 with method override and local variable
class Foo
def initialize
#foo = 'foo'
end
def foo
return 'bar'
end
def print_foo
foo = 'baz'
print #foo
print self.foo
print foo
end
end
#foo accesses instance variable, self.foo accesses override method, and foo accesses local variable:
Foo.new.print_foo #=> foobarbaz
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