Ruby: Passing a block to a class macro that defines instance methods
I'm struggling with code that looks like the example below (but actually does something useful). The block that is passed to def_my_method
is of course created in the context of the class, when I want to evaluate it in the context of the instance that has the instance method. How do I do this?
module Formatters
# Really useful methods, included in several classes
def format_with_stars(str)
return "*** #{str} ***"
end
end
class Test
include Formatters
STRINGS = ["aa", "bb"]
def self.def_my_method(method_name, another_parameter, &format_proc)
define_method(method_name) do
# In reality, some more complex code here, t开发者_如何转开发hen...
return STRINGS.map(&format_proc)
end
end
def_my_method(:star_strings, "another_parameter") { |str| format_with_stars(str) }
# Define other methods
end
tt = Test.new
puts tt.star_strings
# Throws undefined method `format_with_stars' for Test:Class (NoMethodError)
You can use instance_exec
to execute the passed block in the right context. Instead of passing &format_proc
directly to the call to map
, pass a block that calls it using instance exec.
Something like this:
def self.def_my_method(method_name, another_parameter, &format_proc)
define_method(method_name) do
# In reality, some more complex code here, then...
return STRINGS.map{|str| instance_exec(str, &format_proc)}
end
end
This produces this:
$ ruby tt.rb
*** aa ***
*** bb ***
for me (where tt.rb
is the arbitary name I gave the file), which I think is what you want for this example.
...
class Test
- include Formatters
+ extend Formatters
...
should do the trick.
精彩评论