11

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.

slhck
  • 36,575
  • 28
  • 148
  • 201
purbon
  • 193
  • 1
  • 1
  • 7
  • 1
    possible duplicate http://stackoverflow.com/questions/752717/how-do-i-use-define-method-to-create-class-methods – rwilliams Sep 12 '11 at 19:02

3 Answers3

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)
Zack Xu
  • 11,505
  • 9
  • 70
  • 78
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)

Confusion
  • 16,256
  • 8
  • 46
  • 71
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.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Could you please tell me why its an edge case? I am quite new and would love to learn, xD! – purbon Sep 12 '11 at 19:09
  • 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. – tadman Sep 12 '11 at 19:14
  • Ok! Thanks! I have to say, I am learning, but quite happy with the language, although I sounds like a hype for me, xD! – purbon Sep 12 '11 at 19:18
  • 2
    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` – Confusion Sep 12 '11 at 19:40
  • 2
    I thought people created dynamic methods fairly frequently in Ruby; that's where a lot of the "magic" comes from. Am I confusing terminology? – Dave Newton Sep 12 '11 at 21:36
  • @Confusion. Obviously all the magic here come throw inheritance, or could also be provided throw a mixin, lets say. – purbon Sep 13 '11 at 06:42
  • 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. – tadman Sep 13 '11 at 14:27
  • 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. – Guoliang Cao Feb 22 '13 at 22:25
  • 1
    The edit here with `define_singleton_method` puts the method in the correct context. – tadman Jul 23 '14 at 20:09