# Define a class with three methods
class MethodTest
def public_method
p 'In public_method'
end
def method_with_argument x
p "In public2_method - #{x}"
end
def self.class_method
p 'In class_method'
end
private
def private_method
p 'In private_method'
end
end
# Create instance of class
mt = MethodTest.new
# Invoke methods with the dot operator
mt.public_method
mt.method_with_argument('hello')
MethodTest.class_method
begin
mt.private_method
rescue NoMethodError
p $!
end
# Invoke methods with send
mt.send :public_method
mt.send :method_with_argument, 'Hello'
MethodTest.send :class_method
mt.send :private_method
# Invoke methods as objects
mt.method(:public_method).call
mt.method(:method_with_argument).call 'Hello'
MethodTest.method(:class_method).call
mt.method(:private_method).call
The dot operator
I guess this is the most familiar technique, and is common to other languages, like Java and C++. Private methods are not accessible, and instead a
NoMethodError
is raised (and a message is produced informing you that the method is private).The send method
The
send
method is a part of the Object
class and so is available to all objects. It invokes the named method (must be a symbol). As the named method is now being invoked from within the object, this means that all the methods are available, including private and protected methods.Note that you can also use
__send__
, in case you have overwritten the send
method. Overwriting __send__
will generate a warning that it is a bad idea; Rails uses __send__
a lot, for example, on the assumption that no one would be stupid enough to over-write it.A good example of using the
send
method is where you want to access database columns in a large table, where the columns are numbered sequentially, say column0, column1, column2, etc. Rails will handle the generation of methods that will allow @mytable.column1 = 5 and x = @mytable.column1
, but how do you get the total of the columns? Let us suppose ten such columns.total = 0
10.times {|i|
total += send("column" + i.to_s)
}
Strangely, Ruby is quite a stickler for types. In Java or C# you could write
"column" + i
, but Ruby requires the to_s
method to explicitly convert to a string. The send
method sends a message (in OO talk) to the named method, column0, column1, etc. To assign a value, you need to append an equals sign to the method name. This loop assigns zero to each column:10.times {i
send("column" + i.to_s + "=", 0)
}
The
send
method is a variable length method; just send it the right number of parameters for the method you are invoking.The method object
Everything in Ruby is an object, including methods. You can access the method object with
my_object.method(:my_method)
. Here is an example of using the method object for the length method of strings = "string"
puts s.method(:length).class # => Method
puts s.method(:length).call # => 6
puts s.method(:length).methods.sort
# => ["==", "===", "=~", "[]", "__id__", "__send__", ...
As seen earlier, the method that the object represents can be invoked using the
call
method. As with send
, this allows you to access private and protected methods. By the way, you can convert a method to a Proc
using the to_proc
method.One important practical difference between using
send
and using the Method
object is that the latter will only work on methods that are actually defined. It will not work for method calls that go though method_missing
, including calls to column names for ActiveRecord
or calls to the various find methods in Rails. This is because the method has to be defined to become an object.API for the
Method
object:http://www.ruby-doc.org/core/classes/Method.html
Struggling with Ruby: Contents Page
No comments:
Post a Comment