-
Notifications
You must be signed in to change notification settings - Fork 1
Methods
Key characteristics of Ruby methods:
- They are defined on a class or a module
- They are defined at runtime e.g. by the
defstatement or by calling theModule.define_methodmethod - Methods can also be redefined, removed or undefined
- Method calls are invoked on an object, and resolved at runtime using a "message-passing" paradigm - at the time of method call, the object's class and its ancestor classes are searched until one is found which has the method defined on it
- A method's "signature" is just its name - there is no overloading to allow method calls on the same method with different arguments to be resolved to different implementations
- They have powerful and flexible options for passing arguments, including the option of providing either a block or a block argument
- They have a return value
- The name of a method can be an operator, such as
+, in order to define the behaviour of the operator for a class/module
#Method names A method name may be:
-
Any combination of "identity characters" (i.e. alphanumeric,
_or non-ASCII characters), as long as it doesn't begin with a digit -
The name can be one of Ruby's 41 reserved words (e.g.
class), but there are restrictions on what syntax you can use to call it -
The name may begin with an uppercase letter (which is a name normally interpreted as a constant), but there are restrictions on what syntax you can use to call it
-
Any combination of "identity characters" followed by
?or!, as long as it doesn't begin with a digit -
Any combination of "identity characters" followed by
=, as long as it doesn't begin with a digit (this is an "attribute set" method) -
One of the following operators:
| (binary bitwise or) ^ (binary bitwise xor) & (binary bitwise and) <=> (comparison operator) == (equal to) === (case expression match) =~ (match) !~ (not match) > (greater than) >= (greater than or equal to) < (less than) <= (less than or equal to) != (not equal to) << (bitwise left shift) >> (bitwise right shift) + (binary plus) - (binary minus) * (multiply) / (divide) % (modulus) ** (power) ! (boolean unary not) ~ (complement) -
One of the following tokens
+@ (defines unary plus e.g. +value) -@ (defines unary minus e.g. -value) [] (defines array reference get e.g. value[1]) []= (defines array reference set e.g. value[1] = 'hello') ` (defines processing of backquote literal) !@ (same as !) ~@ (same as ~)
A "full" method call, in the sense that this uses the full syntax for a method call, looks like this:
1. value.method <optional_block>
2. value.method(optional_arguments) <optional_block>
3. value.method arguments <optional_block> # command call style
These call method on value with the arguments and block provided. Any method type except attribute set methods can be invoked in this way.
Syntax #3, with no parentheses around the arguments, is called a "command call" and there are syntax grammar restrictions on when you are permitted to use command calls.
Instead of using . you may also use ::, which means the same thing - except if using syntax #1 and the method name begins with an uppercase letter, this will be treated as a constant reference, not a method call (e.g. Object::Class is a constant, while Object.Class and Object::Class() are method calls)
There are also these syntax shorthands, where the method is not specified - the call method will be invoked in these cases:
value.(optional_arguments) <optional block>
value::(optional_arguments) <optional block>
A method call where the object on which the method is invoked is not specified is called a "function call". The method will be invoked on whichever object is currently self.
method(optional_arguments) <optional block>
method arguments <optional block> # command call style
method <block>
Attribute set methods, operator override methods and methods whose name is a Ruby keyword can't be invoked using this syntax.
Where a method is invoked using just the method name itself, this is called a variable call because Ruby must decide whether this is a variable reference or a method call.
method
Attribute set methods, operator override methods, methods whose name is a Ruby keyword, methods whose name begins with an uppercase letter and methods whose name is also used by a local variable in scope can't be invoked using this syntax.
Attribute set methods have a name ending in =, such as attr=. These are invoked by referencing the "attribute" on the left hand side of an assignment, e.g. the following syntax will invoke the attr= method on obj with 1 argument, value:
obj.attr = value
Any other syntax which performs an assignment in Ruby may also be used (e.g. complex assignment operators such as +=, multiple left hand side assignments).
Again, instead of . you may use ::, unless the first character of the attribute name is uppercase, because this will be interpreted as a constant assignment.
Note that as a special exception to normal visibility rules, an invocation on self (e.g. self.attr = value) will be treated as a function call and therefore can call the attribute set method irrespective of its visibility. However, this only works for the '=' operator, not for any complex assignment operators.
The array reference method, [], can be invoked directly (i.e. obj.[]) however it is typically invoked using the array reference operator:
obj[optional_arguments]
There is not any special restriction on the arguments you may provide inside an array reference operator - arguments can be entirely omitted, or they may contain the full range of method call argument possibilities including splats, hash associations and block arguments. Arguments provided in the array reference operator are passed on to the array reference method directly.
The array set method, []=, can be invoked directly (i.e. obj.[]=) however it is typically invoked by using an array reference on the left hand side of an assignment:
obj[optional_arguments] = assigned_value
Any other syntax which performs an assignment in Ruby may also be used (e.g. complex assignment operators such as +=, multiple left hand side assignments).
The arguments passed are similar to the array reference method, however in addition the value to be assigned is appended to the right of all the other arguments except for any block argument provided (but including any hash created by hash associations in the arguments)
Here is the precise logic by which method calls are processed:
- Caller arguments are processed into a "simple" form:
- Splat arguments are splatted - for each splat arg, it's converted to an Array subclass if necessary via
to_aif possible, otherwise it's left as is - Hash associations are combined together into a new Hash object, and this object is added to the end of the arguments (but before a block argument)
- If a block argument is provided
- If it's
nil, it's ignored - Otherwise, it's converted to a Proc subclass if necessary via
to_proc- if this fails, an exception is raised - then the block inside this Proc becomes the block passed to the method - The block will be passed to the method, but the block argument is otherwise removed from the argument list
- If it's
- The class on which the method is defined is resolved:
- The object's class (including its singleton), then each of its ancestor classes in turn (including references to mixed-in modules), are examined to see if the method has an entry on them (including an entry that marks the method as undefined)
- Once a class is found which contains the method, resolution stops
- If the method entry found marks the method as undefined, it's treated as not found
- If the method is found, visibility is checked
- If method visibility is public, OK
- If method visibility is private
- If method was invoked using "function call" or "variable call" syntax, OK
- Otherwise (full syntax), treat the method as not found
- If method visibility is protected
- If method's class is the same as, or an ancestor of, the class of
selfin the scope of the method call, OK - Otherwise, treat the method as not found
- If method's class is the same as, or an ancestor of, the class of
- If the method is not found, and the method searched for is not
method_missing - call
object.method_missing()with the following arguments:- First argument: the missing method name in symbol form
- Remaining arguments: method call's arguments
- Block: the block passed to the method (if any)
- Note this call is treated as a function call and so can access method_missing if its visibility is private.
- If the method is not found and the method searched for IS
method_missing, raise an exception - Arguments are processed into callee's formal arguments
- Each mandatory and post argument is assigned to a single corresponding "simple" form argument
- If there are a multiple assignment structures, each structure (excluding nested ones) is matched to one "simple" form argument, and the "simple" form argument is assigned to the structure using regular multiple assignment logic
- If there are insufficient "simple" form arguments to populate the mandatory and post arguments, an exception is raised
- Optional formal arguments are assigned from left to right - if run out of "simple" form arguments, the remaining optional arguments are assigned their default value
- If any remaining "simple" form arguments
- If the method has a rest formal argument, the remaining "simple" form arguments are put into an array and assigned to the rest argument
- Otherwise, an exception is raised
- If no remaining "simple" form arguments and the method has a rest argument, the rest argument is assigned an empty array
- If the method has a block formal argument
- if a block has been provided, it will be converted to a Proc object if one is not already available (i.e. provided in a block call argument by the caller) and saved to the block formal argument
- otherwise, the block formal argument will be set to nil
- The method is invoked
Because all methods in Ruby can be altered at runtime, including methods on core classes, you may wonder what effect such altering may have on the internal workings of Ruby - to what extent can the behaviour of the language be modified by altering methods on core classes?
With some explicit exceptions, in Ruby's internal workings if it needs to do something with a Ruby core class, it does not invoke Ruby methods, rather it directly invokes C functions in the core class's C API. These C functions are typically the default implementation of the Ruby methods, or they may simply be unavailable to Ruby code. Therefore, unless explicitly designed otherwise, Ruby's internal workings will not honour alterations to core class methods.
For example, you can alter the Array.new method for different behaviour when Array.new is called in Ruby code. However, when Ruby code contains an array literal such as [1, 2], Ruby will create this array without reference to the Array.new method, or any other Ruby method - the behaviour of array literals can't be altered.
Method missing Method formal arguments Method call arguments Undefining Blocks and block arguments send() Arguments to operators Visibility
- If it's already a subclass of Array, it's replaced with its contents
- Otherwise, conversion from the`to_a` method is tried, it's replaced with its contents post-conversion
- If the `to_a` method call fails to resolve or returns `nil`, the splat argument is left as is
- If the returned object from `to_a` is not a subclass of Array, an exception is thrown
A Ruby Language Reference
Copyright © by Michael Hore, 2016.
Introduction to This Document
Ruby Elements
- Classes and Modules
- Methods
- Blocks, Procs and Lambdas
- Execution Context and Closures
- Variables, Constants and Namespaces
- Types and Literals
- Ruby Expressions
- Operators
Syntax Grammar
Exceptions and Throw
Ruby Sourcefiles and Libraries
Multi Threading
Execution and Lifecycle