How to use define_method inside initialize()
Asked Answered
A

3

30

Trying to use define_method inside initialize but getting undefined_method define_method. What am I doing wrong?

class C
  def initialize(n)    
    define_method ("#{n}") { puts "some method #{n}" }    
  end
end

C.new("abc") #=> NoMethodError: undefined method `define_method' for #<C:0x2efae80>
Aromaticity answered 14/10, 2013 at 20:9 Comment(2)
What are you trying to do?Sardine
Nothing specific, trying to see how I can dynamically define a method using define_methodAromaticity
S
28

Do as below :

class C
  def initialize(n)    
    self.class.send(:define_method,n) { puts "some method #{n}" }    
  end
end

ob = C.new("abc")
ob.abc
# >> some method abc

Module#define_method is a private method and also a class method.Your one didn't work,as you tried to call it on the instance of C.You have to call it on C,using #send in your case.

Selfdelusion answered 14/10, 2013 at 20:12 Comment(4)
It works. Could you explain why my earlier version is incorrect?Aromaticity
@Bala: Note that o = C.new('pancakes') will leave you with ob.abc, ob.pancakes, o.abc, and o.pancakes as valid method calls. Is everyone sure that this is the intent?Mikkel
@muistooshort: Interesting comment. Although there was no intent, now I am thinking how to make it object specific (singleton).Aromaticity
Your one didn't work,as you tried to call it on the instance of C ---> it is not immediately clear to me why a private method cannot be called in an instance of c. Any ideas?Bathetic
M
47

I suspect that you're looking for define_singleton_method:

define_singleton_method(symbol, method) → new_method
define_singleton_method(symbol) { block } → proc

Defines a singleton method in the receiver. The method parameter can be a Proc, a Method or an UnboundMethod object. If a block is specified, it is used as the method body.

If you use define_method on self.class, you'll create the new method as an instance method on the whole class so it will be available as a method on all instances of the class.

You'd use define_singleton_method like this:

class C
  def initialize(s)    
    define_singleton_method(s) { puts "some method #{s}" }    
  end
end

And then:

a = C.new('a')
b = C.new('b')
a.a # puts 'some method a'
a.b # NoMethodError
b.a # NoMethodError
b.b # puts 'some method b'

If your initialize did:

self.class.send(:define_method,n) { puts "some method #{n}" }    

then you'd get:

a.a # puts 'some method a'
a.b # puts 'some method b'
b.a # puts 'some method a'
b.b # puts 'some method b'

and that's probably not what you're looking for. Creating a new instance and having the entire class change as a result is rather odd.

Mikkel answered 14/10, 2013 at 20:39 Comment(2)
I had a follow up question to this answer #56100781Mutualize
Great explanation!Warfourd
S
28

Do as below :

class C
  def initialize(n)    
    self.class.send(:define_method,n) { puts "some method #{n}" }    
  end
end

ob = C.new("abc")
ob.abc
# >> some method abc

Module#define_method is a private method and also a class method.Your one didn't work,as you tried to call it on the instance of C.You have to call it on C,using #send in your case.

Selfdelusion answered 14/10, 2013 at 20:12 Comment(4)
It works. Could you explain why my earlier version is incorrect?Aromaticity
@Bala: Note that o = C.new('pancakes') will leave you with ob.abc, ob.pancakes, o.abc, and o.pancakes as valid method calls. Is everyone sure that this is the intent?Mikkel
@muistooshort: Interesting comment. Although there was no intent, now I am thinking how to make it object specific (singleton).Aromaticity
Your one didn't work,as you tried to call it on the instance of C ---> it is not immediately clear to me why a private method cannot be called in an instance of c. Any ideas?Bathetic
S
0

You were almost there. Just point to the class with self.class, don't even need to use :send:

class C
  def initialize(n)    
    self.class.define_method ("#{n}") { puts "some method #{n}" }    
  end
end
ob = C.new('new_method')
ob2 = C.new('new_method2')
# Here ob and ob2 will have access to new_method and new_method2 methods

You can also use it with :method_missing to teach your class new methods like this:

class Apprentice
  def method_missing(new_method)
    puts "I don't know this method... let me learn it :)"
    self.class.define_method(new_method) do
      return "This is a method I already learned from you: #{new_method}"
    end
  end
end
ap = Apprentice.new
ap.read
=> "I don't know this method... let me learn it :)"
ap.read
=> "This is a method I already learned from you: read"
Sieve answered 26/8, 2020 at 18:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.