diff options
Diffstat (limited to 'doc/syntax/calling_methods.rdoc')
| -rw-r--r-- | doc/syntax/calling_methods.rdoc | 151 |
1 files changed, 137 insertions, 14 deletions
diff --git a/doc/syntax/calling_methods.rdoc b/doc/syntax/calling_methods.rdoc index 5abecc69da..a24c5fbf1f 100644 --- a/doc/syntax/calling_methods.rdoc +++ b/doc/syntax/calling_methods.rdoc @@ -30,7 +30,41 @@ NoMethodError. You may also use <code>::</code> to designate a receiver, but this is rarely used due to the potential for confusion with <code>::</code> for namespaces. -=== Safe navigation operator +=== Chaining Method Calls + +You can "chain" method calls by immediately following one method call with another. + +This example chains methods Array#append and Array#compact: + + a = [:foo, 'bar', 2] + a1 = [:baz, nil, :bam, nil] + a2 = a.append(*a1).compact + a2 # => [:foo, "bar", 2, :baz, :bam] + +Details: + +- First method <tt>merge</tt> creates a copy of <tt>a</tt>, + appends (separately) each element of <tt>a1</tt> to the copy, and returns + [:foo, "bar", 2, :baz, nil, :bam, nil] +- Chained method <tt>compact</tt> creates a copy of that return value, + removes its <tt>nil</tt>-valued entries, and returns + [:foo, "bar", 2, :baz, :bam] + +You can chain methods that are in different classes. +This example chains methods Hash#to_a and Array#reverse: + + h = {foo: 0, bar: 1, baz: 2} + h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]] + +Details: + +- First method Hash#to_a converts <tt>a</tt> to an \Array, and returns + [[:foo, 0], [:bar, 1], [:baz, 2]] +- Chained method Array#reverse creates copy of that return value, + reverses it, and returns + [[:baz, 2], [:bar, 1], [:foo, 0]] + +=== Safe Navigation Operator <code>&.</code>, called "safe navigation operator", allows to skip method call when receiver is +nil+. It returns +nil+ and doesn't evaluate method's arguments @@ -176,6 +210,24 @@ definition. If a keyword argument is given that the method did not list, and the method definition does not accept arbitrary keyword arguments, an ArgumentError will be raised. +Keyword argument value can be omitted, meaning the value will be fetched +from the context by the name of the key + + keyword1 = 'some value' + my_method(positional1, keyword1:) + # ...is the same as + my_method(positional1, keyword1: keyword1) + +Be aware that when method parenthesis are omitted, too, the parsing order might +be unexpected: + + my_method positional1, keyword1: + + some_other_expression + + # ...is actually parsed as + my_method(positional1, keyword1: some_other_expression) + === Block Argument The block argument sends a closure from the calling scope to the method. @@ -239,16 +291,16 @@ override local arguments outside the block in the caller's scope: This prints: hello main this is block - place is world + place is: world So the +place+ variable in the block is not the same +place+ variable as outside the block. Removing <code>; place</code> from the block arguments gives this result: hello main this is block - place is block + place is: block -=== Array to Arguments Conversion +=== Unpacking Positional Arguments Given the following method: @@ -270,17 +322,58 @@ Both are equivalent to: my_method(1, 2, 3) -If the method accepts keyword arguments, the splat operator will convert a -hash at the end of the array into keyword arguments: +The <code>*</code> unpacking operator can be applied to any object, not only +arrays. If the object responds to a <code>#to_a</code> method, this method +is called, and is expected to return an Array, and elements of this array are passed +as separate positional arguments: + + class Name + def initialize(name) + @name = name + end - def my_method(a, b, c: 3) + def to_a = @name.split(' ') end - arguments = [1, 2, { c: 4 }] - my_method(*arguments) + name = Name.new('Jane Doe') + p(*name) + # prints separate values: + # Jane + # Doe -Note that this behavior is currently deprecated and will emit a warning. -This behavior will be removed in Ruby 3.0. +If the object doesn't have a <code>#to_a</code> method, the object itself is passed +as one argument: + + class Name + def initialize(name) + @name = name + end + end + + name = Name.new('Jane Doe') + p(*name) + # Prints the object itself: + # #<Name:0x00007f9d07bca650 @name="Jane Doe"> + +This allows to handle one or many arguments polymorphically. Note also that <tt>*nil</tt> +is unpacked to an empty list of arguments, so conditional unpacking is possible: + + my_method(*(some_arguments if some_condition?)) + +If <code>#to_a</code> method exists and does not return an Array, it would be an +error on unpacking: + + class Name + def initialize(name) + @name = name + end + + def to_a = @name + end + + name = Name.new('Jane Doe') + p(*name) + # can't convert Name to Array (Name#to_a gives String) (TypeError) You may also use the <code>**</code> (described next) to convert a Hash into keyword arguments. @@ -289,12 +382,13 @@ If the number of objects in the Array do not match the number of arguments for the method, an ArgumentError will be raised. If the splat operator comes first in the call, parentheses must be used to -avoid a warning: +avoid an ambiguity of interpretation as an unpacking operator or multiplication +operator. In this case, Ruby issues a warning in verbose mode: - my_method *arguments # warning + my_method *arguments # warning: '*' interpreted as argument prefix my_method(*arguments) # no warning -=== Hash to Keyword Arguments Conversion +=== Unpacking Keyword Arguments Given the following method: @@ -316,6 +410,35 @@ Both are equivalent to: my_method(first: 3, second: 4, third: 5) +The <code>**</code> unpacking operator can be applied to any object, not only +hashes. If the object responds to a <code>#to_hash</code> method, this method +is called, and is expected to return an Hash, and elements of this hash are passed +as keyword arguments: + + class Name + def initialize(name) + @name = name + end + + def to_hash = {first: @name.split(' ').first, last: @name.split(' ').last} + end + + name = Name.new('Jane Doe') + p(**name) + # Prints: {first: "Jane", last: "Doe"} + +Unlike <code>*</code> operator, <code>**</code> raises an error when used on an +object that doesn't respond to <code>#to_hash</code>. The one exception is ++nil+, which doesn't explicitly define this method, but is still allowed to +be used in <code>**</code> unpacking, not adding any keyword arguments. + +Again, this allows for conditional unpacking: + + my_method(some: params, **(some_extra_params if pass_extra_params?)) + +Like <code>*</code> operator, <code>**</code> raises an error when the object responds +to <code>#to_hash</code>, but it doesn't return a Hash. + If the method definition uses the keyword splat operator to gather arbitrary keyword arguments, they will not be gathered by <code>*</code>: |
