ndn ndn - 6 months ago 11
Ruby Question

Ignore lambda argument checking

I want to convert a proc that exhibits lambda behavior (argument checking) into one that doesn't. The following is a very contrived example, but it should get the point across:

The aim is to create a DSL that looks something like this:

NumberSeries.perform do
add first_series: -> { natural_numbers.take(10) },
second_series: -> { fibonacci_numbers.take(10) }
end


Note that
natural_numbers
and
fibonacci_numbers
are not passed as arguments in the DSL. The implementation of
add
looks something like this:

NaturalNumbersFibonacciNumbers = Struct.new(:natural_numbers, :fibonacci_numbers)
FAMOUS_NUMBER_SERIES = NaturalNumbersFibonacciNumbers.
new(natural_numbers, fibonacci_numbers)

def add(first_series:, second_series:)
first_numbers = FAMOUS_NUMBER_SERIES.instance_eval(&first_series)
second_numbers = FAMOUS_NUMBER_SERIES.instance_eval(&second_series)
first_numbers.zip(second_numbers).map { |x, y| x + y }
end


Now, if I replace
->
with
proc
in the DSL, it will work. However, keeping the lambdas, I would get


ArgumentError: wrong number of arguments (1 for 0)


as
BasicObject#instance_eval
yields self to the lambda, but the lambda expects no arguments.




I don't want to use
Fiddle
for obvious reasons.

Answer

instance_exec is an alternative to instance_eval that allows you to control the parameters passed to the block, and doesn’t pass the receiver as instance_eval does. You can use it as a workaround to your problem as it allows you to pass zero args:

irb:108:0>  Object.new.instance_eval &-> { puts "Hello" }
ArgumentError: wrong number of arguments (given 1, expected 0)
    from (irb):108:in `block in irb_binding'
    from (irb):108:in `instance_eval'
    from (irb):108
    from /Users/matt/.rubies/ruby-2.3.0/bin/irb:11:in `<main>'
irb:109:0>  Object.new.instance_exec &-> { puts "Hello" }
Hello
=> nil