Default method for Ruby class
Is there a way to specify a class method in such a way that when an object is used as if it were a function, that method is called? Something like that:
class MyClass
def some_magic_method(*args)
# stuff happens
end
end
# create object
myob = MyClass.new
# implicitly call some_magic_method
myob 'x'
source to share
As @CarySwoveland mentioned in the comments, you can use method_missing
. A basic example is as follows:
class MyClass
def method_missing(method_name, *args)
if method_name.match?(/[xyz]/)
send(:magic_method, args.first)
else
super
end
end
def magic_method(a)
a = 'none' if a.nil?
"xyz-magic method; argument(s): #{a}"
end
end
myob = MyClass.new
myob.x #=> "xyz-magic method; argument(s): none"
myob.x(1) #=> "xyz-magic method; argument(s): 1"
myob.y #=> "xyz-magic method; argument(s): none"
myob.z #=> "xyz-magic method; argument(s): none"
This captures all methods named x, y, or z. Our branch else
dispatches all other undefined methods to the original one method_missing
:
myob.v #=> test.rb:7:in `method_missing': undefined method `v' for
#<MyClass:0x000000021914f8> (NoMethodError)
#from test.rb:25:in `<main>'
Which methods you use is up to you and is determined in this case by a regular expression /[xyz]/
.
Key methods: BasicObject#method_missing
, Object#send
. For more information check out this question , read "Eloquent Ruby" by Rus Olsen (from which this answer links)
source to share
You want to call the class instance method
when the object is called as a function. This is already supported: an instance method call
is called when you "invoke" an object with a function call method ()
(see How do I reference a function in Ruby? For more details ).
class C
def call(x)
puts "Called with #{x}"
end
end
obj = C.new
obj.(88) # Called with 88 => nil
obj (88) # NoMethodError: undefined method `obj' for main:Object
If you want the latter syntax, the horrible trick is the following (but only works at the top level if you don't bind):
module Kernel
def method_missing(name,*args)
obj = begin
TOPLEVEL_BINDING.local_variable_get(name)
rescue
nil
end
return super if obj.nil?
obj.send :call, *args
end
end
obj = C.new
obj 88 # Called with OK => nil
This example also wants to advise that you should always keep in mind who the receiver of your method calls is and what syntaxes are available for calling methods (especially if you do not include periods and parentheses).
class D
def obj; C.new end
def f
#(obj) 88 # BAD
(obj).(88)
#obj() 88 # BAD
obj().(88)
end
end
The point is that you don't actually have functions, but methods that are called on objects. If you omit the receiver of a method call, then by default the receiver accepts the self
current object. But your example myob
does not show up as an explicit receiver (since there is no next dot as in myob.
), so the current object looks for a method myob
.
source to share