raorao
raorao

Reputation: 156

How do you delegate class methods to another object in Ruby using the Forwardable Module?

I'd like to delegate a class method on Bar to an instance of the Foo class, but I can't get the delegation to work. None of the delegated methods throw an error, the getters simply return nil. Here's what my code looks like:

class Foo
  attr_reader :worksheet_id, :access_token

  def configure worksheet_id: raise, access_token: raise
    @worksheet_id = worksheet_id
    @access_token = access_token
  end
end

class Bar
  class << self
    extend Forwardable
    def_delegators Foo.new, :configure, :worksheet_id, :access_token
  end

  configure worksheet_id: 1, access_token: 2
end

And here's what I'd like to be able to do.

Bar.worksheet_id # => returns nil, should be 1.
Bar.access_token # => returns nil, should be 2.

What am I doing wrong?

Upvotes: 1

Views: 2485

Answers (1)

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369428

The first argument to the def_delegator family of methods is a Symbol or String (or anything else that implements to_s) that denotes the "accessor" by which to access the delegation target. It is not the delegation target itself.

So, something like this will work:

class Bar
  class << self
    extend Forwardable
    def_delegators :@foo, :configure, :worksheet_id, :access_token
  end

  @foo = Foo.new

  configure worksheet_id: 1, access_token: 2
end

Bar.worksheet_id # => 1
Bar.access_token # => 2

What actually happens, is that the forwardable library uses string interpolation to build definitions for forwarder methods, which are then evald.

In essence, it looks like this:

"#{target}.__send__(#{method})"

So, you can pass anything as target, whose to_s results in something that you can eval and then call __send__ on. However, in your case, Foo.new.to_s is something like #<Foo:0x007fc4aa521ab8>. When interpolated into the string and evald, the line starts with a # character, or simply put: the forwarder method consists of a single line which is commented out! And that's why it returns nil, because that's what an empty method returns.

Upvotes: 5

Related Questions