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...'
1

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for remind me of this, 'Module#define_method : Defines an instance method in the receiver'. I've got it, thanks again.
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

4 Comments

thanks for your answer, but i still don't get that why def foo acts as def self.foo
@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
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.
if you need aa.bar3, use class_eval instead: aa.class_eval { define_method :bar3, cause class_eval creates instance methods.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.