What's wrong with my metaprogramming?

def define_my_method(method_name)
  define_method method_name do
    yield
  end
end

class X
  define_my_method :method1 do
    puts "Method 1"
  end
  define_my_method :method2 do
    puts "Method 2"
    method1
  end
end

X.new.method2

Gives:

C:\wrk\johnk\wiksprint-001>define.rb
Method 2
C:/wrk/johnk/wiksprint-001/define.rb:13: undefined local variable or method `met
hod1' for X:Class (NameError)
        from C:/wrk/johnk/wiksprint-001/define.rb:3:in `method2'
        from C:/wrk/johnk/wiksprint-001/define.rb:17

Hi --

def define_my_method(method_name)
define_method method_name do
  yield
end

class X
define_my_method :method1 do
  puts "Method 1"
end
define_my_method :method2 do
  puts "Method 2"
  method1
end

X.new.method2
<<<<<

Gives:

C:\wrk\johnk\wiksprint-001>define.rb
Method 2
C:/wrk/johnk/wiksprint-001/define.rb:13: undefined local variable or method `met
hod1' for X:Class (NameError)
      from C:/wrk/johnk/wiksprint-001/define.rb:3:in `method2'
      from C:/wrk/johnk/wiksprint-001/define.rb:17

The problem is that define_method creates instance methods -- in this
case, method1 and method2. In method2 you call method1... but you're
calling it on the class object X, when you've defined it for
*instances* of X.

If you replace method1 with new.method1, in the second call to
define_my_method, you'll see the difference.

David

···

On Sun, 15 Oct 2006, John Ky wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

John Ky wrote:

try this one:

def define_my_method(method_name,&blk)
  define_method method_name, &blk
end

class X
  define_my_method :method1 do
    puts "Method 1"
  end
  define_my_method :method2 do
    puts "Method 2"
    method1
  end
end

X.new.method2

lopex

you don't want yield here as the context of that yield is bound to the
__class__ (think of what self is at the time of call) and you want it bould to
the instance.

   harp:~ > cat a.rb
   def define_my_method *a, &b
     define_method *a, &b
   end

   class X
     define_my_method :method1 do
       puts "Method 1"
     end
     define_my_method :method2 do
       puts "Method 2"
       method1
     end
   end

   X.new.method2

   harp:~ > ruby a.rb
   Method 2
   Method 1

your metaprogramming is fine - this is an issue of blocks and scoping.

regards.

-a

···

On Sun, 15 Oct 2006, John Ky wrote:

def define_my_method(method_name)
define_method method_name do
  yield
end

class X
define_my_method :method1 do
  puts "Method 1"
end
define_my_method :method2 do
  puts "Method 2"
  method1
end

X.new.method2
<<<<<

Gives:

C:\wrk\johnk\wiksprint-001>define.rb
Method 2
C:/wrk/johnk/wiksprint-001/define.rb:13: undefined local variable or method `met
hod1' for X:Class (NameError)
      from C:/wrk/johnk/wiksprint-001/define.rb:3:in `method2'
      from C:/wrk/johnk/wiksprint-001/define.rb:17

--
my religion is very simple. my religion is kindness. -- the dalai lama

John Ky wrote:

>>>>>
def define_my_method(method_name)
  define_method method_name do
    yield
  end
end

Yea, funny thing about Ruby, closures go a long ways. Have a look at
what self is in one fo your definitions:

class X
  define_my_method :method1 do
    puts "Method 1"
  end
  define_my_method :method2 do
    puts "Method 2"

        p self

    method1
  end
end

X.new.method2

This is why define_method takes a block. So a fix would be:

  def define_my_method(method_name, &block)
    define_method method_name, &block
  end

Unfortuantely that's not always the prefect solution since you might
want to wrap extra code around the yield . In that case, you can use
#instance_eval if you don't need to pass parameters. If you do need to
pass parameters you'll have to use more complicated tricks until 1.9
introduces #instance_exec.

On a related note, I still think it would be helpful if we could create
clean-closure blocks.

T.