Mon 02 Apr 2018 — Mon 07 May 2018

Ruby

Windows support

Ruby is a bit funny about Windows. It's usually distributed with some emulated Unix tools.

Editing in Emacs

Emacs has ruby-mode and org-babel support for Ruby build in. Just add (ruby . t) to the org-babel-load-languages alist.

On the command line, there is a read-eval-print-loop (REPL) called irb, for Interactive Ruby.

Encoding

Since version 2.0, Ruby uses utf-8 as default. Good.

Comments

Lines starting with # are comments.

All lines between =begin and =end are too.

Operators

Comparison operator: a <=> b. This is also sometimes called the spaceship operator. It returns an integer telling us whether a is less than, equal to, or greater than b.

.. and ... are the open and closed range operators.

|| and or are or, but the latter has low operator precedence.

Null coalescing method calls: &..

Variables

Most variables are references to an object. Some variables are primitive types (in Ruby, these are called immediate values), which get stored directly.

As with Java, C#, and most other recentish languages, what we are usually doing most of the time is call by value, but the value we pass is a reference (except for the primitive types mentioned above, which are immutable anyway).

There is no call by reference like R's promises or <<- operator.

You can use * on the left hand side of a variable assignment to create an array, or on the right hand side to destructure/expand one.

Variables have some different properties depending on their first character:

$
evil global variable
@
instance (this object)
[a-z_]
local (lives as long as current scope/environment)
[A-Z]
constant (warning if you try to redefine)

You can put hooks on global variables, which fire when they are set.

There are some useful built-in globals.

For constants, it's usually a good idea to put them in a Module.

Variables start out as nil, except for local variables. Local variables must be assigned to before use.

Branching

There's a ternary operator, which looks like C or Perl: condition ? trueResult : falseResult.

There's also an if/elsif/end branching statement:

if true == false
  "Logic is broken!"
elsif 2 + 2 == 5
  "A boot stamping on a human face — FOREVER!"
else
  "Nothing to see here, move along."
end
Nothing to see here, move along.

You can use until instead of if not. This may or may not be more readable. Use judgement.

There's also a kind of guard clause, which in Ruby is called an if modifier puts "true" if true.

Lastly, we have a switch statement, which uses JavaScript-style type-coerced matching:

case "oy"
when "nope"
  false
when "yep"
  true
when /\w+/
  "regex match"
end
regex match

Loops

We have while, until and for. Like blocks, they finish with end. Until is just the negation of while — don't confuse it for do...until from other languages.

Unlike blocks, they don't create a new scope.

While has an additional form similar to the if modifiers:

i = 7
puts i+=1 while i < 10
8
9
10

Loops have a redo statement, which repeats the current iteration, and a retry statement, which tries to repeat the whole loop.

In Ruby, we often prefer an iterator to a loop.

Blocks

Use the do and end do ... end keywords, or braces { ... }.

They introduce a new scope for variables. Variables from outside the block used inside the block aren't shadowed. You can modify that variable directly.

Declare parameters as |a, b, c|.

Booleans

Only false and nil are falsey values. The empty list, empty string, empty set, and so on, are all truthy.

Symbols

Symbols are prefixed with a colon, like in Clojure: :symbol. They are interned (deduplicated and cached).

You can use them to access variables. binding.local_variable_get(:symbol) or eval("{#:symbol}").

Ranges

You can write a range with ... Ranges have a to_a() method which returns an array.

puts 0..5
puts ("a".."z").to_a.join("")
0..5
abcdefghijklmnopqrstuvwxyz

Arrays

Array literals look like Python [1, 2, 3].

You can index into arrays, and use negative numbers to index from the end. You can use a pair of values or a range to slice an index. Ranges are considered open for this purpose.

puts [0, 1, 2, 3, 4, 5][3]
puts [0, 1, 2, 3, 4, 5][0, 3].join("")
puts [0, 1, 2, 3, 4, 5][0..3].join("")
3
012
0123

The << operator appends to an array:

a = [0, 1, 2, 3]
a << 4

a.join("")
1234

Strings

Strings are mutable in Ruby. This is likely to change in Ruby 3.0. You can also make your strings immutable by called the freeze method on them.

There are single-quoted and double-quoted string literals.

