Metaprogammatically define methods

I’m writing a harness for some tests that use watir. Rather than having
to boilerplate code in the definition of each test like
def test_1
load test1
end

I want to read the testnames in from a file which will be easier for the
user. What I’d like to do is something along this structure (which I
adapted from a really good blog in metaprogramming
http://invisibleblocks.wordpress.com/2006/06/14/ruby-meta-programming-and-watir/):

testlist = Array.new

File.open(‘readme.txt’, ‘r’) do |eachline|
while (line = eachline.gets)
testlist << line
end
end
#puts testlist

testlist.each { |entry|
code = <<METHOD_TEMPLATE
def test_#{entry}()
puts “testing #{entry}”
end
METHOD_TEMPLATE
}

and then call each method I’ve defined. However, I’m not sure I’m going
about this the correct way.

Hi –

On Sat, 18 Nov 2006, Max R. wrote:

code = <<METHOD_TEMPLATE
def test_#{entry}()
puts “testing #{entry}”
end
METHOD_TEMPLATE
}

and then call each method I’ve defined. However, I’m not sure I’m going
about this the correct way.

We (Austin Codefest of 2002) did something very like this in the tests
for Ruby scanf. If you look for test_scanf.rb in the Ruby source,
you’ll see it, near the bottom of the file. We used define_method.
(I think that was back in the days when I habitually switched the
order of actual and expected in my assertions, so you have to
compensate for that :slight_smile:

It’s not exactly the same as yours but I’d say that define_method is
your best bet too.

David

Max R. wrote:

testlist = Array.new

File.open(‘readme.txt’, ‘r’) do |eachline|
while (line = eachline.gets)
testlist << line
end
end
#puts testlist

testlist = File.open(‘readme.txt’).readlines

testlist.each { |entry|
code = <<METHOD_TEMPLATE
def test_#{entry}()
puts “testing #{entry}”
end
METHOD_TEMPLATE
}
and then call each method I’ve defined. However, I’m not sure I’m going
about this the correct way.

Use define_method whenever possible. The only case where you would put
code in a string (and then load it using eval) is if your methods need
to accept a block argument (define_method does not accept block argument
until Ruby 1.9).

class Foo
File.open(‘readme.txt’).readlines.each do |entry|
define_method “test_#{entry}”.to_sym do
puts “testing #{entry}”
end
end
end

Foo.new.test_whatever

Suraj K. wrote:

class Foo
File.open(‘readme.txt’).readlines.each do |entry|

Well it turns out that the above line can be simplified to:

File.open(‘readme.txt’).each_line do |entry|

or even to:

IO.foreach(‘readme.txt’) do |entry|

Personally, I prefer each_line because IO.foreach sounds like “for each
IO object in …” instead of “for each line in …”.

define_method "test_#{entry}".to_sym do
  puts "testing #{entry}"
end

end
end

Foo.new.test_whatever

Hi –

On Sat, 18 Nov 2006, Suraj K. wrote:

class Foo
File.open(‘readme.txt’).readlines.each do |entry|
define_method “test_#{entry}”.to_sym do

You don’t need to_sym there; define_method will take a string.

David

Suraj K. wrote:

Suraj K. wrote:

class Foo
File.open(‘readme.txt’).readlines.each do |entry|

Well it turns out that the above line can be simplified to:

File.open(‘readme.txt’).each_line do |entry|

or even to:

IO.foreach(‘readme.txt’) do |entry|

Personally, I prefer each_line because IO.foreach sounds like “for each
IO object in …” instead of “for each line in …”.

define_method "test_#{entry}".to_sym do
  puts "testing #{entry}"
end

end
end

Foo.new.test_whatever

Got a bit of help on this one and the solution I’m now going with is:

File.open(‘readme.txt’).each_line do |entry|
self.send(:define_method, entry.strip.to_sym){ puts “testing
#{entry}” }
#self.send(:public, entry.strip.to_sym )
end
end

Hi –

On Fri, 24 Nov 2006, Max R. wrote:

Got a bit of help on this one and the solution I’m now going with is:

File.open(‘readme.txt’).each_line do |entry|
self.send(:define_method, entry.strip.to_sym){ puts “testing
#{entry}” }
#self.send(:public, entry.strip.to_sym )
end
end

define_method takes a string, so you can get rid of “to_sym”. (Same
for public in the commented-out line, if you ever use that.)

David

Use define_method whenever possible. The only case where you would put
code in a string (and then load it using eval) is if your methods need
to accept a block argument (define_method does not accept block argument
until Ruby 1.9).

class Foo
File.open(‘readme.txt’).readlines.each do |entry|
define_method “test_#{entry}”.to_sym do
puts “testing #{entry}”
end
end
end

Foo.new.test_whatever

I still run into undefined method errors using this format. Are there
any restrictions on the format the generated method name can take?

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs