Ruby

Essence of Ruby

  • Everything (including numbers, blocks) are objects
  • all values except nil and false are true
  • Classes are open (you can modify their definition on the fly); even built-in classes (called "monkey patching")

Fundamentals

Ranges

There are three uses for Ranges:

  1. as a sequence (e.g. 1..5 means 1,2,3,4,5 — but it's not an array!)
  2. as a condition — where the starting expression turns the condition to true and remains true until the ending expression becomes true.
  3. as an interval (for use with the case-equality operator: ===).

Ranges can be inclusive:

ruby-1.9.2-p0 > puts (1..5).to_a.join(",")
1,2,3,4,5

or exclusive
ruby-1.9.2-p0 > puts (1...5).to_a.join(",")
1,2,3,4

Splat Operator ("*")

This operator has two different behaviors: one when applied to lvalues and one when applied to rvalues:

  • When applied to an lvalue, the splat operator turns that reference into a "generous sponge": all other lvalues will be assigned and then the target lvalue gets the remaining values as an array (even if there's only one value left). If there are none left, the lvalue gets an empty array.
one, two, *the_rest = 1, 2, 3, 4, 5  ==>  one = 1; two = 2; the_rest = [3, 4, 5]
one, *the_middle, penultimate, ultimate = 1, 2, 3, 4, 5 ==> one = 1; the_middle = [2, 3]; penultimate = 4; ultimate = 5
one, *sad_panda, two, three, four, five = 1, 2, 3, 4, 5 ==> one = 1; sad_panda = []; two = 2; three = 3; four = 4; five = 5
  • When applied to an rvalue, the splat operator flattens the value. How it is rendered depends on the type of the rvalue:
    • if it is a single object, the resulting value is an array of that object (except nil… *nil = [])
    • if it is an array, then the array is flattened and then put in an array.
    • if it is a hash, then it is converted into an array of arrays, where each inner array has two values: the key and then the value.
    • if it is a range, then it is converted into the array equivalent of the range (if enumerable… for example float ranges are not enumerable).
  • when applied to an rvalue, the splat operator applies only one level, it does not flatten contained values.
a = *4  ==> a = [4]
a = *[1,2,3] ==> a = [1, 2, 3]
a = [1, 2, *[3, 4]] ==> a = [1, 2, 3, 4]
a = *{ one: 1, two: 2 } ==> a [[:one, 1], [:two, 2]]
a = *(1..4) ==> a = [1, 2, 3, 4]
a = *[1, 2, (3..4)] ==> a = [1, 2, (3..4)]
a = [1, 2, *(3..4)] ==> a = [1, 2, 3, 4]

Idioms

Singleton

class Foo
  include Singleton
end

Visitor

class Foo
  include Enumerable
 
  def each
    # ...
    yield
    # ...
  end
end

Observer

class Foo
  include Observable
 
  def something
    changed && notify_observers(event)
  end
end
 
class Bar
  def update(event)
    # process event
  end
end
 
foo = Foo.new
foo.add_observer(Bar.new)

Singleton Class

(aka: "eigenclass", "metaclass")

(class << some_object; self; end).class_eval do
  # some class modifying code
  # that will apply only to this instance.
end

(for more details see Singleton Class)

Memoization / Lazy Initialization

use '||' operator to lazily initialize a value

class Output
  def messages
    @messages ||= []
  end
  def puts(message)
    messages << message
  end
end

Symbols as Procs

In certain situations, it's quite a tidy short-hand to use the block operator on a symbol.

  (0..10).to_a.map(&:to_s)
  1. take the sequence 0 through 10;
  2. convert to an array;
  3. call the map method, executing the supplied block against each element in the array, creating a new array for the results.
  4. here, the passed in block is :to_s … which is not a Proc, it's a Symbol; so Ruby attempts to coerce it into a Proc (by calling to_proc).

Turns out Symbol implements to_proc:

def to_proc
  proc { |obj, *args| obj.send(self, *args) }
end

for more details see section "The Symbol.to_proc Trick" p.363 of "Programming Ruby (Third Edition)

Quirks

Empty Array on Size == slice

  • Array slice where the starting index equals the size of the array and any length value will produce an empty array.
  • Array slize where the starting index is greater than the size of the array, whether a length is specified or not produces a nil.
ruby-1.9.2-p0 > a = [1,2,3]
 => [1, 2, 3] 
ruby-1.9.2-p0 > a.size
 => 3 
ruby-1.9.2-p0 > a[3]
 => nil 
ruby-1.9.2-p0 > a[3,1]
 => [] 
ruby-1.9.2-p0 > a[3,0]
 => [] 
ruby-1.9.2-p0 > a[4,0]
 => nil

Flexible Quote vs. Doc

  • Both Flexible Quote and Documents can be used to contain multi-line strings. But with Documents, the string begins with the next line; whereas in Flexible quotes, the string begins immediately after the open-string delimiter.
a = %{
This is a multi-line string.
There is more than one line and both are stored in the string.
}
b = <<END_OF_DOC
This is also a multi-line string.
However, the first character in the String is 'T' not '\n' as above.
END_OF_DOC

New in 1.9

Strings

Characters used to be represented as integers, but now are single chars.

# 1.8
?a == 97

# 1.9
?a == "a"
a.ord == 97

Study

Questions

  • Can you use "alias" on a class method? If so, what's the syntax?

Notes

  • Many operators can be overloaded (e.g. + - « [] …)
    • "[]=" means Lvalue of a bracket operation (cool, you can define what it means to do an assignment to a portion of your object)
    • "[]" can contain one or more parameters. Look at Array[] for an example.
  • Integrate with AppleScript: use the "rb-appscript" gem.
  • when declaring class methods, the reference variable "self" and the name of the class point to the same exact object: the Class being defined.
class Foo
  puts Foo.object_id
  puts self.object_id
end
  • alias cannot be applied to a local, instance, constant, or class variable.

RI in Unix

Get more colorful displays of RDoc:
export LESS="-r" # tells "less" to interpret ANSI sequences
export RI="—format ansi" # tells "ri" to format using ANSI

Access Control

  • You can specify access control in two ways: 1) as modifying the default access qualifier; 2) as a list of symbols.
    • With the symbol approach, you must specify the access control AFTER the method has been defined.
  • Modifiers are different than in Java:
    • *protected* — allows access to instances of the same class or instances of subclasses; denies all others.
    • *private* — only accessible within the same instance. That is, even instances of the same class do not have access.

Loading other files in a ruby program

  • Ruby source files (.rb) and OS-specific libraries (.so in Unix and .dll in windows) can be loaded.
  • There are two methods included (brought to you by the Kernel class)
    • require
      • you specify a "library name": omitting the extension.
      • require searches the "load path" ($LOAD_PATH), source files win over OS-libs
      • keeps track of files loaded and does not load the same one twice (but you could fool it by playing tricks of the "load path"); see also $LOADED_FEATURES.
    • load
      • literally loads/includes a source file. You specify the filename.
      • will blindly load the same file twice.

References

Finding Gems

Learning Ruby

Random Notes

  • Array.unshift "pushes" a new value on the front of the array, Array.shift "pops" the front-most value from the array.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License