3

Say I have the following classes:

class Foo
  attr_accessor :name, :age
end

class Bar
    def initialize(name)
        @foo = Foo.new
        @foo.name = name
    end
end

I'd like to define an accessor on Bar that is simply an alias to foo.name. Easy enough:

def name
  @foo.name
end

def name=(value)
  @foo.name = value
end

With only one property, this is easy enough. However, say Foo exposes several properties that I want to expose through Bar. Rather than defining each method manually, I want to do something like this, though I know the syntax isn't right:

[:name, :age].each do |method|
  def method
    @foo.method
  end

  def method=(value)
    @foo.method = value
  end
end

So...what is the correct way of defining methods like this?

Paul Alexander
  • 31,970
  • 14
  • 96
  • 151
  • 1
    Btw, your example code won't actually work because `foo` is a local variable and goes out of scope at the end of the `initialize` method. – sepp2k Jan 26 '11 at 22:01

3 Answers3

2

To define a method dynamically you can use define_method which takes the method name as a symbol or string argument.

To call a method dynamically you can use send which also takes the method name as a symbol or string.

[:name, :age].each do |method|
  define_method(method) do
    @foo.send(method)
  end

  define_method("#{method}=") do |value|
    @foo.send("#{method}=", value)
  end
end
sepp2k
  • 363,768
  • 54
  • 674
  • 675
1
[:name, :age].each do |method|
  define_method(method) do
    foo.send(method)
  end

  define_method("#{method}=") do |value|
    foo.send("#{method}=", value)
  end
end
Bob Aman
  • 32,839
  • 9
  • 71
  • 95
  • Sweet, just what I was looking for. Out of curiousity, is there a performance issue with using foo.send? I was hoping to define the body of the method as a string replacing "method" with the action name. – Paul Alexander Jan 26 '11 at 22:05
  • Yes, `Object#send` is slower. Whether this matters or not depends entirely on how often you dispatch methods in this way. To define a method body as a `String` you'll need to use one of the `eval` methods, which is slower still. It also may come with a number of security implications. – Bob Aman Jan 26 '11 at 22:54
1

See the Delegator class in the Ruby Standard Library, depending on how many methods you want to pass along.

Phrogz
  • 296,393
  • 112
  • 651
  • 745