Ruby Code explained
||= is a common Ruby idiom: it assigns the value only if it is not already set. The effect is the same as code like
if some_variable == nil
some_variable = some_value
end
or
some_variable= some_value unless some_variable
===, when not overridden, compares two objects for identity. In the case of Hash === args.last
, Hash (which is an object of type Class) is checking to see if it matches the class of the last item in the args Array. The code is making use of the apparent fact that the implementation of Class#=== forces a check on the class of the compared object.
It doesn't work the other way around, for example:
a = [{}]
Hash === a.last #=> true
a.last === Hash #=> false
The trailing arguments to a method may be supplied as the contents of a hash without needing to provide the {}
So you can do this:
def hello(arg1, arg2, arg3)
puts [arg1.class, arg2.class, arg3.class].join(',')
end
hello 1,2,3 #=> Fixnum,Fixnum,Fixnum
hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash
It's often used to provide a variable-length list of optional parameters to the function.
Are you sure you transcribed the original code precisely, btw? In order to get an array of arguments, you would normally add an * to the argument as declared, otherwise args would have to be input as an array, whcih would rather defeat the object.
def add_spec_path_to(*args) # now args is an array
args << {} unless Hash === args.last # if trailing arguments cannot be
# interpreted as a Hash, add an empty
# Hash here so that following code will
# not fail
args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not
# already set
end
EDIT: Expanding further on the *args thing, try this:
def x(*args)
puts args.join(',')
puts args.map{|a| a.class }.join(',')
end
x 1,2,:a=>5,:b=>6
1,2,a5b6
Fixnum,Fixnum,Hash
... using *args causes args to be presented to the method as an array. If I don't use the *, like this, for example:
def y(args)
puts args.join(',')
puts args.map{|a| a.class }.join(',')
end
... then args has to be an array before I call the method, or I'll get an "ArgumentError: wrong number of arguments" for anything but one thing passed. So it has to look like this:
y [1,2,{:c=>3,:d=>4}]
...with the Hash explicitly created with {}. And it's ugly.
All the above tested with MRI 1.8.6, btw.
What does ||= (or-equals) mean in Ruby?
This question has been discussed so often on the Ruby mailing-lists and Ruby blogs that there are now even threads on the Ruby mailing-list whose only purpose is to collect links to all the other threads on the Ruby mailing-list that discuss this issue.
Here's one: The definitive list of ||= (OR Equal) threads and pages
If you really want to know what is going on, take a look at Section 11.4.2.3 "Abbreviated assignments" of the Ruby Language Draft Specification.
As a first approximation,
a ||= b
is equivalent to
a || a = b
and not equivalent to
a = a || b
However, that is only a first approximation, especially if a
is undefined. The semantics also differ depending on whether it is a simple variable assignment, a method assignment or an indexing assignment:
a ||= b
a.c ||= b
a[c] ||= b
are all treated differently.
What does ||= mean?
Basically, a ||= b
means assign b to a if a is null or undefined or false (i.e. false-ish value in ruby)
, it is similar to a = b unless a
, except it will always evaluate to the final value of a
(whereas a = b unless a
would result in nil
if a
was true-ish).
What is Ruby's double-colon `::`?
::
is basically a namespace resolution operator. It allows you to access items in modules, or class-level items in classes. For example, say you had this setup:
module SomeModule
module InnerModule
class MyClass
CONSTANT = 4
end
end
end
You could access CONSTANT
from outside the module as SomeModule::InnerModule::MyClass::CONSTANT
.
It doesn't affect instance methods defined on a class, since you access those with a different syntax (the dot .
).
Relevant note: If you want to go back to the top-level namespace, do this: ::SomeModule – Benjamin Oakes
What do the different brackets in Ruby mean?
It depends on the context:
When on their own, or assigning to a variable,
[]
creates arrays, and{}
creates hashes. e.g.a = [1,2,3] # an array
b = {1 => 2} # a hash[]
can be overridden as a custom method, and is generally used to fetch things from hashes (the standard library sets up[]
as a method on hashes which is the same asfetch
)
There is also a convention that it is used as a class method in the same way you might use astatic Create
method in C# or Java. e.g.a = {1 => 2} # create a hash for example
puts a[1] # same as a.fetch(1), will print 2
Hash[1,2,3,4] # this is a custom class method which creates a new hashSee the Ruby Hash docs for that last example.
This is probably the most tricky one -
{}
is also syntax for blocks, but only when passed to a method OUTSIDE the arguments parens.When you invoke methods without parens, Ruby looks at where you put the commas to figure out where the arguments end (where the parens would have been, had you typed them)
1.upto(2) { puts 'hello' } # it's a block
1.upto 2 { puts 'hello' } # syntax error, ruby can't figure out where the function args end
1.upto 2, { puts 'hello' } # the comma means "argument", so ruby sees it as a hash - this won't work because puts 'hello' isn't a valid hash
How does ruby tell the difference between instance method and class method definition?
In a method body, self
refers to the receiver. In lines 3..4 of the following, once the receiver is determined to be a User
instance (by the def greeting
syntax), self
refers to that instance.
class User
def greeting
puts "Hi, #{name}" # method body
puts "Hi, #{self.name}" # method body
end
end
In a class body, self
refers to the class. In lines 2, 4, 8, 10 of the following, the class is User
, so def self.greeting
is the same as def User.greeting
.
class User
def self.greeting # class body
# ...
end # class body
end
class User
def greeting # class body
# ...
end # class body
end
But I actually think your real issue is not what self
means, but rather what "an omitted receiver" means, in different contexts.
In method-calling syntax, an omitted receiver stands for self
. So the following two are the same:
name
self.name
In method-defining syntax, an omitted receiver stands for "any instance of the class". So the following two are not the same:
def User.greeting; ... end
def greeting; ... end
When you define an instance method, there is no explicit way to express "any instance of the class", so actually omission is mandatory.
Related Topics
Linkedin API for Company Directory
Get All Instance Variables Declared in Class
Is There a Way in Ruby 1.9 to Remove Invalid Byte Sequences from Strings
How to Skip Has_Secure_Password Validations
Sinatra Configuring Environments on the Fly
Reading and Updating Yaml File by Ruby Code
Is Ruby Really an Interpreted Language If All of Its Implementations Are Compiled into Bytecode
How to Copy File Across Buckets Using Aws-S3 or Aws-Sdk Gem in Ruby on Rails
Check If a Constant Is Already Defined
Difference Between Resource and Resources in Rails Routing
Before/After Suite When Using Ruby Minitest
Rack::Request - How to Get All Headers
Rails Put Validation in a Module Mixin
Nested Forms in Rails - Accessing Attribute in Has_Many Relation
How to Traverse Symlinked Directories in Ruby with a "**" Glob