diff options
Diffstat (limited to 'doc/syntax/methods.rdoc')
| -rw-r--r-- | doc/syntax/methods.rdoc | 197 |
1 files changed, 125 insertions, 72 deletions
diff --git a/doc/syntax/methods.rdoc b/doc/syntax/methods.rdoc index 55feecc0c2..14810a188f 100644 --- a/doc/syntax/methods.rdoc +++ b/doc/syntax/methods.rdoc @@ -11,6 +11,11 @@ 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]. @@ -64,6 +69,23 @@ object to indicate a true value (or "truthy" value). 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 use or name of the operator. Creating an alternate meaning for the operator @@ -78,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 @@ -258,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) @@ -379,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. + +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 -The array argument will capture a Hash as the last entry if a hash was sent by -the caller after all positional arguments. + 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}] @@ -401,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: @@ -441,6 +489,13 @@ 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>: @@ -451,107 +506,105 @@ 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. -=== Keyword and Positional Argument Separation +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: -Between Ruby 2.0 and 2.6, keyword and positional arguments were not -separated, and a keyword argument could be used as a positional argument -and vice-versa. In Ruby 3.0, keyword and positional arguments will -be separated if the method definition includes keyword arguments. -In Ruby 3.0, if the method definition does not include keyword arguments, -keyword arguments provided when calling the method will continue to be -treated as a final positional hash argument. - -Currently, the keyword and positional arguments are not separated, -but cases where behavior will change in Ruby 3.0 will result in a -warning being emitted. - -There are a few different types of keyword argument separation issues. + def meth(arg) + arg + end + meth(a: 1) + # => {:a=>1} -==== Conversion of Hash to Keywords +=== Block Argument -If a method is called with the hash, the hash could be treated as -keywords: +The block argument is indicated by <code>&</code> and must come last: - def my_method(**keywords) - keywords + def my_method(&my_block) + my_block.call(self) end - my_method({a: 1}) # {:a => 1} -This occurs even if the hash could be an optional positional argument -or an element of a rest argument: +Most frequently the block argument is used to pass a block to another method: - def my_method(hash=nil, **keywords) - [hash, keywords] + def each_item(&block) + @items.each(&block) end - my_method({a: 1}) # [nil, {:a => 1}] - def my_method(*args, **keywords) - [args, keywords] +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 - my_method({a: 1}) # [[], {:a => 1}] -However, if the hash is needed for a mandatory positional argument, -it would not be treated as keywords: +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 +block parameter is preferred. This method is equivalent to the first method +in this section: - def my_method(hash, **keywords) - [hash, keywords] + def my_method + yield self end - my_method({a: 1}) # [{:a => 1}, {}] -==== Conversion of Keywords to Positional Arguments +=== Argument Forwarding -If a method is called with keywords, but it is missing one -mandatory positional argument, the keywords are converted to -a hash and the hash used as the mandtory positional argument: +Since Ruby 2.7, an all-arguments forwarding syntax is available: - def my_method(hash, **keywords) - [hash, keywords] + def concrete_method(*positional_args, **keyword_args, &block) + [positional_args, keyword_args, block] end - my_method(a: 1) # [{:a => 1}, {}] - -This is also true for empty keyword splats: - kw = {} - my_method(**kw) # [{}, {}] + def forwarding_method(...) + concrete_method(...) + end -==== Splitting of Positional Hashes or Keywords + forwarding_method(1, b: 2) { puts 3 } + #=> [[1], {:b=>2}, #<Proc:...skip...>] -If a method definition accepts specific keywords and not arbitrary keywords, -keywords or a positional hash may be split if the hash includes both Symbol -keys and non-Symbol keys and the keywords or positional hash are not needed -as a mandatory positional argument. In this case, the non-Symbol keys are -separated into a positional argument hash, and the Symbol keys are used -as the keyword arguments: +Calling with forwarding <code>...</code> is available only in methods +defined with <code>...</code>. - def my_method(hash=3, a: 4) - [hash, a] + def regular_method(arg, **kwarg) + concrete_method(...) # Syntax error end - my_method(a: 1, 'a' => 2) # [{"a"=>2}, 1] - my_method({a: 1, 'a' => 2}) # [{"a"=>2}, 1] -== Block Argument +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). -The block argument is indicated by <code>&</code> and must come last: + def request(method, path, **headers) + puts "#{method.upcase} #{path} #{headers}" + end - def my_method(&my_block) - my_block.call(self) + def get(...) + request(:GET, ...) # leading argument in invoking end -Most frequently the block argument is used to pass a block to another method: + get('http://ruby-lang.org', 'Accept' => 'text/html') + # Prints: GET http://ruby-lang.org {"Accept"=>"text/html"} - def each_item(&block) - @items.each(&block) + def logged_get(msg, ...) # leading argument in definition + puts "Invoking #get: #{msg}" + get(...) 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 -block parameter is preferred. This method is equivalent to the first method -in this section: + logged_get('Ruby site', 'http://ruby-lang.org') + # Prints: + # Invoking #get: Ruby site + # GET http://ruby-lang.org {} - def my_method - yield self +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 Methods have an implied exception handling block so you do not need to use |
