views:

323

answers:

1

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):4

irb(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?

+4  A: 

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.class will be Class, so the method will be available on all classes, not just meta. You probably rather want:

class <<self
  self
end.send

Edit in response to your update:

'a' is a class method and it is not passed on to the new Meta object (c). Why?

Because a is 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, not instance_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 do Meta.a as well as Meta.new.a you have to define both an instance as well as a class method a.

[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).

sepp2k
Also the big caveat with this approach is the method_missing infinite loop of doom problem
Sam Saffron
still now working like that... :|
sardaukar
After making the changes I mentioned, it works fine for me. http://pastie.org/609427
sepp2k