How can I make this code work?
class Meta
@array = [:a,:b]
def self.method_missing(name, *args, &block)
if @array.include? name
self.class.send(:define_method, name) do
do_call(name)
end
else
puts "[#{name.inspect}] is not part of array!"
end
end
def do_call arg
puts "doing call for ['#{arg}'] "
end
end
The idea is to have Meta.a (after being defined) to have
def a
do_call(a)
end
in its body. But after running this, my output is:
[:do_call] is not part of array!
UPDATE: now kinda working like this:
class Meta
@array = [:a,:b]
def self.do_call arg
puts "doing call for ['#{arg}'] "
end
def self.method_missing(name, *args, &block)
puts "#{name} is missing!"
if @array.include? name
self.class.send(:define_method, name) do
do_call name
end
else
puts "[#{name.inspect}] is not part of array!"
end
end
end
but still, here is an excerpt of an IRB session:
[~/code] $ irb -r meta
irb(main):001:0> Meta.a
a is missing!
=> #
irb(main):002:0> Meta.a
doing call for [‘a’]
=> nil
irb(main):003:0> c = Meta.new
=> #
irb(main):004:0> c.a
NoMethodError: undefined method `a’ for #
from (irb):4irb(main):005:0> Meta.methods
=> [“inspect”, “send”, “pretty_inspect”, “class_eval”, “clone”, “yaml_tag_read_class”, > >”public_methods”, “protected_instance_methods”, “send“, “private_method_defined?”,
“equal?”, “freeze”, “do_call”, “yaml_as”, “methods”, “instance_eval”, “to_yaml”, “display”,
“dup”, “object_id”, “include?”, “private_instance_methods”, “instance_variables”, “extend”,
“protected_method_defined?”, “const_defined?”, “to_yaml_style”, “instance_of?”, “eql?”,
“name”, “public_class_method”, “hash”, “id”, “new”, “singleton_methods”,
“yaml_tag_subclasses?”, “pretty_print_cycle”, “taint”, “pretty_print_inspect”, “frozen?”,
“instance_variable_get”, “autoload”, “constants”, “kind_of?”, “to_yaml_properties”, “to_a”,
“ancestors”, “private_class_method”, “const_missing”, “type”, “yaml_tag_class_name”,
“instance_method”, “<“, “protected_methods”, “<=>”, “instance_methods”, “==”,
“method_missing”, “method_defined?”, “superclass”, “>”, “pretty_print”, “===”,
“instance_variable_set”, “const_get”, “is_a?”, “taguri”, “>=”, “respond_to?”, “to_s”, “<=”,
“module_eval”, “class_variables”, “allocate”, “class”, “taguri=”,
“pretty_print_instance_variables”, “tainted?”, “public_instance_methods”, “=~”,
“private_methods”, “public_method_defined?”, “autoload?”, “id“, “nil?”, “untaint”,
“included_modules”, “const_set”, “a”, “method”]
what gives? ‘a’ is a class method and it is not passed on to the new Meta object (c). Why?
You defined do_call as an instance method while you probably intended to define it as a class method. Which is why it calls method_missing for do_call as well and you get the error you’re getting.
Also note that when you do
self.class.send,self.classwill be Class, so the method will be available on all classes, not just meta. You probably rather want:Edit in response to your update:
Because
ais a class method[1]. Instances of a class only get the class’s instance methods.You’re defining a as an instance method on Class and then you try to call it on an instance of Meta, which does not work. In ruby instance methods of Class as well as singleton methods defined on a class can only be called by doing
TheClass.the_method, notinstance_of_the_class.the_method. If you want to call a method on instances of Meta, define it as an instance method. If you want to be able to doMeta.aas well asMeta.new.ayou have to define both an instance as well as a class methoda.[1] Actually, as I already said, the way you’re defining it isn’t even a class method of Meta. It’s an instance method of Class (which means you can also call it as e.g. String.a).