Understanding Ruby Method Parameters Syntax

Ruby syntax in method calls arguments

create_table is a function and we are calling it with some arguments

No. create_table is a message that is being sent to a receiver. In this case, the message send doesn't mention an explicit receiver, in which case the implicit receiver is always self.

A message send, in turn, will typically lead to an invocation of a method of the same name. (In other languages, message sends are called method calls for that reason.)

There are no functions in Ruby.

I can't figure out what force: :cascade is. Can someone explain what force: :cascade is?

You actually can't know by only looking at the arguments of the message send. It could be either a keyword argument or a Hash literal positional argument.

Keyword arguments allow you to pass arguments to a message send via a name instead of a position (as positional arguments do). This means that you can pass keyword arguments in any order, and it means that you can leave out any optional argument, whereas with positional arguments, you can only leave out optional arguments from the end.

Hash literal positional arguments are a bit of syntactic sugar that allows you to leave out the curly braces if the very last argument of a message send is a Hash literal. They were originally intended to act as a substitute for keyword arguments.

As I wrote above, you cannot tell just from the message send whether it is a keyword argument or a Hash literal positional argument. We actually have to look at the parameter list of the method to see what the argument list of the message means. (Note: this is unique to Hash literal positional arguments and keyword arguments, normally you can tell what an argument list means without looking at the parameter list. This ambiguity is due to the fact that Hash literal positional arguments were originally intended as substitutes for keyword arguments, and then keyword arguments were added in a way that tries to be backwards-compatible with Hash literal positional arguments.)

In older versions of Ruby on Rails, the method is defined like this:

create_table(table_name, options = {})

So, this means that force: :cascade is an optional Hash literal positional argument which gets bound to the optional parameter options.

However, in Ruby on Rails 5, the definition looks like this:

create_table(table_name, comment: nil, **options)

Which means that force: :cascade is a keyword argument that gets bound as part of the **options keyword splat parameter. (A keyword splat parameter acts for keyword arguments like a positional splat parameter acts for positional arguments; it collects all "leftover" keyword arguments in a Hash the same way a positional splat parameter collects all "leftover" positional arguments in an Array.)

So, the answer to your question what force: :cascade is in the code snippet you posted is actually dependent on which version of Ruby on Rails (or more precisely ActiveRecord) you use: it is either a keyword argument or a Hash literal positional argument.

Only in the latter case, i.e. only if you use an older version of Ruby on Rails is it equivalent to

create_table("articles", { force: :cascade }) do |t|

In Ruby on Rails 5, you would be trying to pass two positional arguments to a method that only accepts one.

Understanding Ruby Syntax

respond_to is a method which takes block. The block takes one argument, which here is called format.

Now you call two methods on format. html which you call without arguments. And xml which you call with a block.

This block takes no arguments and contains a call to the render method with a hash as an argument. The hash contains the key :xml and the value @contact_lists.

Is there a way to know how many parameters are needed for a method?

You can use the method Method#arity:

"string".method(:strip).arity
# => 0

From the Ruby documentation:

Returns an indication of the number of arguments accepted by a method.
Returns a nonnegative integer for methods that take a fixed number of
arguments. For Ruby methods that take a variable number of arguments,
returns -n-1, where n is the number of required arguments. For methods
written in C, returns -1 if the call takes a variable number of
arguments.

So, for example:

# Variable number of arguments, one is required
def foo(a, *b); end
method(:foo).arity
# => -2

# Variable number of arguments, none required
def bar(*a); end
method(:bar).arity
# => -1

# Accepts no argument, implemented in C
"0".method(:to_f).arity
# => 0

# Variable number of arguments (0 or 1), implemented in C
"0".method(:to_i).arity
# => -1


Update I've just discovered the exitence of Method#parameters, it could be quite useful:

def foo(a, *b); end
method(:foo).parameters
# => [[:req, :a], [:rest, :b]]

How does Ruby's block syntax work?

Let's first forget about Active Record and focus on the code structure itself. Here is a super simple version of that structure.

class MyBuilder
def initialize
# keys are property names, values are options
@properties = {}
end

def property(name, options={})
@properties[name] = options
end

def build
# For simplicity, just return all properties
@properties
end
end

def create_thing(name)
puts "Begin creating #{name}"

builder = MyBuilder.new

puts "Let user use the builder to define properties"
yield builder

puts "Consume the builder"
properties = builder.build

puts "Persist changes to #{name}..."
# For simplicity just print them out
p properties

puts 'done'
end

