Ruby Code Explained

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:

  1. 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
  2. [] 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 as fetch)

    There is also a convention that it is used as a class method in the same way you might use a static 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 hash

    See the Ruby Hash docs for that last example.

  3. 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



Leave a reply



Submit