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 whatforce: :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 yield
ing 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
Illegal Quoting in Line 1 Using Ruby CSV
Rails "Template Is Missing" Error
Instantiate Capybara Browser and Set a Proxy
Iterating Over the Registers of a Yardoc '@Macro'
How Does Rails Know My Timezone
How to Create This File Input and Output Assignment in Ruby
Difference Between Block and &Block in Ruby
Shoulda/Rspec Matchers - Conditional Validation
What Ruby Features Are Used in Chef Recipes
Deleting Blank Lines After Loop
Sum of All Amount If Charge Dates Are the Same in Stripe
Watir Won't Download PDF, Only Opens in Viewer
Ruby, No Implicit Conversion of Symbol into Integer
Linking to External File in Ruby on Rails