method_missing shows up in Object.private_methods, not in Object.public_methods.
However, when I call Object.method_missing :stupidmethod, I get
NoMethodError: undefined method `stupidmethod' for Object:Class
I would expect to get
NoMethodError: private method `method_missing' called for Object:Class
because that's what I get when I try to invoke Object's other private methods, e.g. Object.chop.
As more evidence, if I call Object.method_missing without an argument, I get
ArgumentError: no id given
So it seems like I really am invoking that "private" method_missing function from outside of its object. Can you explain this?
EDIT: Thank you to Eugene in the comments. ruby --version tells me 1.8.7. Also, irb --version is 0.9.5(05/04/13). Good to know that this behaves as I'd expect in the later versions.
It is not the private method of Object that is called but the module method in Kernel. You can check which method is called with set_trace_func as described in the answer to a similar question:
irb(main):001:1> set_trace_func proc { |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname if id.to_s == 'method_missing' }
=> #<Proc:0x0423d278#(irb):1>
irb(main):002:0> Object.method_missing :test
c-call (irb):4 method_missing Kernel
c-return (irb):4 method_missing Kernel
NoMethodError: undefined method `test' for Object:Class
from (irb):4
from :0
As some commenters pointed out in MRIs newer than 1.8.7 this behaviour has changed: method_missing has been removed from Kernel and the private instance method from Object was moved to BasicObject which is the new superclass.
Related
Why can't we initialize TrueClass in Ruby? I get this:
TrueClass.new # => NoMethodError: undefined method `new' for TrueClass:Class
But, superclass of TrueClass is Object.
Similarly we are unable to initialize NilClass and FalseClass
I just wanted to know how that is possible even if that is the child class of Object. If we wanted to write a class similar to this how can we achieve it?
I just wanted to know how that is possible even if that is the child class of Object. If we wanted to write a class similar to this how can we achieve it?
You can undefine inherited methods using the undef keyword. Since new is a class method, you'll have to use undef inside the class's singleton class. That would look like this:
class <<MyClass
undef new
end
MyClass.new # NoMethodError: undefined method `new' for MyClass:Class
I just wanted to know how that is possible even if that is the child class of Object.
It works by undefining allocate and new. Here's the corresponding C code:
rb_undef_alloc_func(rb_cTrueClass);
rb_undef_method(CLASS_OF(rb_cTrueClass), "new");
You can achieve a similar result in Ruby via undef_method:
class FooClass
::FOO = new # <- this will be the only Foo instance
class << self
undef_method :allocate
undef_method :new
end
end
FooClass.new #=> NoMethodError: undefined method `new' for FooClass:Class
FooClass.allocate #=> NoMethodError: undefined method `allocate' for FooClass:Class
FOO #=> #<FooClass:0x007fddc284c478>
"similar", because TrueClass.allocate doesn't actually raise a NoMethodError, but a TypeError:
TrueClass.allocate #=> TypeError: allocator undefined for TrueClass
Unfortunately, rb_undef_alloc_func is not available from within Ruby. We could mimic the behavior by overriding allocate:
class FooClass
class << self
def allocate
raise TypeError, "allocator undefined for #{self}"
end
undef_method :new
end
end
FooClass.allocate #=> TypeError: allocator undefined for FooClass
Not sure, which approach is cleaner.
The above changes prevent you from creating an instance via new, but there are other ways:
FOO #=> #<FooClass:0x007fddc284c478>
FOO.dup #=> #<FooClass:0x007fad721122c8>
FOO.clone #=> #<FooClass:0x007f83bc157ba0>
Marshal.load(Marshal.dump(FOO)) #=> #<FooClass:0x007f83bc13e330>
To account for all these special cases, Ruby' stdlib provides the Singleton module:
require 'singleton'
class Foo
include Singleton
end
It works by making allocate and new private methods: (among other changes)
Foo.new #=> NoMethodError: private method `new' called for Foo:Class
Foo.allocate #=> NoMethodError: private method `new' called for
And it adds instance which returns an instance: (or the instance, there's only one)
Foo.instance #=> #<Foo:0x007fdca11117e8>
using external API that has defined as special class where almost all standard methods are undefined to use for building xml. Where #method_missing is responsible to generate elements based on the missing method name called over the object.
Basically in class body there is something to the effect of:
undef_method :send
Now I want to programatically call method by name. I guess I can use eval "obj.#{something}" but I really don't like eval.
I was thinking there must be some dark-side technique to revert the undefining of method #send so I can alias it to #__send__ and undefine it again. That way I can happily call methods by name without eval. e.g. obj.__send__(:something, params).
So my question is how do I use monkey patching to revert the #send method. I can't find anything to that effect. Even can't find anybody asking about it either.
UPDATE: My original problem was non-problem because there is a method #__send__ anyway, I knew only about #send. The other part of the question was how to restore an undefined method. With help of #philomory asnwer here's what I've got:
[46] pry(#<CucuShift::DefaultWorld>)> class A
[46] pry(#<CucuShift::DefaultWorld>)* def gah
[46] pry(#<CucuShift::DefaultWorld>)* puts "gah"
[46] pry(#<CucuShift::DefaultWorld>)* end
[46] pry(#<CucuShift::DefaultWorld>)* end
=> :gah
[49] pry(#<CucuShift::DefaultWorld>)> class C < A
[49] pry(#<CucuShift::DefaultWorld>)* undef_method :gah
[49] pry(#<CucuShift::DefaultWorld>)* end
=> C
[50] pry(#<CucuShift::DefaultWorld>)> C.new.gah
NoMethodError: undefined method `gah' for #<C:0x000000070d7918>
[57] pry(#<CucuShift::DefaultWorld>)> class C
[57] pry(#<CucuShift::DefaultWorld>)* define_method :fff , A.instance_method(:gah)
[57] pry(#<CucuShift::DefaultWorld>)* end
=> :fff
[58] pry(#<CucuShift::DefaultWorld>)> C.new.fff
gah
The dark magic you are looking for is the UnboundMethod class. Use it like this (assuming obj is your builder API object):
send_method = BasicObject.instance_method(:__send__)
bound_method = send_method.bind(obj)
bound_method.call(:method_you_are_calling_dynamically,*arguments)
Of course, depending on what you're trying to accomplish, you could just get the specific method you want and bind it directly, like
id_method = BasicObject.intance_method(:__id__)
bound_method = id_method.bind(obj)
bound_method.call
Or, if the builder class is using method_missing and you just want to dynamically dispatch to that, you don't need send or anything like it at all, just call obj.method_missing(:my_dynamic_method_name)
UPDATE:
It's worth noting that you should not need to alias send to __send__ as __send__ is already provided as part of BasicObject and it is very unusual to un-define it. Though I suppose if you are using an earlier version of Ruby without BasicObject and __send__ you do it this way:
def obj.__send__(*args,&blk)
Object.instance_method(:send).bind(self).call(*args,&blk)
end
I would like to know how to remove the method Kernel.Array.rand. When the user tries to call it, it should give an error; any kind of error would do.
I tried as below. I tried Kernel.Array and Kernel::Array instead of Random, but they did not work too.
class << Random; self; end.send :remove_method, :rand
Using my IRB I saw that:
2.0.0-p195 :028 > Kernel.Array.rand
ArgumentError: wrong number of arguments (0 for 1)
from (irb):28:in `Array'
from (irb):28
And was even available in the autocomplete with tab.
Tried the rand because was necessary to avoid any use of random method. So I need to remove also sample and shuffle from the Array.
But look what I get:
class << Array; self; end.send :remove_method, :sample
NameError: method `sample' not defined in Class
from (irb):31:in `remove_method'
from (irb):31
So, I would still know how to remove a method from the Array, in that case should be related to the Kernel.Array.
You can use undef_method (the difference with remove_method is that undef_method will walk up the inheritance chain)
rand # => 0.3417719504956065
Kernel.send :undef_method, :rand # private method, have to use `send`
rand # ~> -:5:in `<main>': undefined local variable or method `rand' for main:Object (NameError)
Update
Ah, you are confused. There is a Kernel::Array method, which is completely different from Array the class. Also, there's no need to obfuscate your code with those eigenclass constructs. You can do simply this:
module Kernel
undef_method :rand
end
class Array
undef_method :sample
end
rand # ~> -:9:in `<main>': undefined local variable or method `rand' for main:Object (NameError)
[1, 2].sample # ~> -:10:in `<main>': undefined method `sample' for [1, 2]:Array (NoMethodError)
I've found an oddness in Ruby. Using method_missing/respond_to_missing? to dynamically handle method calls initiated via __send__ fails when Kernel defines the same method:
class Testerize
def method_missing(method, *args, &block)
if method == :system
puts 'yay'
else
super
end
end
def respond_to_missing?(method, internal)
return true if method == :system
end
end
t = Testerize.new
puts t.respond_to?(:system)
# Prints true
t.system
# Prints 'yay'
t.__send__(:system)
# Exception: wrong number of arguments (ArgumentError)
Kernel.system is somehow getting in the mix. Does anyone know what's going on here? I would have expected the :system "message" to get posted to the Testerize instance, hit method_missing, and voila. Why isn't my method_missing getting called when using __send__ when it is with direct invocation?
I'm using Ruby 1.9.3, if that is relevant.
with '__send__' or 'send' we can even call private methods of the object.
In your script do:
t.private_methods.grep /system/
You will see system method, while with
t.methods.grep /system/
you will see an empty array.
__send__ tries to call the private method inherited from Kernel in the inheritance chain, hence instead of using __send__ use Ruby's public_send method.
If you look up the ancestor chain of your class (Testerize.ancestors), you will find:
[Testerize, Object, Kernel, BasicObject]
Since new classes inherit from Object by default, and Object inherits from Kernel, all of the instance methods available to Kernel are available in your instances.
When answering another question, I realized that the following program does not quite do what I thought it does.
puts "test"
self.puts "test" # => private method `puts' called for main:Object (NoMethodError)
The exception surprises me, as I always thought that top-level method calls would be resolved by the main object instance, but this doesn't seem to be the case.
Who's the actual receiver of the first call and how is it resolved? Is this a special rule that only applies to method calls at the top-level scope?
Here is a good discussion that talks about this question.
The top level methods, which are provided by Kernel, are automatically included to the Object class. This means the Kernel methods will appear in everything.
The error private method 'puts' called for main:Object (NoMethodError) is just stating that puts exists but is privately scoped.
ree-1.8.7-2011.03 :001 > puts "test"
test
ree-1.8.7-2011.03 :004 > self.send(:puts, "hi" )
hi
UPDATE
There is no magic for Kernel methods. There is no scope hopping or anything. I think the confusion lines in what the scope is when using self. You do not have access to private methods using self.
class PutsTest
def success_puts
private_puts
end
def failed_puts
# trying to access a private method from self
self.private_puts
end
private
def private_puts
puts 'hi'
end
end
By using self, you are changing the scope from calling the method inside of PutsTest to calling from the outside of PutsTest
ree-1.8.7-2011.03 :095 > test = PutsTest.new
ree-1.8.7-2011.03 :096 > test.success_puts
hi
ree-1.8.7-2011.03 :097 > test.failed_puts
NoMethodError: private method `private_puts' called for #<PutsTest:0xd62c48>