Ruby notes

Expression oriented.

Program structure

#!/usr/bin/ruby -w  # shebang
# -*- coding: utf-8 -*-
# or like this (the above one can be understood by editors):
# coding: utf-8

require 'socket'
...  # code

__END__

...  # data

load and require serve similar purposes, though require is much more commonly used.
require_relative was introduced in Ruby 1.9.
load can also load binary extensions. load expects a complete filename. load can load the same file multiple times.

Execute code at the very beginning/end of the program:

BEGIN {
  ... # global init
}

END {
  ... # global shutdown
}

Load path: $LOAD_PATH or $:.

Autoloading, register name of the undefined constant and library to load:

autoload :TCPSocket, 'socket'

Syntax

Break to new line:

a = 1 +
2 # works because the first line contains not a complete expression

Module1::
  Submodule # same thing

a = Array.new
  .push('example') # works because the first characted is period

::

Encoding::Converter  # namespace

* - splat operator

z, y = *[1, 2]
x, y, z = 1, 2 # z = nil
x, y = 1, 2, 3 # 3 is not assigned
x, y, z = 1, *[2, 3]
x, *y = 1, 2, 3 # x = 1, y = [2, 3]
*x, y = 1, 2, 3 # x = [1, 2], y = 3

Loops

Statements: while, until, and for. Also custom looping using iterators.

The loop variables of a for loop are not local to the loop, they remain defined even after the loop exits!

for i in arr do
  puts i
end

The only difference between the for version of the loop and the each version is that the block of code that follows an iterator does define a new variable scope.

1..10.each do |i|
  puts i
end
loop do
  puts 1
  # next
  break
end
while x > 0:
  puts x
  x = x - 1
end
until x < 0:
  puts x
  x -= 1
end

Inline:

x = 0
puts x = x + 1 while x < 10

Break out from nested loops:

for a in aa do
  catch :my_error do
    for b in bb do
      for c in cc do
        throw :my_error
      end
    end
  end
end

Throw and return value

If throw is called, then the return value of the corresponding catch is, by default, nil. You can, however, specify an arbitrary return value for catch by passing a second argument to throw.

Variables

CONSTANT = 1
$global_var = 1
@@class_variable = 1
@instance_variable = 1
local_variable = 1

Parallel assignment:

x, y = 1, 2

Constans can be defined outside class:

MyClass::MY_CONST = 1

Instance and class variables are encapsulated and effectively private, and constants are effectively public.

Functions

Partial

Use lambda.

Global functions

Global functions are defined as private methods of the Object class.

Methods

A method is a named block of parameterized code associated with one or more objects.

Ruby methods are not objects.

Methods are defined with the def keyword. The return value of a method is the value of the last expression evaluated in its body.

Methods are always invoked on an object (the receiver, and methods are called messages, messages are sent to receiver).

def say
  # method body goes here
end

When a method is defined outside a class or module, it is effectively a global function (technically, becomes a private method of the Object class).

Methods can be defined on individual objects (singleton methods):

def Math.square(x)
  x * x
end

def sefl.my_class_method(x)
  # ...
end

Blocks can be passed to methods:

def take_block(&block)
  block.call
end

take_block do
  puts "Block call"
end

Procs are stored blocks:

talk = Proc.new do
  puts "I am talking."
end

Assignment methods:

def a=(x):
  x + 1
end

# my_object.a = 10  # => 11

Qustion mark suffix:

def can_call?
  true
end

A question mark is used to mark predicates - methods that return a Boolean value.

Exclamation mark suffix: is used to indicate that caution is required with the use of the method.

def delete!
end

Often exclamation mark is used in mutator method that alters the objects in place.
Example: #sort vs #sort!.

Singleton methods are available on a single object:

o = 'message'
def o.printme
  puts self
end

Undefine method:

undef my_method

When method name resolution algorithm fails to find a method, it looks up a method named method_missing instead.

Alias method:

def my_method
end

alias my_method_new_name my_method

Params:

def a_method(s, len=10) end
def a_method(s, *a) end
def a_method(s:, len: 10) end

Methods can be represented as instances of the Method class:

m = 0.method(:succ)
p = m.to_proc

unbound_m = Fixnum.instance_method('+')
unbound_m.bind(2).call

