This guide will focus on Ruby - the language - alone.
After finishing this guide you will
yield
in Ruby.Fork the learn_ruby repository to try some test driven ruby learning
Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen
Ruby is an open source project. It was started in 1996 by Yukihiro 'Matz' Matsumoto. He is still the "benevolent dictator" who decides on the future of the language.
In a colossal break with tradition he did not choose a name starting with p for his scripting language (think perl, python, php) but opted for r instead.
Ruby is a thoroughly object oriented scripting language. Even basic data types are object:
1.to_s
=> "1"
In this example the number 1 is used as an object, the method to_s
is
called on it. The result is an Object of class String.
Ruby on Rails is a web framework written in Ruby. It was created by David Heinemeier Hansson ('DHH') starting in 2005.
Rails is famous for the high productivity it gives to developers. It is often used in startups, where speed of delivery is very important. Rails moves fast, new versions with major improvement appear about every 18 months. The Rails community values speed of development, DRY code, testing, version control, ... when you learn Rails you also pick up a whole culture surrounding it.
Some sites built with Rails are: GitHub and GitLab, Shopify, Fiverr, codepen.io, Airbnb, Twitch, Square, Dribble and of course Basecamp, DHHs own product.
Why should you use Ruby and Rails over other programming languages and frameworks?
Because of the cool t-shirts?
StackOverflows Survey from 2024 shows there are fewer Ruby developers, but they are better payed:
To get to know Ruby you don't need to write
whole programes. You can start out with interactive ruby - irb.
When you start irb you get a command line to type in Ruby code. When
you press enter the code is evaluated immediately and you get the result.
Use the commands exit
or quit
or the key combination CONTROL-D to get out.
In this guide we will show code run in irb by marking the prompt as >>
and
the result as =>
, for example:
>> 2 + 2
=> 4
Try to stick to github's style for ruby.
When you choose names for your objects, classes and methods you should stick to the following conventions to avoid confusing other Ruby developers:
the_variable = SomeClass.new
# variables are written in snake_case
# classes in capital CamelCase
# method names are written in snake_case
a = b.sugar # a method that returns something
b.sweet? # a method that returns true or false
# ends in a question mark
b.sugar! # a method that changes its object
# ends in an exclamation mark
In the last two examples the punctuation marks are really part of the method names!
When calling a method, the parantheses around the arguments are optional. Leave them off unless your code get's confusing:
puts("less code")
puts "less code"
def f(a,b)
puts "Hello World"
puts "I'm just ignoring my arguments for now"
end
Methods in Ruby return the last expression - even
if no explicit return
statement is given.
def f(a,b)
"x"
end
f(1,42) # returns "x"
Since ruby 2.0 keyword arguments can be used, and can be given default values:
def apply_the_style(font_family: 'MS Comic Sans', font_size: 16 )
...
end
apply_the_style() # uses both default values
apply_the_style(font_family: "Arial")
apply_the_style(font_size: 10, font_family: "Arial") # order can be different
Everything is an Object, even Integers and Strings. They have methods and properties, like other objects:
>> nil.class
=> NilClass
>> 2.class
=> Fixnum
>> "some text".length
=> 9
Imagine you have an Object a
, that has a property b
containing
another object, and b
has a property c
. You can access c
through a.b.c
But if a
is nil
then you will get an error:
> a.b.c
NoMethodError: undefined method `b' for nil:NilClass
The Operator &.
avoids this error. It is called "save navigation operator" or sometimes "lonely operator".
> a&.b&.c
nil
All of Ruby's basic data types are Classes.
# NilClass
# are converted automatically to each other
# TrueClass
# FalseClass
Ruby is strict about data types, there is no automatic conversion except between numeric types.
>> "a string" + "another"
=> "a stringanother"
>> "a string" + 2
TypeError: no implicit conversion of Fixnum into String
>> 42 + 3.141
=> 45.141
>> ":-)" * 4
=> ":-):-):-):-)"
s = 'just a string of characters'
s = "string with #{the_variable} embedded"
s = "string with #{a+b/c} a ruby expression embedded"
s = <<EOM
This is a so called "Here-Document"
it can contain many lines of text
and ends with the identifier EOM (that i chose!)
but only if it's alone on a line all by itself:
EOM
s = %Q|with %Q you can chose any character als string delimiters|
s = %Q{
with %Q you can chose any character als string delimiters.
opening brackets go with closing brackets.
}
s = 'aBC'
puts s.upcase # ABC
puts s.downcase # abc
puts s.capitalize # Abc
puts s[0] # a (index of character)
puts s[0,2] # aB (start + length)
In Ruby only false
and nil
are treated as false. This might
be confusing for programmers used to other languages with
more complex rules for truthyness:
if 0
puts '0 is true!'
end
if "false"
puts '"false" (the string) is true'
end
A Symbol looks - at first glance - similar to a string: you can invent it at any time (no 'declaration') and give it any name:
a = :foo
a = :bar
s = "foo"
s = "bar"
But: there is always just one instance of a symbol while there can be several strings that have the same content, but are different objects:
>> :foo.object_id
=> 635528
>> :foo.object_id
=> 635528
>> "foo".object_id
=> 70099463087600
>> "foo".object_id
=> 70099463106400
Use symbols where you would enums in a database or another language, or if you need distinct constants, when the value is not important.
There are several ways of writing literal arrays in Ruby. The first one looks like JSON:
>> a = ["this", "that", "something"]
=> ["this", "that", "something"]
For creating an array of words (strings without whitespace in them)
you can use %w
:
>> a = %w(this that something)
=> ["this", "that", "something"]
When creating an array of consecutive numbers you can use a Range and convert it to an Array:
>> (1..4).to_a
=> [1, 2, 3, 4]
A Hash is a datastructure similar to an array. An array uses integers as keys while a Hash allows any type as the keys. Mostly strings and symbols are used:
h = Hash.new
h["alice"] = "beer"
h["chris"] = "tea"
h["bob"] = "mate"
But you can use other objects:
t = Date.new
h[t] = "recently"
The data structure behind a Ruby Hash is more complex than an array: The key is sent through a function (called hash function) that returns a number. This number is used as the index for an array. If the result for two keys is the same, a linked list is built.
This datastructure seems like a serious waste of memory at first. But it offers the following interesting features:
Most scripting languages offer Hashes as a basic data type, most compiled languages as a library. Read more about Hashes in Wikipedia:
(If you don't know what "in constant time" means above, you should learn more about the analysis of algorithms. e.g. by taking an algorithms and data structure course as offered in the second semester of most computer science programs.)
A Hash can be created with Hash.new, or by writing it as a literal:
roomnumber = { "Jane Doe" => 10, "Jim Doe" => 6 }
When the keys are symbols you can use an alternative syntax that looks like json
style = { :font_size => 10, :font_family => "Arial" }
style = { font_size: 10, font_family: "Arial" }
Before keyword arguments were added to ruby, often a hash was used as the single argument for a method. Calling the method then reads like named arguments:
def apply_the_style( h )
...
end
apply_the_style(font_size: 10, font_family: "Arial")
apply_the_style font_size: 10, font_family: "Arial"
Most objects in Ruby are mutabel by default.
# mutable
>> configuration = { color: 'yellow', number: 10 }
=> {:color=>"yellow", :number=>10}
>> configuration[:smoking] = false # no problem, can add new key
=> false
>> configuration[:number] = 2 # no problem, change value
=> 2
>> configuration
=> {:color=>"yellow", :number=>2, :smoking=>false}
You can freeze an object to make it immutable.
Notice that this is different from using a const
in JavaScript: changing the object in any way
will throw a runtime error.
# frozen
>> configuration = { color: 'yellow', number: 10 }.freeze
=> {:color=>"yellow", :number=>10}
>> configuration[:smoking] = false
=> RuntimeError: can't modify frozen Hash
>> configuration[:number] = 2
=>RuntimeError: can't modify frozen Hash
You cannot modify a frozen object, but you can copy it with .dup
Since Ruby 2.3 all String literals can be frozen by adding this line to to top of a ruby file:
# frozen_string_literal: true
This is switched on in Rails since 5.2.
The Basic condition with if
works like in most programming languages.
if i > 10
puts 'cannot compute, not enough fingers'
elsif i <= 0
puts 'cannot compute, negative number'
else
puts 'input correct'
end
The case
expression can match one value in several ways.
case x
when 1..5
puts "It's between 1 and 5"
when 6
puts "It's 6"
when "foo", "bar"
puts "It's either foo or bar"
when String
puts "You passed a string"
else
puts "You gave me #{x} -- I have no idea what to do with that."
end
If the if
only has one statement
you can write it in a shorthand version:
if errors > 0
puts "Some errors occured"
end
puts "Some errors occured" if errors > 0
This syntax should be familiar to you if you understand English. (yes, that's an English sentence using the same syntax).
When Ruby evaluates a boolean operator, it does as little work as possible. It stops evaluation as soon as the result is clear:
# the second argument is not evaluated!
a = true || ...
a = false && ...
The boolean operators don't just return true or false, they return the argument last evaluated. This is often used to set a variable:
default_value = "gray"
input_value = nil
# here input_value might be set...
a = input_value || default_value
When working with a list of values Ruby helps you think about data on a new, more abstract level with Enumerables:
From the UNIX shell you may know the concept of piping data from one command to the next:
# I know mysql server is running, but which user is it running as ?
#
$ ps aux | grep mysqld_safe | head -1 | cut -c1-8
This is a sequence of Unix programs connected by the pipe symbol (|). Each program in this chain reads data from its "Standard Input" and writes data to its "Standard Output." The pipe symbol serves as a conduit, channeling the output from one program as input to the subsequent program in the sequence. The data being transferred is plain text, often spanning multiple lines.
Try it out on your commmand line by building up the pipe step by step:
$ ps aux | less
$ ps aux | grep mysqld_safe | less
$ ps aux | grep mysqld_safe | head -1 || less
$ ps aux | grep mysqld_safe | head -1 | cut -c1-8 | less
When piping data in ruby you can start with an Array
languages = %w[Fortran Ada C C++ Java Scala Haskell]
languages.sort.first(3)
The elements of the array are piped into the sort-method, which again outputs a list of elements. These are piped into first, which only returns the first three and discards the rest. The result is a list of 3 elements.
Here are some simple methods you can use on Arrays (and other Enumerables) that return a new Enumerable. You can connect theses methods to each other:
The method tally
counts the occourance of elements and returns a hash:
["a", "b", "c", "b"].tally
#=> {"a"=>1, "b"=>2, "c"=>1}
Some other methods return just a single value, and thus end the pipe:
More advanced methods take a Block (of code) as their argument.
The method map
applys the Block to each piece of data, and
returns an Enumerable of the new data:
>> (1..10).map{ |x| 2*x }
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>> (1..10).map{ |x| 2*x }.reverse
=> [20, 18, 16, 14, 12, 10, 8, 6, 4, 2]
If the computation is more complex you can write
the Block on several lines, ending with end
>> (1..10).map do |x|
?> x*2
>> end
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>> (1..10).map do |x|
?> x*2
>> end.reverse
=> [20, 18, 16, 14, 12, 10, 8, 6, 4, 2]
Some other methods for Enumerables that take a Block:
map {|x|
new value computed from x }
map.with_index {|x, i|
new value computed from x and i, the position in the array }
select {|x|
should x be selected? }
reduce(:+)
reduce{|memo, item|
compute new value for memo, using current item }
These methods should help you avoid loops and thus simplify code considerably.
Read the Reference on
Blocks of code are not just used in Enumerables, they are a basic building block of ruby. You can write functions that take a Block as an argument:
Any function you write can take an additional block
of code as its last argument. The block is only
called if and when you call yield
inside the function:
def my_function_with_block_arg
puts "code in the funtion"
yield
puts "more code in the function"
end
my_function_with_block_arg { puts "code in the block" }
# OUTPUT:
# code in the funtion
# code in the block
# more code in the function
There is an alternate syntax for calling the function: instead
of the curly braces you can use do
and end
:
my_function_with_block_arg do
puts "code in the block"
puts "more code in the block"
end
You now know about the basic data types, about enumerables and about blocks - features that distinguish Ruby from other scripting languages.
If you want to get more practical with Ruby, you can do the Learn Ruby test driven Ruby exercises.
This should be a good enough basis to start with Rails. But do take every opportunity you get to learn more about Ruby itself: if you are unsure about a line of code, look it up in the Ruby documentation and use the opportunity to read a bit more than strictly necessary.