tscheingeld
tscheingeld

Reputation: 849

Delegate methods to the result of a method

First of all, this is really just a golf question. My code works fine as it is. But I feel like there is probably a better (i.e. cooler) way to do this.

So I've got a class that acts a lot like a hash. However, it really internally generates a hash for each call to its hash-ish methods. The private method for generating that hash is calculated(). So my code currently has a lot of method definitions like this:

def each(&block)
    return calculated.each(&block)
end

def length()
    return calculated.length
end

Is there a concise way to delegate all those method calls to the calculated method?

Upvotes: 1

Views: 258

Answers (2)

max pleaner
max pleaner

Reputation: 26768

edit: don't do this; I forgot Forwardable existed

You can write a "macro" for this. Well, Ruby doesn't technically have actual "macros" but it's a fairly common pattern nonetheless. Rails in particular uses it extensively - stuff like belongs_to, validates, etc are all class methods which are being used to generate instance-level functionality.

module DelegateToFunc
  def delegate_to_func(delegate, delegators)
    delegators.each do |func_name|
      # Note: in Ruby 2.7 you can use |...| instead of |*args, &blk|
      define_method(func_name) do |*args, &blk|
        send(delegate).send(func_name, *args, &blk)
      end
    end
  end
end

class SequenceBuilder
  extend DelegateToFunc

  delegate_to_func(:calculated, [:length, :each])

  attr_accessor :min, :max

  def initialize(min:, max:)
    @min, @max = min, max
  end

  def calculated
    min.upto(max).to_a
  end
end

SequenceBuilder.new(min: 5, max: 10).length # => 6
SequenceBuilder.new(min: 1, max: 4).each { |num| print num } # => 1234

I will say, though, that methods generated by metaprogramming can sometimes be hard to track down and can make a program confusing, so try and use them tastefully ...

For example, do you really need your object to expose these hash-like methods? Why not just let the caller read the hash via calculated, and then call the hash methods directly on that?

Upvotes: 0

tscheingeld
tscheingeld

Reputation: 849

I figured it out and it's incredibly simple. Just delegate to the name of the method. Here's a working example:

class MyClass
    extend Forwardable
    delegate %w([] []=) => :build_hash
    
    def build_hash
        return {'a'=>1}
    end
end

Upvotes: 1

Related Questions