Add ruby class methods or instance methods dynamically

17,471

Solution 1

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.

Solution 2

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)

Solution 3

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)

Share:
17,471
purbon
Author by

purbon

Updated on June 28, 2022

Comments

  • purbon
    purbon almost 2 years

    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.

  • purbon
    purbon over 12 years
    Could you please tell me why its an edge case? I am quite new and would love to learn, xD!
  • tadman
    tadman over 12 years
    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.
  • purbon
    purbon over 12 years
    Ok! Thanks! I have to say, I am learning, but quite happy with the language, although I sounds like a hype for me, xD!
  • user1678401
    user1678401 over 12 years
    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`
  • Lars Haugseth
    Lars Haugseth over 12 years
    You'll want to do B.run :foo before you can call B.foo.
  • Lars Haugseth
    Lars Haugseth over 12 years
    You can also avoid send by using eigenclass.class_eval { define_method(method) { ... }}
  • Dave Newton
    Dave Newton over 12 years
    I thought people created dynamic methods fairly frequently in Ruby; that's where a lot of the "magic" comes from. Am I confusing terminology?
  • Marc-André Lafortune
    Marc-André Lafortune over 12 years
    Edited to use the official terminology of singleton_class, introduced in Ruby 1.9.2
  • purbon
    purbon over 12 years
    @Confusion. Obviously all the magic here come throw inheritance, or could also be provided throw a mixin, lets say.
  • user1678401
    user1678401 over 12 years
    @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' :).
  • tadman
    tadman over 12 years
    Dynamic methods are created more often than in other languages, it's true, but that's still not something you'd ordinarily do. The syntax for creating class methods should be cleaned up so that simple questions like this aren't tricky to answer.
  • Guoliang Cao
    Guoliang Cao about 11 years
    As pointed out by @Confusion, this will create methods on Class and is not what OP wants. This should not be accepted as the answer.
  • tadman
    tadman almost 10 years
    The edit here with define_singleton_method puts the method in the correct context.