My name is Trevor, I’m a software engineer specialising in Ruby on Rails. I’m also a dad, geek and tabletop gamer.

Profile photo of Trevor Vallender


19 January 2022 | Updated on

This is my overview of Ruby, aiming to give a bird’s eye view of the language. My goal is to present information in such a way that it will prove as a useful introduction to Ruby for programmers coming from other languages, and as a reference for relatively new Ruby developers. It is not an introduction to programming, nor is it in any way exhaustive. It should be used as a place to jump off from and dive deeper through other sources whenever you reach an area you wish to know more about.


Setting variables

name = "T S Vallender"


There is no need to declare a variable’s type.


5 + 5 # 10
5 - 2 # 3
5 * 5 # 25
10 / 5 # 2
3 ** 3 # 27 (Exponent)
10 % 5 # 0 (Modulo)

Ruby uses standard mathematical order of operation (PEDMAS/BODMAS).

Numeric types

Floats are an imprecise data type due to the way they are stored and decimal should be used when precision is necessary. Rational is also available for dealing with rational numbers, but care must be taken not to treat an irrational number as rational.

0.2 + 0.1 == 0.3 # false due to float imprecision
BigDecimal("0.2") + BigDecimal("0.1") == 0.3 # true


Strings are defined between double or single quotes. Doubles allow for string interpolation, singles do not.

String interpolation
puts "I can insert #{my_var} into a string"

Any valid Ruby can be interpolated, often used to give an optional default value with a conditional.

String manipulation

Some common string manipulation methods:

name = "T S Vallender"
name.upcase # "T S VALLENDER"
name.downcase # "t s vallender"
name.reverse # "rednellaV S T"

poem = "And the Raven, never flitting, still is sitting, still is sitting"
poem.sub "sitting", "standing"
# "And the Raven, never flitting, still is standing, still is sitting
poem.gsub "sitting", "standing"
# "And the Raven, never flitting, still is standing, still is standing
# Note these do not change the original value - you need sub! and gsub! for that.

quote = "  Quoth the Raven “Nevermore.   "
quote.strip! "Quoth the Raven “Nevermore."
quote.split # ["Quoth", "the", "Raven", "Nevermore"]


Arrays are an Enumerable type.

arr = [3, 'Three', 8, 3, true]
arr.delete 3 # ['Three', 8, true]
arr.delete_at 0 # [8, true]

fellowship = ['Gimli', 'Pippin', 'Aragorn', 'Frodo', 'Pippin' ]
fellowship.delete_if { |x| x.length > 5 }
# ['Gimli', 'Aragorn']
fellowship.join(' and ') # "Gimli and Aragorn"

Items can be pushed to an popped from arrays with .push and .pop. .pop returns the value popped.

nil values are added to any empty spaces in an array.

Arrays of strings can also be declred using the below syntax

arr = %w(One Two Three Four)
Common methods
arr = ["a", "b", "c"]
arr.size # 3


Hashes are another Enumerable type. They are a key: value collection.

fellowship = { boromir: "Human", gimli: "Dwarf", legolas: "Elf",
               aragorn: "Human", frodo: "Hobbit" }
fellowship[:gimli] # "Dwarf"
fellowship.delete :boromir
fellowship.each_key { |x| puts x }
fellowship.each_value { |x| puts x }

fellowship[:gandalf] = 'Maiar' # add a new key-value pair.
fellowship.invert # flips keys and values

more = { pippin: "Hobbit", merry: "Hobbit", sam: "Hobbit" }
fellowship.merge more # combine hashes

fellowship.keys # array of keys
fellowship.values # array of values

This is the modern syntax. Ruby converts these internally to symbols. Older syntaxes include { "gimli" => "Dwarf" } and { :gimli => "Dwarf" }.


