4

I'm a start learner about Ruby MetaProgramming. when practicing my code in irb, I met this problem.

class A; end
a = A.new
b = class << a; self; end

b.instance_eval { def foo; puts 'foo'; end }
# => works for b.foo

b.instance_eval { define_method :bar do; puts 'bar'; end }
# => WHY this one works for a.bar rather than b.bar

The last code fragment confused me.


Thanks for your specific answers, but maybe I didn't explain my confusion clearly. What I'm really trying to understand is why define_method behaves so differently in these cases, here:

class A
  def foo1
    p 'foo1 from A'
  end 

  define_method :bar1 do
    p 'bar1 from A'
  end 
end

a = A.new
a.foo1 # => 'foo1 from A'
a.bar1 # => 'bar1 from A'


a.instance_eval { def foo2; p 'foo2 from a.metaclass'; end }
a.foo2 # => 'foo2 from a.metaclass'
a.instance_eval { define_method :bar2 do; p 'bar2 from a.metaclass'; end }
# => NoMethodError: undefined method `define_method' for #<A:0x000000016a2e70>

aa = class << a; self; end
aa.instance_eval { def foo3; p 'foo3 from a.metaclass.metaclass'; end }
aa.foo3 # => 'foo3 from a.metaclass.metaclass'
aa.instance_eval { define_method :bar3 do; p 'bar3 from a.metaclass.metaclss'; end }
aa.bar3 # => NoMethodError: undefined method `bar3' for #<Class:#<A:0x000000016a2e70>>
a.bar3 # => 'bar3 from a.metaclass.metaclss'

I know that this doesn't come up in day-to-day coding, but I want to make my mind clear.


make a conclusion:

aa = class << a; self; end 

aa.instance_eval { def foo; puts 'foo..'; end }

# defines a singleton-method for aa
aa.foo # => 'foo...'


aa.instance_eval { define_method :bar do; puts 'bar..'; end }
# equals
aa.class_eval { def bar; puts 'bar..'; end }

# both define a singleton-method for a,
# as define_method and class_eval both define instance_method
a.bar # => 'bar...'
ifyouseewendy
  • 6,674
  • 1
  • 21
  • 26
  • About class_eval and instance_eval, please have a look at http://stackoverflow.com/questions/13866047/dynamically-creating-class-method/13870694#13870694 – user1852994 Dec 14 '12 at 16:28

3 Answers3

2

In addition to all other comments :

[from the Pickaxe] The method Object#instance_eval lets you set self to be some arbitrary object, evaluates the code in a block with [self], and then resets self.
And Module#define_method : Defines an instance method in the receiver [self, which must be a (anonymous) Class or Module].

singleton_class_of_object_a = aa = class << a; self; end
aa.instance_eval { def foo3; puts "foo3 from singleton class of a, self=#{self}"; end }
aa.foo3 # => foo3 from singleton class of a, self=#<Class:#<A:0x007fc2e4049e68>>
aa.instance_eval do
    puts "about to define_method :bar3 in self=#{self}"
    define_method :bar3 do; puts "bar3 from singleton class of a, self=#{self}"; end
end # => about to define_method :bar3 in self=#<Class:#<A:0x007fc2e4049e68>>
a.bar3 # => bar3 from singleton class of a, self=#<A:0x007fc2e4049e68>

define_method :bar3 is executed in the context of singleton_class_of_object_a (an anonymous class, see below), thus defines an instance method of that class, hence bar3 becomes a singleton method of a. As already said in my previous answer, it is equivalent to defining directly on the object :

def a.bar4; puts 'bar4 from singleton class of a' end
a.bar4 # => bar4 from singleton class of a

p a.singleton_methods.sort # => [:bar3, :bar4, :foo2]
p a.methods(false).sort # => [:bar3, :bar4, :foo2]  


After a = A.new, the field class of instance a points to class A.
With class << a or def a.bar4, Ruby creates an anonymous class, the field class of instance a now points to this anonymous class, and from there to A.
Methods defined in this context with def or define_method go into the methods table of the anonymous class.

user1852994
  • 223
  • 1
  • 6
  • Thanks for remind me of this, 'Module#define_method : Defines an instance method in the receiver'. I've got it, thanks again. – ifyouseewendy Dec 10 '12 at 06:03
1

because in your case def foo acts as def self.foo
and define_method :bar acts as def bar.

this happens cause instance_eval creates class methods.

your code is identical to:

class << a
  def self.foo
    puts 'foo'
  end

  def bar
    puts 'bar'
  end
end

so the foo method is defined inside a's eigen class
and the bar method is defined inside a itself.

if you need a.foo to work use class_eval instead.

it will work cause class_eval creates instance methods:

b.class_eval { def foo; puts 'foo'; end }
a.foo
# => foo
  • thanks for your answer, but i still don't get that why `def foo` acts as `def self.foo` – ifyouseewendy Dec 08 '12 at 12:00
  • @ifyouseewendy, the main point here is that `instance_eval` actually creates class methods. `class_eval` instead creates instance methods. that's why your `def foo` is equivalent to `def self.foo`. try `class_eval { def foo ` and you'll get `a.foo` working –  Dec 08 '12 at 12:32
  • `a.instance_eval { define_method` will fail cause `a` is a instance and does not respond to `define_method`. `aa.instance_eval { define_method :bar3` is defining `:bar3` inside `a`, cause **`instance_eval` creates class methods**. –  Dec 09 '12 at 08:20
  • if you need `aa.bar3`, use `class_eval` instead: `aa.class_eval { define_method :bar3`, cause **`class_eval` creates instance methods**. –  Dec 09 '12 at 08:22
1
b.instance_eval { def foo; puts 'foo'; end }
b.instance_eval { puts "self in b.instance_eval block=#{self}" }
#=> self in b.instance_eval block=#<Class:#<A:0x007fe3c204d000>>
b.foo #=> foo

You are defining a method in the singleton class of a single instance of A. It looks pretty complex. Rather define a singleton method directly on the instance :

cat = String.new("cat")
def cat.speak
    'miaow'
end
cat.speak #=> "miaow" 
cat.singleton_methods #=> ["speak"] 

The class << notation such as

singleton_class_of_A = eigenclass_of_A = class << A; self; end

is usually used to defined "class methods" (actually singleton methods of A), or instances variables of the class :

class B
    class << self
        def my_class_method_of_B
            puts "my_class_method_of_B"
        end
        @my_first_variable_of_class_B = 123
        attr_accessor :my_second_variable_of_class_B
    end
end

B.my_class_method_of_B
print 'singleton methods of B : '
p B.singleton_methods
print 'instance variables of B : '
p B.instance_variables
print 'class variables of B : '
p B.class_variables
print '"singleton variables" of B : '
class << B; p instance_variables end

Output :

my_class_method_of_B
singleton methods of B : [:my_class_method_of_B, :my_second_variable_of_class_B, :my_second_variable_of_class_B=]
instance variables of B : []
class variables of B : []
"singleton variables" of B : [:@my_first_variable_of_class_B]

As you can see, this matter is not easy, you may wish to have a look at http://pragprog.com/book/ppmetr/metaprogramming-ruby

user1852994
  • 223
  • 1
  • 6