Reputation: 849
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
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
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