diff options
Diffstat (limited to 'doc/syntax/methods.rdoc')
| -rw-r--r-- | doc/syntax/methods.rdoc | 240 |
1 files changed, 217 insertions, 23 deletions
diff --git a/doc/syntax/methods.rdoc b/doc/syntax/methods.rdoc index a47c1a3cbf..14810a188f 100644 --- a/doc/syntax/methods.rdoc +++ b/doc/syntax/methods.rdoc @@ -11,14 +11,19 @@ A method definition consists of the +def+ keyword, a method name, the body of the method, +return+ value and the +end+ keyword. When called the method will execute the body of the method. This method returns +2+. +Since Ruby 3.0, there is also a shorthand syntax for methods consisting +of exactly one expression: + + def one_plus_one = 1 + 1 + This section only covers defining methods. See also the {syntax documentation on calling methods}[rdoc-ref:syntax/calling_methods.rdoc]. == Method Names Method names may be one of the operators or must start a letter or a character -with the eight bit set. It may contain letters, numbers, an <code>_</code> -(underscore or low line) or a character with the eight bit set. The convention +with the eighth bit set. It may contain letters, numbers, an <code>_</code> +(underscore or low line) or a character with the eighth bit set. The convention is to use underscores to separate words in a multiword method name: def method_name @@ -26,7 +31,7 @@ is to use underscores to separate words in a multiword method name: end Ruby programs must be written in a US-ASCII-compatible character set such as -UTF-8, ISO-8859-1 etc. In such character sets if the eight bit is set it +UTF-8, ISO-8859-1 etc. In such character sets if the eighth bit is set it indicates an extended character. Ruby allows method names and other identifiers to contain such characters. Ruby programs cannot contain some characters like ASCII NUL (<code>\x00</code>). @@ -62,9 +67,24 @@ Methods that end with a question mark by convention return boolean, but they may not always return just +true+ or +false+. Often, they will return an object to indicate a true value (or "truthy" value). -Methods that end with an equals sign indicate an assignment method. For -assignment methods, the return value is ignored and the arguments are returned -instead. +Methods that end with an equals sign indicate an assignment method. + + class C + def attr + @attr + end + + def attr=(val) + @attr = val + end + end + + c = C.new + c.attr #=> nil + c.attr = 10 # calls "attr=(10)" + c.attr #=> 10 + +Assignment methods can not be defined using the shorthand syntax. These are method names for the various Ruby operators. Each of these operators accepts only one argument. Following the operator is the typical @@ -80,6 +100,7 @@ operators. <code>/</code> :: divide <code>%</code> :: modulus division, String#% <code>&</code> :: AND +<code>|</code> :: OR <code>^</code> :: XOR (exclusive OR) <code>>></code> :: right-shift <code><<</code> :: left-shift, append @@ -94,8 +115,8 @@ operators. <code>></code> :: greater-than <code>>=</code> :: greater-than or equal -To define unary methods minus, plus, tilde and not (<code>!</code>) follow the -operator with an <code>@</code> as in <code>+@</code> or <code>!@</code>: +To define unary methods minus and plus, follow the operator with an +<code>@</code> as in <code>+@</code>: class C def -@ @@ -107,6 +128,13 @@ operator with an <code>@</code> as in <code>+@</code> or <code>!@</code>: -obj # prints "you inverted this object" +The <code>@</code> is needed to differentiate unary minus and plus +operators from binary minus and plus operators. + +You can also follow tilde and not (<code>!</code>) unary methods with +<code>@</code>, but it is not required as there are no binary tilde +and not operators. + Unary methods accept zero arguments. Additionally, methods for element reference and assignment may be defined: @@ -253,6 +281,13 @@ The parentheses around the arguments are optional: value + 1 end +The parentheses are mandatory in shorthand method definitions: + + # OK + def add_one(value) = value + 1 + # SyntaxError + def add_one value = value + 1 + Multiple arguments are separated by a comma: def add_values(a, b) @@ -283,6 +318,25 @@ This will raise a SyntaxError: a + b + c end +Default argument values can refer to arguments that have already been +evaluated as local variables, and argument values are always evaluated +left to right. So this is allowed: + + def add_values(a = 1, b = a) + a + b + end + add_values + # => 2 + +But this will raise a +NameError+ (unless there is a method named ++b+ defined): + + def add_values(a = b, b = 1) + a + b + end + add_values + # NameError (undefined local variable or method `b' for main:Object) + === Array Decomposition You can decompose (unpack or extract values from) an Array using extra @@ -355,11 +409,22 @@ converted to an Array: gather_arguments 1, 2, 3 # prints [1, 2, 3] -The array argument must be the last positional argument, it must appear before -any keyword arguments. +The array argument must appear before any keyword arguments. -The array argument will capture a Hash as the last entry if a hash was sent by -the caller after all positional arguments. +It is possible to gather arguments at the beginning or in the middle: + + def gather_arguments(first_arg, *middle_arguments, last_arg) + p middle_arguments + end + + gather_arguments 1, 2, 3, 4 # prints [2, 3] + +The array argument will capture a Hash as the last entry if keywords were +provided by the caller after all positional arguments. + + def gather_arguments(*arguments) + p arguments + end gather_arguments 1, a: 2 # prints [1, {:a=>2}] @@ -377,6 +442,13 @@ Also, note that a bare <code>*</code> can be used to ignore arguments: def ignore_arguments(*) end +You can also use a bare <code>*</code> when calling a method to pass the +arguments directly to another method: + + def delegate_arguments(*) + other_method(*) + end + === Keyword Arguments Keyword arguments are similar to positional arguments with default values: @@ -395,13 +467,56 @@ Arbitrary keyword arguments will be accepted with <code>**</code>: # prints 1 then {:second=>2, :third=>3} When calling a method with keyword arguments the arguments may appear in any -order. If an unknown keyword argument is sent by the caller an ArgumentError -is raised. +order. If an unknown keyword argument is sent by the caller, and the method +does not accept arbitrary keyword arguments, an ArgumentError is raised. + +To require a specific keyword argument, do not include a default value +for the keyword argument: + + def add_values(first:, second:) + first + second + end + add_values + # ArgumentError (missing keywords: first, second) + add_values(first: 1, second: 2) + # => 3 When mixing keyword arguments and positional arguments, all positional arguments must appear before any keyword arguments. -== Block Argument +Also, note that <code>**</code> can be used to ignore keyword arguments: + + def ignore_keywords(**) + end + +You can also use <code>**</code> when calling a method to delegate +keyword arguments to another method: + + def delegate_keywords(**) + other_method(**) + end + +To mark a method as accepting keywords, but not actually accepting +keywords, you can use the <code>**nil</code>: + + def no_keywords(**nil) + end + +Calling such a method with keywords or a non-empty keyword splat will +result in an ArgumentError. This syntax is supported so that keywords +can be added to the method later without affected backwards compatibility. + +If a method definition does not accept any keywords, and the +<code>**nil</code> syntax is not used, any keywords provided when calling +the method will be converted to a Hash positional argument: + + def meth(arg) + arg + end + meth(a: 1) + # => {:a=>1} + +=== Block Argument The block argument is indicated by <code>&</code> and must come last: @@ -415,8 +530,15 @@ Most frequently the block argument is used to pass a block to another method: @items.each(&block) end +You are not required to give a name to the block if you will just be passing +it to another method: + + def each_item(&) + @items.each(&) + end + If you are only going to call the block and will not otherwise manipulate it -or send it to another method using <code>yield</code> without an explicit +or send it to another method, using <code>yield</code> without an explicit block parameter is preferred. This method is equivalent to the first method in this section: @@ -424,14 +546,64 @@ in this section: yield self end -There is also a performance benefit to using yield over a calling a block -parameter. When a block argument is assigned to a variable a Proc object is -created which holds the block. When using yield this Proc object is not -created. +=== Argument Forwarding + +Since Ruby 2.7, an all-arguments forwarding syntax is available: + + def concrete_method(*positional_args, **keyword_args, &block) + [positional_args, keyword_args, block] + end + + def forwarding_method(...) + concrete_method(...) + end + + forwarding_method(1, b: 2) { puts 3 } + #=> [[1], {:b=>2}, #<Proc:...skip...>] -If you only need to use the block sometimes you can use Proc.new to create a -proc from the block that was passed to your method. See Proc.new for further -details. +Calling with forwarding <code>...</code> is available only in methods +defined with <code>...</code>. + + def regular_method(arg, **kwarg) + concrete_method(...) # Syntax error + end + +Since Ruby 3.0, there can be leading arguments before <code>...</code> +both in definitions and in invocations (but in definitions they can be +only positional arguments without default values). + + def request(method, path, **headers) + puts "#{method.upcase} #{path} #{headers}" + end + + def get(...) + request(:GET, ...) # leading argument in invoking + end + + get('http://ruby-lang.org', 'Accept' => 'text/html') + # Prints: GET http://ruby-lang.org {"Accept"=>"text/html"} + + def logged_get(msg, ...) # leading argument in definition + puts "Invoking #get: #{msg}" + get(...) + end + + logged_get('Ruby site', 'http://ruby-lang.org') + # Prints: + # Invoking #get: Ruby site + # GET http://ruby-lang.org {} + +Note that omitting parentheses in forwarding calls may lead to +unexpected results: + + def log(...) + puts ... # This would be treated as `puts()...', + # i.e. endless range from puts result + end + + log("test") + # Prints: warning: ... at EOL, should be parenthesized? + # ...and then empty line == Exception Handling @@ -454,6 +626,28 @@ May be written as: # handle exception end +Similarly, if you wish to always run code even if an exception is raised, +you can use +ensure+ without +begin+ and +end+: + + def my_method + # code that may raise an exception + ensure + # code that runs even if previous code raised an exception + end + +You can also combine +rescue+ with +ensure+ and/or +else+, without ++begin+ and +end+: + + def my_method + # code that may raise an exception + rescue + # handle exception + else + # only run if no exception raised above + ensure + # code that runs even if previous code raised an exception + end + If you wish to rescue an exception for only part of your method, use +begin+ and +end+. For more details see the page on {exception handling}[rdoc-ref:syntax/exceptions.rdoc]. |