Methods are not closures. The only binding is self - the object on which the method is to be invoked.

A private method is internal to the implementation of a class, and it can only be called by other instance methods of the class (or, its subclasses).

A protected method is like a private method in that it can only be invoked from within the implementation of a class or its subclasses.

Eval private methods from outside:

obj.send(:abc)
obj.public_send(:abc)
obj.instance_eval { ... }

Chaining (overriding methods with original call):

def my_method(a, b)
  super(a, b + 1)
end

If you use super without arguments (bare keywords) - then all the arguments that were passed to the current method are passed to the superclass method.

Flow control

The value of nil is treated the same as false and any other value is the same as true!

x = if x < y then x else y end - this is an expression.

if a == 1
  puts 1
elsif a == 2
  puts 2
else
  puts 'another'
end
&& -> and
|| -> or
! - not

Ternary operator:
ruby true ? "True" : "Not True"

Statement (or expression) modifier:

code if expression
code unless expression

Altering control flow

return - method exit and return a value to its caller.

Nested within blocks

Return is remarkably consistent, it always causes the enclosing method to return, regardless of how deeply nested within blocks it is.

break - exit loop (or iterator).

next - skip the rest of the current iteration and move to the next iteration.

redo - restart loop (or iterator) from the beginning.

retry - restart an iterator, reevaluate the entire expression (can also be used in exception handling).

throw/catch - exception propagation and handling.

Ruby's catch method defines a labeled block of code, and Ruby's throw method causes that block to exit.

Case

The comparison is done using ===!

case a
when 1
  puts 1
when 2
  puts 2
else
  puts 3
end
case
  when x == 1, y == 0 then
    'one and y is zero'
  when x == 2 then
    'two'
end

=== - case equality

For some classes, === is a membership or matching operator.

(1..10) === 5
/\d+/ === '123'
String === 's'
:s === 's'

<=> operator

1 <=> 5 # -1
5 <=> 5 # 0
9 <=> 5 # 1

Input/Output

puts 'Hello!'  # prints the string and appends a newline (unless already ends with a newline)
print 'Hello!'  # prints the string (without appending a newline)
p 'Hello!'  # same a puts + converts objects to string (more programmer friendly)

Program args:

x = ARGV[0]

Threads

Thread.new { File.read(f) }

The key feature of Queue that makes it suitable for concurrent programming is that the deq method blocks if the queue is empty and waits until the producer thread adds a value to the queue.

Fibers

Ruby's fibers are coroutines (semicoroutines), they are no lightweight threads.

f = Fiber.new do |message|
  message = Fiber.yield('Hello!')
end

Introspection (reflection)

Set instance variable:

obj.instance_variable_set(:@a, 0)
Math.const_set(:EPI, Math::E * Math::PI)
o.public_methods
String.private_method_defined? :initialize

def add_method(c, m, &b)
  c.class_eval {
    define_method(m, &b)
  }
end

Module, Class and Object implement several callback methods, or hooks. These methods are not defined by default.
Using hooks we can extend Ruby's behavior when classes are subclassed, when modules are included, or when methods are defined.

For tracing: __FILE__, __LINE__.

Metaprogramming

o.class
o.class.superclass
o.instance_if? String # o.class == String
o.is_a? String # instance of any subclass os String, String === o

Example of metaprogramming in ruby: attr_readers/attr_accessor.

Attributes (creates methods to access @x, @y):

class Point
  # attributes :x => 0, :y => 0
  attributes x:0, y:0  # Ruby 1.9
end

The goal of metaprogramming in Ruby is often the creation of domain-specific languages, or DSLs.

Debugging

Run inline:

ruby -e 'puts "Hello world!"'
irb  # interactive ruby
irb --simple-prompt
pry

pry

require 'pry'
binding.pry

Documentation:

ri Math::sqrt

Types

Type of an object - set of behaviors that characterize the object (the set of methods it responds to).

TRUE, FALSE, NIL constants, but lowercase is preferred.

Literals

Literals = appear directly in source code:

Boolean

Evaluates to false: nil, false. Other - true.

and, or and not are low-precedence versions of &&, ||, !.

Bool operators precedence:

Arrays

