You’ve seen many method invocations in examples throughout this book, and method invocation syntax was described in detail in Method Invocations. Now we turn to the syntax for defining methods. This section explains method definition basics. It is followed by three more sections that cover method names, method parentheses, and method arguments in more detail. These additional sections explain more advanced material and are relevant to both method definition and method invocation.
Methods are defined with the def
keyword. This is followed by the method name and an optional list
of parameter names in parentheses. The Ruby code that constitutes the
method body follows the parameter list, and the end of the method is
marked with the end
keyword.
Parameter names can be used as variables within the method body, and the
values of these named parameters come from the arguments to a method
invocation. Here is an example method:
# Define a method named 'factorial' with a single parameter 'n' def factorial(n) if n < 1 # Test the argument value for validity raise "argument must be > 0" elsif n == 1 # If the argument is 1 1 # then the value of the method invocation is 1 else # Otherwise, the factorial of n is n times n * factorial(n-1) # the factorial of n-1 end end
This code defines a method named factorial
. The method has a single parameter
named n
. The identifier n
is used as a variable within the body of the
method. This is a recursive method, so the body of the method includes
an invocation of the method. The invocation is simply the name of the
method followed by the argument value in parentheses.
Methods may terminate normally or abnormally. Abnormal termination
occurs when the method raises an exception. The factorial
method shown earlier terminates
abnormally if we pass it an argument less than 1
. If a method terminates normally, then the
value of the method invocation expression is the value of the last
expression evaluated within the method body. In the factorial
method, that last expression will
either be 1
or n*factorial(n-1)
.
The return
keyword is used to
force a return prior to the end of the method. If an expression
follows the return
keyword, then
the value of that expression is returned. If no expression follows,
then the return value is nil
. In
the following variant of the factorial
method, the return
keyword is required:
def factorial(n) raise "bad argument" if n < 1 return 1 if n == 1 n * factorial(n-1) end
We could also use return
on
the last line of this method body to emphasize that this expression is
the method’s return value. In common practice, however, return
is omitted where it is not required.
Ruby methods may return more than one value. To do this, use an
explicit return
statement, and
separate the values to be returned with commas:
# Convert the Cartesian point (x,y) to polar (magnitude, angle) coordinates def polar(x,y) return Math.hypot(y,x), Math.atan2(y,x) end
When there is more than one return value, the values are
collected into an array, and the array becomes the single return value
of the method. Instead of using the return
statement with multiple values, we
can simply create an array of values ourselves:
# Convert polar coordinates to Cartesian coordinates def cartesian(magnitude, angle) [magnitude*Math.cos(angle), magnitude*Math.sin(angle)] end
Methods of this form are typically intended for use with parallel assignment (see Parallel Assignment) so that each return value is assigned to a separate variable:
distance, theta = polar(x,y) x,y = cartesian(distance,theta)
A def
statement that defines a method may include exception-handling
code in the form of rescue
,
else
, and ensure
clauses, just as a begin
statement can. These exception-handling clauses go after the end of
the method body but before the end
of the def
statement. In short
methods, it can be particularly tidy to associate your rescue
clauses with the def
statement. This also means you don’t
have to use a begin
statement and
the extra level of indentation that comes with it. See rescue with Method, Class, and Module Definitions for further details.
Methods are always invoked on an object. (This object is
sometimes called the receiver
in a reference to an object-oriented paradigm in which methods are
called “messages” and are “sent to” receiver objects.) Within the body
of a method, the keyword self
refers to the object on which the method was invoked. If we don’t
specify an object when invoking a method, then the method is
implicitly invoked on self
.
You’ll learn how to define methods for classes of objects in Chapter 7. Notice, however, that you’ve already seen examples of invoking methods on objects, in code like this:
first = text.index(pattern)
Like most object-oriented languages, Ruby uses .
to separate the object from the method to
be invoked on it. This code passes the value of the variable pattern
to the method named index
of the object stored in the variable
text
, and stores the return value
in the variable first
.
The methods we’ve defined so far are all global methods. If we place a
def
statement like the ones shown
earlier inside a class
statement,
then the methods that are defined are instance methods of the class;
these methods are defined on all objects that are instances of the
class. (Classes and instance methods are explained in Chapter 7.)
It is also possible, however, to use the def
statement to define a method on a single
specified object. Simply follow the def
keyword with an expression that
evaluates to an object. This expression should be followed by a period
and the name of the method to be defined. The resulting method is
known as a singleton method because it is available only on a single
object:
o = "message" # A string is an object def o.printme # Define a singleton method for this object puts self end o.printme # Invoke the singleton
Class methods (covered in Chapter 7) such as
Math.sin
and File.delete
are actually singleton methods.
Math
is a constant that refers to a
Module
object, and File
is a constant that refers to a Class
object. These two objects have
singleton methods named sin
and
delete
, respectively.
Ruby implementations typically treat Fixnum
and Symbol
values as immediate values rather
than as true object references. (See Immediate values.) For this reason, singleton methods may
not be defined on Fixnum
and
Symbol
objects. For consistency,
singletons are also prohibited on other Numeric
objects.
Methods are defined with the def
statement and may be undefined with
the undef
statement:
def sum(x,y); x+y; end # Define a method puts sum(1,2) # Use it undef sum # And undefine it
In this code, the def
statement defines a global method, and undef
undefines it. undef
also works within classes (which are
the subject of Chapter 7) to undefine the instance
methods of the class. Interestingly, undef
can be used to undefine inherited
methods, without affecting the definition of the method in the class
from which it is inherited. Suppose class A
defines a method m
, and class B
is a subclass of A
and therefore inherits m
. (Subclasses and inheritance are also
explained in Chapter 7.) If you don’t want to allow
instances of class B
to be able to
invoke m
, you can use undef m
within the body of the
subclass.
undef
is not a commonly used
statement. In practice, it is much more common to redefine a method
with a new def
statement than it is
to undefine or delete the method.
Note that the undef
statement
must be followed by a single identifier that specifies the method
name. It cannot be used to undefine a singleton method in the way that
def
can be used to define such a
method.
Within a class or module, you can also use undef_method
(a private method of Module
) to undefine methods. Pass a symbol
representing the name of the method to be undefined.
3.144.8.212