Given the simple example here:
class Base
@tag = nil
def self.tag(v = nil)
return @tag unless v
@tag = v
end
end
class A < Base
tag :A
end
class B < Base
tag :B
end
class C < Base; end
puts "A: #{A.tag}"
puts "B: #{B.tag}"
puts "A: #{A.tag}"
puts "C: #{C.tag}"
which works as expected
A: A
B: B
A: A
C:
I want to create a module that base will extend to give the same functionality but with all the tag information specified by the class. Eg.
module Tester
def add_ident(v); ....; end
end
class Base
extend Tester
add_ident :tag
end
I’ve found i can do it with a straight eval, so:
def add_ident(v)
v = v.to_s
eval "def self.#{v}(t = nil); return @#{v} unless t; @#{v} = t; end"
end
but i really dislike using eval string in any language.
Is there a way that i can get this functionality without using eval? I’ve gone through every combination of define_method and instance_variable_get/set i can think of and i can’t get it to work.
Ruby 1.9 without Rails.
You want to define a dynamic method on the singleton class of the class you’re extending. The singleton class of a class can be accessed with expression like this:
class << self; self end. To open the scope of a class’s class, you can useclass_eval. Putting all this together, you can write:If you’re using recent versions of Ruby, this approach can be replaced with Module#define_singleton_method:
I don’t believe you want to use
self.class.send(:define_method), as shown in another answer here; this has the unintended side effect of adding the dynamic method to all child classes ofself.class, which in the case ofAin my example isClass.