Ruby's arrays are untyped and mutable.

<< - append operator.

Last element:

s[s.length - 1]

%w and %W - array literals.

Creation:

Array.new(3) # [nil, nil, nil]

Array.new(2, 0) # [0, 0]

Array.new(3) {|i| i + 1} # [1, 2, 3]

Ranges:

x..y # Range.new(x, y)
x...y # Range.new(x, y, true)
a[-2..-1] # last 2 elements

a[-2,2] = nil # delete last 2 elements

Operations:

a | b
a & b

# clear, compact!, delete_if, each_index, empty?, fill, flatten!, include?, 
# index, join, pop, push, reverse,reverse_each, rindex, sort, sort!, uniq!, unshift.

Set

require 'set'

(1..5).to_set
Set.new(1..5)
Set.new(1. 2, 3)

s.subset? t

Strings

Strings are mutable.
Use freeze to prevent future modification.

"Some text"
"Some text with \""

'Some string'  # no interpolation

# interpolation
"Some text #{1 + 1}"
"Some test #$global_variable_name"
# also can be used sprintf("pi is about %.4f", Math::PI)

%|Some text|

# using %, %q, %Q
# %i - array of symbols
# %q (%Q or % for double quoted rules) - string follows single quoted rules
# %r - regular expression
# %s - symbol
# %w - array of strings
# %x - backtick (can use instead backtick) - it will be executed, for example `ls`

# multiline
result = <<HEREDOC
This would contain specially formatted text.

That might span many lines
HEREDOC

# heredoc starts with << or <<-, the text begins in the next line
# ends when text of the delimiter appears on the line by itself

# Python 2 style
"%d %s" % [3, 'rubies']

Embedded documents as multiline comments:

=begin Multiline comment ...
Another line ...
=end

Multiline:

with_newline = 'line 1 \
line2'

without_newline = 'line 1 '\
'line 2'

a = "This string literal
has two lines \
but is written on three."

Double quoted " string literals support \t, \n, \r, \".

Unicode:

"\u{A5}"  # same as \u00A5

Edit string:

s[-1] = 'x'

s[5,0] = 'y' # insert without deleting

s[5,2] = '' # delete 2 positions 

Ranges:

s[0,2]  # first 2 letters (psition, length)

s[2..3] # positions 2 and 3
s[2...3] # position 2 only

Contains:

s['x'] # contails L

while(s['x'])
  s['x'] = 'y'
end

Regular expressions:

/\d+/

"powerball" =~ /b/

s[/[aeiou]/] = '*' # replace

sub (substitute - replace the first occurrence), gsug (global substitute - replace all), sub!, gsub! for replace:

