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
andpublic :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.