2

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, then...
      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)

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

7 Comments

You don't need the wrapped proc, you can just do STRINGS.map {|str| instance_exec(str, &format_proc)}.
@sepp2k - oh yes, that's a clearer way - thanks. I've incorporated your suggestion into the answer. (Anyone interested - have a peek at the edit history to see my original idea of using a wrapper lambda to pass to map.)
instance_exec sounds perfect, but it's not core Ruby is it? My Googling suggests that it's an extension to Object introduced in Rails 2. My simplified example was pure Ruby, though the real thing is Rails - but Rails 1.2! So I can't see any way to use this. :-(
I think I have answered my own question with more Googling. eigenclass.org/hiki.rb?bounded+space+instance_exec appears to be the same implementation as used in Rails 2. So I'll do my own Object patching and my problem is solved. Thanks!
I think instance_exec may have originated in rails, but is part of ruby 1.9 and is in 1.8.7, so if you're using an up to date ruby you should be okay. My example above was done with 1.8.7
|
1

...

class Test
-  include Formatters
+  extend Formatters

...

should do the trick.

1 Comment

It works for my trivial example, but only by changing my instance methods to class methods, which is definitely not something I can do in the real code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.