Single-quoted strings have simpler escaping rules (like Python's raw strings). Double-quoted strings allow expression interpolation using #{}.

Objects have a to_s method (convert object to string).

You can concatenate strings with +, and duplicate then with *.

You can use array indexing and slicing on them.

puts "a" + "b" * 5
puts "hello"[0,3]
puts "hello"[3..-1]
abbbbb
hel
lo

You can convert between strings and arrays using the split and join functions on objects of those types.

The chop function strips trailing whitespace.

"\n\thello    \t\n\n".chop()

hello

There are useful iterators on string like each_byte and each_line.

Regexp

Regular expression literals are surrounded with /. You can test a string against a regular expression with =~.

You can compile a regex with Regexp.new(/some-pattern/).

You can also use sub gsub to replace one and all patterns in a string, respectively.

puts "hello, world" =~ /[a-zA-Z, ]+/ # truthy
puts "12345" =~ /[a-zA-Z]+/ # falsey

pattern = Regexp.new(/l/)
puts "hello".sub(pattern, "iy")
puts "hello".gsub(pattern, "iy")
0

heiylo
heiyiyo

Numbers

Large integers are automatically taken care of since Ruby 2.4.

There's an r syntax for representing rational numbers (fractions).

There's an i syntax for imaginary parts of numbers.

Dictionaries

In Ruby they're called hashes.

Created as: {key => value, key2 => value2}.

If you're using symbols as keys, there's a shorted syntax: {key: "value", key2: "value2"}.

Hashes in Ruby have a to_proc method, which spit out an anonymous function. This can be good for shinking your code.

TODO Iterators

As with C# and Python, we use the yield keyword to create an iterator. Unlike C# and Python, the job of this keyword is to pass the control flow to a block. It's inside out compared to those languages. The iterator is the calling part of code.

In Java or C#, you write some code to make something iterable, and then passing it to general methods which operate on it like map or reduce, along with a (usually anonymous) function to do something on the way. This is an external iterator.

In Ruby, you instead implement map or reduce separately on each type of collection as an iterator method. The iterator itself then takes your anonymous function. This is an internal iterator.

Ruby's Enumerable class does both. It also provides lots of helpful methods for operating on them (for example: chunking, lazy evaluation).

You can use the redo and retry keywords inside a function. They often provide a neat way to implement an iterator. I don't really understand this yet.

TODO: this is something to revisit once I've actually used the language a bit more.

Loop

There is a special method called Loop, which exists on the Kernel class. It's job is to run a block in a new scope.

Proc

The Proc class defines a procedure, which can be passed to a method. Use the proc keyword in front of a block.

It can be more useful than an ordinary block, because it closes over an environment.

closedOver = "hello"
thing = proc {
  puts "I kept hold of " + closedOver
}

Methods

Declared with def.

Method calls without arguments can omit the parentheses. This can lead to some ambiguity, so be careful.

Ruby is really into OO. If you define a function outside of a class, they are actually a private method of the Object class, which is the root of the whole class hierarchy.

You can use the self variable to get the object a method was dispatched on. You don't need to include this in the method signature: it's implicit.

Varargs is done the same way as in Python: using *, like def blah(first, *rest). You can also use it at method call time to apply an array as multiple arguments. This mirrors the variable assignment destructuring and array creation syntax.

Method arguments can have default values, using the same syntax as in every over language.

Like R or a Lisp, Ruby returns the last value in an function — you don't need a return statement unless you want an early exit.

Singleton Methods

This naming might be a bit confusing. Singleton methods exist on a single object, and not on its class. They have override priority over class methods.

Ruby only lets you define methods on classes. When you do add a singleton method to an object, it works around that by adding a singleton class to that object.

class Thing
  def my_instance_method
    "Instance method — shared by all objects of this class."
  end
end

o = Thing.new

def o.my_singleton_method
  "Singleton method — only exists on this instance."
end

def Thing.my_static_method
  "Static method (class singleton method) — only exists on class itself"
end

puts o.my_instance_method
puts o.singleton_methods
puts o.my_singleton_method
puts Thing.my_static_method

## This would be an error:
## puts o.my_static_method
Instance method — shared by all objects of this class.
my_singleton_method
Singleton method — only exists on this instance.
Static method (class singleton method) — where do I use this?

You can think of languages with prototype-based inheritance (Self, JavaScript) as only having singleton methods.

You can add a singleton method to a class (classes are instances of Class). This gets you the equivalent of a static method in C# or Java.

Method Call Overriding

Ruby finds for a method to dispatch by searching up the inheritance graph. Look at SomeClass.ancestors to see the order.

There's a method_missing method which you can use to implement your own dispatch for cases when the method is not found.

You can use super.someMethod to call up the hierarchy.

Modules

Modules are stripped down classes. Class is a subclass of Module.

Their main use is for mixins, but they can also be a useful bundle of static methods.

module MyModule
  def InstanceThingy()
       5
  end

  def self.StaticThingy()
       4
  end
end

puts MyModule.StaticThingy
4

Classes

Use the class keyword, instantiated with new ClassName. When you create an object, it goes on the heap.

As mentioned earlier, classes are instances of the class Class. This allows some metaprogramming.

Classes can have:

method definitions
def method(args)
attributes
attr :attr_name.
visibility controls for methods
private :symbol, protected :symbol and public :symbol.
mixins
include SomeClass

Objects have a send(symbol) function which does dispatch on a symbol match a method name: thing.send(:methodname).

They also have a method(symbol) function which gets a method object: thing.method(:methodname).call.

You can inspect the methods on a class using instance_methods(SomeClass).

Instance Variables (attributes)

Variables are marked as instance variables by starting their name with @. Instance variables are private to the object.

You could define some methods to allow access to them:

class Thingy
  def myVar()
    @myVar
  end

  def myVar=(myVar)
    @myVar=myVar
  end
end

t = Thingy.new
puts t.myVar
t.myVar = 10
puts t.myVar


10

This is a bit wordy though, so we have something similar to auto-properties from C#:

class Thingy
  ## calling the method 'attr_accessor' with the symbol ':myVar' fills
  ## in an auto-property pattern for the instance variable @myVar
  ##
  ## attr_accessor combines attr_reader and attr_writer
  attr_accessor :myVar
end

t = Thingy.new
puts t.myVar
t.myVar = 10
puts t.myVar


10

Static Variables

Called class variables in Ruby. Start your variable's name with @@.

There are also class instance variables, which aren't available to subclasses, which start with a single @ instead.

This last one is potentially confusing. When you define a variable starting with @ inside the class, it will be a class instance variable, and is not accessible to the instance. If you try to get or set it, the instance will create its own instance-specific copy.

class ConfusingVariables
  ## Write two @@ if you want this to be heritable.
  @myClassInstanceVariable = 10

  ## Static variable accessor — this will see the 10 above
  def self.getMyClassInstanceVariable()
    @myClassInstanceVariable
  end

  ## Instance variable accessor — won't see the 10 above, but will make its own copy.
  def getMyClassInstanceVariable()
    @myClassInstanceVariable
  end


  def getMyInstanceVariable()
    @myInstanceVariable
  end

  def setMyInstanceVariable(myInstanceVariable)
    @myInstanceVariable = myInstanceVariable
  end
end

o1 = ConfusingVariables.new
o2 = ConfusingVariables.new

o1.setMyInstanceVariable 10
o2.setMyInstanceVariable 5

puts "Instance variable 1: #{o1.getMyInstanceVariable}"
puts "Instance variable 2: #{o2.getMyInstanceVariable}"

puts "Class instance variable accessed from instance: #{o1.getMyClassInstanceVariable}"
puts "Class instance variable accessed from class: #{ConfusingVariables.getMyClassInstanceVariable}"

Instance variable 1: 10
Instance variable 2: 5
Class instance variable accessed from instance:
Class instance variable accessed from class: 10

Standard Methods

It can be a good idea to implement these on your classes:

to\s
string output of an object
inspect
useful debugging information about an object
inintialize
constructor method, can take parameters

Imports

Packages are called Gems. The gem command-line tool fetchs them and their dependencies.

The load command is fetches code from a *.rb file.

require is the better one to us normally. It can also load binaries, and it prevents multiple imports of the same feature.

TODO IO

STDIN.gets
puts / print
output to stdout, puts ensures the string ends with a newline
STOUT.flush
make sure everything gets written out
ARGV
array of command line arguments
open
open a file. The file will need closing when we're done, which is usually best done inside an ensure block.

TODO: Need to play with this a bit more.

Exceptions

Throw exceptions using raise (or fail, which is an alias for raise):

raise 'Oh dear! Abandon ship!'

Ruby has its own special naming scheme for handling exceptions:

begin
try
rescue[exception_type]
catch/except — may be used multiple times
ensure
finally
end
closing brace

There's a built-in global variable — $! — which holds the last exception.

Conventions

It's a convention to name methods which change state with an ! and those which are a boolean test with ?. Often both destructive and non-destructive versions of methods will exist.

Variables names are usually written with underscores between words.