12

I am quite new to Ruby, so still learning. I was researching quite a bit about how to add methods dynamically, and I was successful to create instance methods, but not successful when creating class methods.

This is how I generated instance methods:

  class B
    def before_method
      puts "before method"
    end

    def self.run(method)
        send :define_method, method do
          before_method
          puts "method #{method}"
        end
    end
  end

  class A < B
    run :m
    run :n
  end

Any idea about the best ways to create static methods?

My final task is to look for the best way to create "before" and "after" tasks for class methods.

1

3 Answers 3

27

To create instance methods dynamically, try

class Foo
  LIST = %w(a b c)

  LIST.each do |x|
    define_method(x) do |arg|
      return arg+5
    end
  end
end

Now any instance of Foo would have the method "a", "b", "c". Try

Foo.new.a(10)

To define class methods dynamically, try

class Foo
  LIST = %w(a b c)

  class << self
    LIST.each do |x|
      define_method(x) do |arg|
        return arg+5
      end
    end
  end
end

Then try

Foo.a(10)
Sign up to request clarification or add additional context in comments.

Comments

7

Instance methods of an objects singleton class are singleton methods of the object itself. So if you do

class B
  def self.run(method)
    singleton_class = class << self; self; end
    singleton_class.send(:define_method, method) do
        puts "Method #{method}"
    end
  end
end

you can now call

B.run :foo
B.foo 
=> Method foo

(Edit: added B.run :foo as per Lars Haugseth's comment)

4 Comments

You'll want to do B.run :foo before you can call B.foo.
You can also avoid send by using eigenclass.class_eval { define_method(method) { ... }}
Edited to use the official terminology of singleton_class, introduced in Ruby 1.9.2
@Marc-André: ah, I wasn't aware there finally was a standard; we're still at 1.8.7. Now I'll have to unlearn our company standard 'eigenclass' :).
2

Here's something re-worked to use class methods:

class B
   def self.before_method
     puts "before method"
   end

  def self.run(method)
    define_singleton_method(method) do
      before_method
      puts "method #{method}"
    end
  end
end

Update: Using define_singleton_method from Ruby 1.9 which properly assigns to the eigenclass.

9 Comments

Could you please tell me why its an edge case? I am quite new and would love to learn, xD!
People rarely create dynamic instance methods, and dynamic class methods are even rarer still. Sadly, the semantics are kind of ugly because of how infrequently it comes up. There's nothing wrong with doing it, but most people never will, that's all.
This probably isn't what you want: you've now added the method to Class, which means it is available in every class (B.class == Class). This is a problem especially because not every class has a 'before_method'. Try calling B.run 'foo', defining a class A and calling A.foo. It will fail with NameError: undefined local variable or method before_method' for A:Class`
I thought people created dynamic methods fairly frequently in Ruby; that's where a lot of the "magic" comes from. Am I confusing terminology?
The edit here with define_singleton_method puts the method in the correct context.
|

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.