When calling a function or method, parens are optional, so:
puts msg
is equivalent to:
puts(msg)
This makes code look clean but can also lead to ambiguity - e.g.:
log decode msg, level
could mean:
log(decode(msg, level))
or:
log(decode(msg), level)
For this reason it's usually the right thing to use parens even though they are optional (and even when the examples in the docs don't use them).
Compounding the situation is that you can use an implict hash as the final argument. The standard syntax for a hash is:
hash = {:key => "value", :key2 => "value2"}
Say we had a hash like:
{:class => "warning"}
And a function:
def p_tag content, options
# some code
end
We could pass that into a function call like this:
p_tag "content", :class => "warning"
which is equivalent to:
p_tag("content", {:class => "warning"})
so it's basically a cheap way to simulate named arguments.
Because the hash uses commas to separate its members and the arguments to the function are also comma-separated we can only use this form for the final positional argument to avoid ambiguity.
A block in Ruby is a unit of code, similar but different to a function. They can be passed around similarly (but different) to how we pass higher order functions in JavaScript.
These aren't passed as regular arguments, but after the arguments like so:
File.open('filename.txt', 'r') do |f1|
while line = f1.gets
puts line
end
end
Blocks can be wrapeed in do..end
or {..}
so the above is equivalent to:
File.open('filename.txt', 'r') {|f1|
while line = f1.gets
puts line
end
}
The convention is to use the {..}
form for single line blocks and do..end
for multiline blocks.