Reputation: 410
I am trying to dynamically define methods and pass a parameter to it. This is what I have so far:
class Commands
end
@commands = Commands.new
def route(name, &block)
Commands.send(:define_method, name, &block)
end
route 'foo' do
puts 'oh hai'
puts data
end
When route
is called, this is what I expect to be executed:
data = {name: 'foo', optional: 'bar'}
@commands.send(data['name'].to_sym, data)
This fails because data
isn't defined unless I do something like:
route 'foo' do |data|
puts 'oh hai'
puts data
end
Is there a way to pass a default parameter? Seems like it's sinatrarb does it, but I haven't been able to make head or tails from the code yet.
Upvotes: 2
Views: 91
Reputation: 106127
Your question isn't very clear, but I suspect that the functionality you're looking for is the sort usually achieved with instance_eval
and friends. instance_eval
evaluates the given block in the context of its callee, so that the callee's instance methods and instance variables are available inside the block.
Here's a really elementary example:
"foo".instance_eval do
puts size
puts upcase
end
# -> 3
# -> FOO
size
and upcase
aren't parameters we've given to the block, they're methods that are already available in the context of the String object.
With that in mind, you might start with something like this:
class Route
attr_reader :data
def initialize(name)
@data = { name: name, optional: "bar" }
end
end
rt = Route.new("foo")
rt.instance_eval do
puts 'oh hai'
puts data
end
# -> oh hai
# -> {:name=>"foo", :optional=>"bar"}
Here we've defined a Route class whose instances can serve as a context in which to evaluate blocks given to the route
method:
def route(name, &block)
rt = Route.new(name)
Commands.send(:define_method, name) do
rt.instance_eval(&block)
end
end
route 'foo' do
puts 'oh hai'
puts data
end
route 'qux' do
puts "My name is %{name} and optional is %{optional}" % data
end
@commands = Commands.new
@commands.foo
# -> oh hai
# -> {:name=>"foo", :optional=>"bar"}
@commands.qux
# -> My name is qux and optional is bar
Upvotes: 3
Reputation: 121010
The block is what you are passing to route
method; you are free to pass whatever you want, hence ruby won’t complain against any number of parameters (till the @commands.foo
is actually called.)
One might assign a default value to block parameter:
λ = lambda do |data = 'hello'|
puts data
end
and pass this lambda to route
:
route 'foo', &λ
#⇒ "hello"
AFAIK, there is no way to explicitly assign local variable in foreign binding
using some kind of hack like:
block.binding.local_variable_set(:data, 'hello')
Upvotes: 1