create_thing :bar do |builder|
builder.property :counter, color: 'brown'
builder.property :wine_storage, texture: 'wood'
end

Please type the code above by hand to grab some feel.

Although the code above has nothing to do with Active Record, it has the same structure as the migration.

When ever create_table is called, it instantiates a builder (of type TableDefinition), and "pushes" that builder to the block (by yielding it) in order to let user define the tables columns. The builder is consumed later by create_table when the user is done defining the columns.

Whats the ruby syntax for calling a method with multiple parameters and a block?

The space before parentheses is causing ruby to evaluate (:name, :text) as single argument before calling the method which results in a syntax error. Look at these examples for illustration:

puts 1      # equivalent to puts(1)       - valid
puts (1) # equivalent to puts((1)) - valid
puts (1..2) # equivalent to puts((1..2)) - valid
puts (1, 2) # equivalent to puts((1, 2)) - syntax error
puts(1, 2) # valid

Your way of providing the block is syntactically valid, however when the block is not in the same line as the method call it is usually better to use do ... end syntax.

So to answer your question you can use:

item(:name, :text) { label('Name') }

or:

item(:name, :text) do
label('Name')
end

Can you supply arguments to the map(&:method) syntax in Ruby?

You can create a simple patch on Symbol like this:

class Symbol
def with(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

Which will enable you to do not only this:

a = [1,3,5,7,9]
a.map(&:+.with(2))
# => [3, 5, 7, 9, 11]

But also a lot of other cool stuff, like passing multiple parameters:

arr = ["abc", "babc", "great", "fruit"]
arr.map(&:center.with(20, '*'))
# => ["********abc*********", "********babc********", "*******great********", "*******fruit********"]
arr.map(&:[].with(1, 3))
# => ["bc", "abc", "rea", "rui"]
arr.map(&:[].with(/a(.*)/))
# => ["abc", "abc", "at", nil]
arr.map(&:[].with(/a(.*)/, 1))
# => ["bc", "bc", "t", nil]

And even work with inject, which passes two arguments to the block:

%w(abecd ab cd).inject(&:gsub.with('cde'))
# => "cdeeecde"

Or something super cool as passing [shorthand] blocks to the shorthand block:

[['0', '1'], ['2', '3']].map(&:map.with(&:to_i))
# => [[0, 1], [2, 3]]
[%w(a b), %w(c d)].map(&:inject.with(&:+))
# => ["ab", "cd"]
[(1..5), (6..10)].map(&:map.with(&:*.with(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

Here is a conversation I had with @ArupRakshit explaining it further:

Can you supply arguments to the map(&:method) syntax in Ruby?


As @amcaplan suggested in the comment below, you could create a shorter syntax, if you rename the with method to call. In this case, ruby has a built in shortcut for this special method .().

So you could use the above like this:

class Symbol
def call(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11]

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

Here is a version using Refinements (which is less hacky than globally monkey patching Symbol):

module AmpWithArguments

refine Symbol do
def call(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end

end

using AmpWithArguments

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11]

[(1..5), (6..10)].map(&:map.(&:*.(2)))
# => [[2, 4, 6, 8, 10], [12, 14, 16, 18, 20]]

Ruby: differences between () and [] in method parameters

when you call a method in ruby you use (). Sometimes you are allowed to use [] on a particular object and it works, but only because type of this object has [] method defined in its definition. for instance

class Foo
def [](key)
key
end
end

# and later you can call:

foo = Foo.new
foo['anything']

What does @param and @return mean in Ruby?

From https://yardoc.org/:

YARD is a documentation generation tool for the Ruby programming
language. It enables the user to generate consistent, usable
documentation that can be exported to a number of formats very easily,
and also supports extending for custom Ruby constructs such as custom
class level definitions.

# @param {ListNode} l1
# @param {ListNode} l2
# @return {ListNode}

is the YARD documentation of the add_two_numbers method. It means that the method takes two parameters (l1 and l2, both are ListNode instances) and returns a new ListNode instance.

Ruby passing multiple parameters in methods

You need to do this:

def ss(a,method="GET", *b)
puts a
end

When splat operators are used to accept multiple inputs, it should always be the last parameter of a method. Also a method can have only one splat parameter.

Thanks Cary for a clear and crisp explanation over how to use splats:

Basically, the rule is that if it's unambiguous, it's OK--Ruby will
figure it out. If, however, you add a variable with a default value to
my example (def test(*a,b); p a; p b; end; test 1,2,3), it becomes ambiguous, so Ruby will complain.



Related Topics



Leave a reply



Submit