Local variables
Local variables are limited to the scope in which they are declared (e.g. a method or loop).
Global variables
Global variables are defined with a leading $. They are generally a bad idea.
Instance variables
Instance variables are defined with a leading @ and are available to a given instance of a given object.
Class variables
A class variables are defined with two leading @s and are available to all instances of a class. They are rarely used.
Constants are defined with an initial capital (generally in all caps. They *can be changed*, Ruby will just issue a warning if you do so.


Ruby supports the standard conditional operators ==, !=, <, >, <= and <=.

Compound conditionals are supported with && and ||. Priority can be enforced with parentheses. Ruby also gives and and or, which have lower precendence.

if x < y
  puts "#{x} is bigger"
elsif x > y
  puts "#{y} is bigger"
  puts "They are equal!"

# conditionals can also go on the end:

puts "They are equal" if x == y

arr = [1, 2, 3]
unless arr.empty?
  puts arr

puts arr unless arr.empty?

## Ruby also supports the ternary operator
x = y > z ? y : z # sets x to the larger of y or z

Iterators & Loops


while true
  p "Infinite loop"


The each method is available on any collection of items

[1, 2, 3].each do |x|
  p x # x is the element

[1, 2, 3].each { |x| p i } # equivalent to above

# If an index is required:

[1, 2, 3].each_with_index do | x, i |
  p "Index: ", i, "Value: ", x

With a hash, .each use:

my_hash.each do |key, value|
  p key, value 

For in

Rarely used, .each is more common.

for i in 0..10
  p i # prints 0 to 10


def my_method
  puts "This is my method"

Methods in Ruby return the value of their final statement. return is generally only used to return early.

Naming methods

Methods are generally defined in snake_case.

Methods with a trailing ! are “dangerous”, such as those in the core usually modifies its receiver.

Class vs. instance methods

Class methods are defined with a leading self.. Instance methods can only be called on an instance of a class.


def say_hello(name)
  puts "Hello #{name}!"

say_hello("Trevor") # "Hello Trevor!"

Parentheses are optional both when defining the function and calling it:

def say_hello name
  puts "Hello #{name}!"

say_hello "Trevor" # "Hello Trevor!"

Named arguments

def say_hello name:, place:
  puts "Hello #{name} in #{place}!"

say_hello name: "Trevor", place: "UK" # Hello Trevor in UK

Default arguments

def say_hello name:, place: "UK"
  puts "Hello #{name} in #{place}!"

say_hello name: "Trevor" # Hello Trevor in UK

Collections as arguments

A splat argument allows multiple arguments to be treated as an array.

def make_array *species

make_array "Dwarf", "Elf", "Halfling" # [ "Dwarf", "Elf", "Halfling"]

A keyword-based splat argument takes a hash value instead

def identify **characters
  characters.each do |species, name|
    puts "#{name} is a #{species}

c = { "Dwarf": "Gimli", "Elf": "Legolas" }
identify c
# Gimli is a Dwarf
# Legolas is a Elf

Optional arguments

Optional arguments use an empty options hash.

def stats options={}
  puts options[:optional]

stats optional: "HEY!"

Procs and Lambdas

Procs and lambdas are encapsulations of a piece of code in a variable.

Procs and lambdas are closures and thus remember they retain the context in which they were created.


my_proc = Proc.new { |x| x ** x }
my_equiv_proc = proc { |x| x ** x }
another_equiv_proc = Proc.new do |x|
  x ** x

my_proc.call(3) # 27
my_proc.(3) # 27
my_proc[3] # 27


my_lambda = lambda { |x| x ** x }
my_lambda[3] # 27

# "Stabby lambda" syntax:
my_equiv_lambda = ->(x) { x ** x}




Select returns those elements of a collection which meet a given criteria.

(1..20).to_a.select { |x| x.even? }
(1..20).to_a.select(&:even?) # equivalent


Map takes a collection and performs an operation on each one of its members.

arr = ["1", "2", "10"]
arr.map(&:to_i) # [1, 2, 3]

This can be used to create a hash of a value with an associated value, for example strings with their length:

arr = %w(Aragorn, Frodo, Gimli, Gandalf)
Hash[arr.map { |x| [x, x.length] }]


Inject and reduce are aliases. They combine a collection by the repeated application of a binary operator. For example, to sum all elements of an array:

[4, 3, 6, 2].reduce(&:+) # 15

If passed a code block, each element in the enumerable object is passed an accumulator and the element.

[fellowship = ['Gimli', 'Pippin', 'Aragorn', 'Frodo' ]

fellowship.inject do | longest, current |
  current.length &gt; longest.length ? current : longest

# Aragorn


A symbol is a number with an attached identifier, which is a series of characters or bytes. They are represented in Ruby with a leading colon.

Symbols should be used whenever a textual representation of a discrete set of options is required. If you are dealing with more freeform text, use strings.

Ruby will often convert between symbols and strings for the programmer’s convenience. If possible, you should supply the correct type initially, however, for the best performance.

Working at the console

Writing to the console

puts name # returns nil
p name # returns value

Note also that puts will pretty-print, while p will print arrays including commas, brackets etc., strings wrapped in quotation marks and is thus generally less suitable for use with end users.

Receiving input at the console

name = gets.chomp # chomp removes trailing newline

Object-oriented Ruby

Everything in Ruby is an object.

Classes are named with CamelCase.

class Humanoid
  attr_accessor :name, :address # creates getters and setters
  # initializer method run when new objects are created
  def initialize name:, address:
    @name = name
    @address = address

  def location
    puts "#{name} lives in #{address}"

  def self.fact # Class method
    puts "Humanoids have two legs"

class Hobbit &lt; Humanoid
  def initialize name:, address: "The Shire"
    super # Calls parent method
  def self.fact
    puts "Hobbits have hairy feet"

bilbo = Hobbit.new name: 'Bilbo'
p bilbo.name, bilbo.address
aragorn = Humanoid.new name: "Strider", address: "Gondor"

Methods below a private keyword are only accessible from the given class. Note there are ways around this but doing so is bad practice. Protected methods are also unavailable outside the class, but while within that class may be called on objects. They are uncommon and private should be used where possible.

The filesystem

f = File.new '/path', 'w+'
f.puts "contents"

File.open '/path', 'w+' do |f| # second option is mode 

File.write '/path', "contents"

content = File.read '/path' # reads as string

File.delete '/path'


  puts 10 / 0
rescue ZeroDivisionError => e
  puts e

Note you should deal with the error in the initial begin block, rescue should only be for dealing with the error object itself.