phone.sub!(/#.*$/, '')

Regular expression modifier characters:

Regular rxpression syntax:

. - Any single character except newline.
\w - Word characters.
\W - Non word characters.
\s - Whitespaces (\t,\n,\r,\f).
\S - Non whitespace.
\d - Digits.
\D - Non digits.
a | b - Expression a or b.
() - Group into syntactic groups and captures text.
(?:) - Groups without capturing text.
(?<name> re) - Groups, captures text, labels.
\k<name> - Matches the same text that matched the named captured group name.
re* - Matches zero or more occurrences.
re+ - Matches one or more occurrences.
re? - Matches zero or one occurrences.
re{n} - Matches exactly n occurrences.
re{n,} - Matches n or more occurrences.
re{n,m} - Matches n to m occurrences.
^ - Beginning of line anchor.
$ - End of line.
\A - Beginning of string.
\z or \Z (before newline) - End of string.
\G - Last match finished.

Exclude characters:
[^-. ] - all substrings that don't contain -, ., or a space.

Conversion:

"1".to_i
"1.1".to_f

Character literals:

?A # equal to char('A')

Succ method:

'a'.succ # 'b'
'b'.succ # 'c'

Encodings:

Encoding.list

Numbers

Numeric:

Examples:

1 / 0 -> ZeroDivisionError
1. / 0 -> Infinity
0.0 / 0.0 -> NaN

Conversion

a = 1
Float(a)
a.to_f

Symbols

Symbols - immutable interned strings. Can be compared by identity rather than by textual content.
Immutable and not garbage-collected strings.

:my_symbol

:'my_symbol'

name = 'my_symbol'
:"#{name}"

%s[""] # :'"'

Hashes

{ "a" => 1 }
{ a: 1 } # same as { :a => 1 }, just colon moves to the end of the hash key and replaces the arrow.

Hash keys must be hashable (should have method hash, returns fixnum hashcode).

Methods:

h = Hash.new(-1) # -1 is default value
h.default = -2 # h['two'] == -2
h.delete(:b)
h.select {|k, v| v % 2 == 0}
h.values_at(:a, :b)
h.delete_if {|k, v| k.to_s < 'b'}
h.invert # swap keys and values
h.clear
h.each_key
h.each_value

Strings as keys

In order for an object to be used as a hash key, it must have a hash method that returns an integer "hashcode" for the object.

Because strings are mutable but commonly used as hash keys, Ruby treats them as a special case and makes private copies of all strings used as keys.

Consider making a private copy or calling the freeze method. If you must use mutable hash keys, call the rehash method of the Hash every you mutate a key.

Ranges

(1..2)  # includes ending values
(1...2)  # excludes ending values

== test equally.
=== for matching and membership.

Procs (procedures)

-> { 1 + 1 }
->(a) { a + 1 }

Procs vs Lambdas

Blocks are syntactic structures in Ruby, can not be manipulated as objects.

Depending on how the block is created, it is called a proc or lambda.

Procs have block-like behavior and lambdas - method-like behavior.

p = Proc.new { |x, y| x + y }
l = lambda { |x, y| x + y }
l = ->(x, y) { x + y }
l = ->(x, y; z=1, c) { x + y } # z and c - local variables
l.call(1, 2)
p[1, 2]
p.(1, 2)

lambda?(p) # false
lambda?(l) # true

proc method in lambda in 1.8 and Proc.new in 1.9.

A Proc is like a block, if you call return - it will return from the method that encloses the block.

Proc and lambda are closures. Methods are not closures.

Blocks

Unlike methods, blocks do not have names, and they can only be invoked indirectly through an iterator method.

Define variable scope.

Block parameters are always local to their block, never assigns values to existing variables (Ruby 1.9).

Use { } or do/end.

do |var|
  put 123
end

Accept block:

def print_message(&b)
  puts(b.call)
end

Check if block given:

yield y if block_given? # iterator?

Block local variables:

1.upto(4) { |x; y, z| p x } # y and z - are block local variables

Proc object

A proc object represents a block.

Both procs and lambdas are functions rather than methods invoked on an object.

Struct

Similar to namedtuple in Python.

Struct

Customer = Struct.new(:name, :address) do
  def greeting
    "Hello #{name}!"
  end
end

dave = Customer.new("Dave", "123 Main")

Making immutable:

Point = Struct.new('Point', :x, :y)
class Point
  undef x=, y=, []=
end

Open and add a method:

class << Point
  def sum(*points)
    x = y = 0
    points.each {|p| x += p.x; y += p.y}
    Point.new(x, y)
  end
end

Enumerable

Rhyming methods:

Turn array into enumerable when passing for processing:

process(data.to_enum)

Usage:

for line, number in text.each_line.with_index
  p "#{number + 1}: #{line}"
end

Turning externally iterable into an Enumerable:

module Iterable
  include Enumerable

  def each
    loop { yield self.next }
  end
end

Iterators

In Ruby, the iterator method is in control and "pushes" values to the block that wants them. The most of other languages do the opposite: the client code that uses the iterator is in control and "pulls" values from the iterator when it needs them.

numbers = [1, 2, 3]
numbers.each { |n| puts n }

Use next instead of return if want to return specific value(s) from the block.

Exceptions

begin
    pass
rescue
    pass
end

begin
    answer = number / divisor
rescue ZeroDivisionError => e
    puts e.message
else
  # when none of the rescue clauses are needed
  # exceptions here will not be handler
ensure
  # code that always runs
end

return in ensure

If an ensure clause includes a return statement, then exception propagation stops, and the containing method returns. break and next have similar effects.

If the body of a begin statement includes a return statement, the code in the ensure clause will be run before the method can actually return to its caller. Furthermore, if an ensure clause contains a return statement of its own, it will change the return value of the method.

"Normal" errors, which typical Ruby programs try to handle, are subclasses of StandardError.
If called with Exception object as a single argument - it raises that exception.
If with string - created StandardError with messages = given text and raises it.
Excption class can be given as argument because it has exception method, also string can be passed as the second argument and will be used as message.
The third argument is backtrace (array if strings).

raise ValueError
raise ValueError, 'value error'

$! refers to the Exception object that is being handled.

rescue does not define a new variable scope.

rescue ValueError, TypeError => e
rescue Exception # catch all
rescue StandardError # retry
  retry
end

The code in the ensure clause is guaranteed to run, but it does not affect the value of the begin statement.

Raise

If raise is called without arguments - it creates a new RuntimeError without message and raises it.

Modules

Similar to class but can not be instantiated and can not be subclassed.

Modules are used as namespaces and as mixins.
If a module defines instance methods instead of the class methods, those instance methods can be mixed into other classes.

class MyCls
  include Enumerable
end

# or

MyCls.new.extend(Enumerable)

It is legal to include one module into another.

Modules can contain constants.

Class is a subclass of Module, so classes can be used as namespace, but can not be used as mixins.

Methods inside module:

module Base64
  MY_CONST = 1

  def self.encode
  end
end

Base64.encode(data)
Base64::MY_CONST

Creating modules like Math or Kernel: define your methods as instance methods of the module. Then use module_function to convert those methods to "modulefunctions" (module_function is similar to private, protected).

Classes

A class is a collection of related methods that operate on the state of an object.

Class instances may encapsulate any number of internal instances, but they expose only methods to the outside world.

Assignment to an attribute of array element is actually Ruby shorthand for method invocation.

Classes and modules are "open", and can be modified and extended at runtime.

Classes can include or inherit methods from modules.

It is possible to define getters and setters for accessing state directly. These pairs of accessor methods are known as attributes and distinct from instance variables.

Any ruby program can add methods to existing classes, and it is even possible to add "singleton methods" to individual objects.

self - within the body of the class, but outside of any instance methods defined by the class, refers to the class being defined.

Class variables are visible to, and shared by, the class methods and the instance methods of a class, and also by the class definition itself.

class Customer
   @@no_of_customers = 0  # class variable
   def initialize(id, name, addr)  # when we call new
      @cust_id = id  # instance variables
      @cust_name = name
      @cust_addr = addr
   end
end

Class can be reopened (take existing class and open it).

Extending a class:

class Sequence
  include Enumerable
end

Overriding methods:

def [](index)
end

def *(factor)
end

new method - allocates memory to hold the new object, initializes the state of that newly allocated "empty" object by invoking initialize method with new arguments.

Getter/setter:

class Value
  def initialize(x)
    @x = x
  end

  def x # getter
    @x
  end

  def x=(value) # setter
    @x = value
  end
end

Using setter within class:

self.x=1

attr_reader/attr_accessor can be used:

class Value
  attr_accessor :x

  def initialize(x)
    @x = x
  end
end

Class attr_reader/attr_accessor:

class Value
  class << self
    attr_reader :a, :b
  end
end

If a subclass assigns a value to a class variable already in use by a superclass, it does not create its own private copy of the class variable, but instead alters the value seen by the superclass.

If a constant is averrided in a subclass - a new constant will be created instead, so the class and its parent will have different constants.

Self

In singleton methods - refers to the class:

class Dog
  def self.about
    self  # Dog
  end
end

Singleton

require 'singleton'

class ExampleState
  include Singleton

  def initialize
    @a = 1
  end

  def inc
    @a += 1
  end
end

ExampleState.instance.inc

Memoization

def something
  @something = Something.new()
end

Copy

Shallow copy:

obj.clone
obj.dup

If an object defines initialize_copy - it will be used to create a copy.

Clone: copies both frozen and tainted objects.
Dup: copies tainted state, copying a frozen object - returns an unfrozen copy.
Clone: copies any singleton methods.
Dup: does not copy singleton methods of an object.

Deep copy:

Marshal.load(Marshal.dump(o))

Subclassing

In Ruby, you should only subclass when you are familiar with the implementation of the superclass. If you only want to depend on the public API of a class and not on its implementation, then you should extend the functionality of the class by encapsulating and delegating to it, not by inheriting from it.

Zen

The Zens of Python and Ruby

The Zen of Python by Tim Peters The Zen of Ruby by Eric Pierce
Beautiful is better than ugly. Beauty is in the eye of the beholder.
Explicit is better than implicit. Implicit is preferable to explicit.
Simple is better than complex. Simple is boring.
Complex is better than complicated. Complex is interesting.
Flat is better than nested. Delegate the details to someone else.
Sparse is better than dense. If possible, make it a one-liner.
Readability counts. Readability is sometimes nice.
Special cases aren't special enough to break the rules. Special cases are everywhere; the rules can't cover them all.
Although practicality beats purity. When in doubt, monkeypatch.
Errors should never pass silently. Errors should be suppressed.
Unless explicitly silenced. Unless whiny nils is turned on.
In the face of ambiguity, refuse the temptation to guess. When in doubt, make assumptions about what the user wanted.
There should be one- and preferably only one -obvious way to do it. There should be many- preferably dozens -of non-obvious ways to do it.
Although that way may not be obvious at first unless you're Dutch. What's obvious to you may be completely unintuitive to someone else.
Now is better than never. Now is better than later.
Although never is often better than right now. And later is better than never.
If the implementation is hard to explain, it's a bad idea. If the design is flawed, explain why in the implementation docs.
If the implementation is easy to explain, it may be a good idea. If the design is good, don't bother with implementation docs.
Namespaces are one honking great idea - let's do more of those! Namespaces are completely unnecessary - let's make everything global!

Vocabulary

The arity of an operator - the number of operands it operates on.

Iterator - any method that uses the yield statement.

External iterator - when the client controls the iteration (we call next when we need the next element, raises StopIteration when no more elements).

Internal iterator - when the iterator controls the iteration.

Lambda - a function that can be manipulated as objects.

A singleton - a class that has only a single instance. Singletons can be used to store global program state within an object-oriented framework and can be useful alternatives to class methods and class variables.

Metaprogramming - writing programs that help you write programs. Is a set of techniques for extending Ruby's syntax in ways that make programming easier.

Weak reference - object which holds a reference to a value without preventing the value from being garbage collected if they become otherwise unreachable.

Deadlock - is the condition that occurs when all threads are waiting to acquire a resource held by another thread.

Libs

Standard

Time

Time.now # (same as Time.new)
Time.local(2007, 7, 8)
Time.utc(2007, 7, 7, 9, 10)

Files

File.unlink('fname') # remove file

Threads

Long-running compute-bound threads should periodically call Thread.pass to ask the scheduler to yield the CPU to another thread.

Thread states:

External

Dry-rb

RSpec

Run:

rspec spec/folder
rspec spec/folter/test_spec.rb
rspec spec/folder/test_spec.rb --example(-e) name
rspec spec/folder/test_spec.rb:25 (code line)
rspec --only-failures (requires config)

Pending:

if 'does something' do
  pending 'Not implemented yet ...'
  expect().to be()
end
Testing

Expect:
- .to
- .not_to

Arrays:

expect().to match_array

describe and it - organization.
expect - verification.
let - initializes data on demand.
context (lias to describe) - grouping for setup and examples.
before hook - run before each example (can be inside context).

Expect:
- be_empty
- eq

Tools

rvm - ruby version manager.

gem install rails - package manager.

Compare to Python

Objects in ruby do not expose attributes, only methods.

The fact that top-level methods are private means that they must be invoked like functions, without an explicit receiver. In this way, Ruby mimics a procedural programming paradigm within its strictly object-oriented framework.

Blocks in Ruby is a powerful and popular. A similar, less functional thing in Python is lambda.

Braces in method calls are optional.

Ruby does not use space to define code blocks.

Ruby iterators = Python generators.

Ruby sequences = Python iterators.

Python namedtuple = Ruby Struct.new.

Python coroutines = Ruby fibers.

String are mutable in Ruby.

Ruby at LaunchSchool
Learn Ruby the Hard Way by Zed Shaw (to read)
The Ruby Programming Language by David Flanagan, Yukihiro Matsumoto

Licensed under CC BY-SA 3.0