Reputation: 1702
I'm having trouble dynamically defining class methods within a module. See code below. I get a NameError: undefined local variable or method
when trying to reference another class method in the module. Seems like this might be a scope or context issue, but I've not been able to figure it out so far.
module Bar
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def fruits
["apple", "orange", "banana"]
end
def example_function(string)
string.upcase
end
fruits.each do |fruit|
method_name = fruit.to_sym
define_method(method_name) { example_function(fruit) }
end
end
end
class Foo
include Bar
end
puts Foo.apple
puts Foo.orange
puts Foo.banana
I want to be able to call:
puts Foo.apple => "APPLE"
puts Foo.orange => "ORANGE"
puts Foo.banana => "BANANA"
Currently when I try any of these I get the following error:
NameError: undefined local variable or method 'fruits' for Bar::ClassMethods:Module
Additionally, class methods in Bar::ClassMethods should be available to Foo, so I should be able to call:
puts Foo.fruits => ["apple", "orange", "banana"]
Requirements:
Read "Include vs Extend in Ruby" (esp. section titled "A Common Idiom") http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/
Help with this is greatly appreciated!
Upvotes: 3
Views: 5438
Reputation: 1248
The problem is, the fruits
method is defined as an instance method, but you are calling it without an instance on ClassMethods
. Just define it like this:
def self.fruits
["apple", "orange", "banana"]
end
And your code works.
EDIT:
To make the fruits
method accessible as a class method on Foo
too, you have to declare the fruits
array in a way that is accessible while inside the ClassMethods
module. One way would be to declare the array as a class variable and use it in the fruits
method and the each
block. Look at the following code:
module Bar
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
@@fruits = ["apple", "orange", "banana"]
def fruits
@@fruits
end
def example_function(string)
string.upcase
end
@@fruits.each do |fruit|
method_name = fruit.to_sym
define_method(method_name) { example_function(fruit) }
end
end
end
class Foo
include Bar
end
puts Foo.apple
puts Foo.orange
puts Foo.banana
puts Foo.fruits
Upvotes: 2