cv.cm

Coding · Programming languages

Ruby tutorial for beginners

6 min read

Short answer

Ruby is a dynamic, object-oriented scripting language designed to be pleasant to read and write. Save puts "Hello, world" in hello.rb and run ruby hello.rb. Everything in Ruby is an object, even numbers and nil. The core ideas are methods defined with def, arrays and hashes, blocks passed to iterators such as each and map, classes and modules, and exceptions handled with begin/rescue.

Open Regex tester

What Ruby is and what it is used for

Ruby was created by Yukihiro Matsumoto ("Matz") in Japan and first released in 1995, with the goal of making programmers happy. It became widely known through Ruby on Rails, the web framework behind sites such as GitHub and Shopify. Ruby is used for web applications, scripting and automation, and developer tools such as Homebrew and CocoaPods. Its standard implementation is CRuby (also called MRI).

Install Ruby and run Hello, world

macOS and many Linux systems include a Ruby, but it is often old; install a current Ruby 3 with a version manager such as rbenv, asdf or mise, or with RubyInstaller on Windows. Check it with ruby -v.

puts "Hello, world"
ruby hello.rb
# Hello, world
irb          # interactive Ruby shell

puts prints a value followed by a newline. Parentheses around method arguments are optional when the meaning is clear.

Variables and basic types

Variables need no declaration and hold any object. By convention names use snake_case. Constants start with a capital letter.

ClassExampleNotes
Integer42, 2**100Any size
Float3.14Double precision
String"hi #{name}", 'hi'Mutable; double quotes interpolate
Symbol:nameImmutable identifier, common as hash keys
true, falsetrueOnly false and nil are falsy
NilClassnilNo value
Array, Hash[1, 2], {a: 1}Built-in collections
name = "Ada"
count = 7
puts "#{name} has #{count} items"   # Ada has 7 items
puts count / 2, count / 2.0         # 3, then 3.5
puts "12".to_i + 1                  # 13
puts 5.class, nil.to_a.inspect      # Integer, then []
puts 0 ? "0 is truthy" : "falsy"    # 0 is truthy

Control flow

score = 72
if score >= 90
  puts "A"
elsif score >= 70
  puts "B"          # B
else
  puts "C"
end

puts "pass" unless score < 50    # statement modifier

3.times { |i| print i, " " }     # 0 1 2
puts

n = 10
n /= 2 while n > 1
puts n                           # 1

kind = case "SAT"
       when "SAT", "SUN" then "weekend"
       else "weekday"
       end
puts kind                        # weekend

Blocks end with end rather than braces. Ruby code is usually indented with 2 spaces.

Methods

Methods return the value of their last expression, so return is often unnecessary. By convention, methods that return a boolean end in ?, and methods that change the receiver in place end in !.

def greet(name, greeting: "Hello")
  "#{greeting}, #{name}!"
end

def total(*nums)
  nums.sum
end

puts greet("Lin")                   # Hello, Lin!
puts greet("Lin", greeting: "Hi")   # Hi, Lin!
puts total(1, 2, 3)                 # 6
puts 4.even?, "abc".upcase          # true, then ABC

s = "hello"
s.upcase!          # modifies s in place
puts s             # HELLO

Arrays and hashes

nums = [3, 1, 2]
nums << 5
p nums.sort, nums.first, nums.last   # [1, 2, 3, 5] then 3 then 5
p nums.sort[1..2]                    # [2, 3]

ages = { "ann" => 31, "bob" => 25 }
ages["cy"] = 40
ages.each { |person, age| puts "#{person} is #{age}" }
p ages.fetch("zed", 0)               # 0

user = { name: "Ann", city: "Oslo" } # symbol keys
p user[:name]                        # "Ann"

p prints the inspect form of a value, which is handy for debugging because it shows quotes and brackets.

Signature feature: blocks and iterators

A block is a chunk of code passed to a method, written with { ... } on one line or do ... end across lines. Iteration in Ruby is done mostly through blocks instead of for loops, and the Enumerable methods make data transformations short.

nums = [1, 2, 3, 4, 5]
p nums.map { |n| n * n }               # [1, 4, 9, 16, 25]
p nums.select(&:even?)                 # [2, 4]
p nums.reduce(0) { |sum, n| sum + n }  # 15
p nums.each_slice(2).to_a              # [[1, 2], [3, 4], [5]]
p %w[pear fig apple].sort_by(&:length) # ["fig", "pear", "apple"]

def twice
  yield
  yield
end
twice { puts "hi" }   # prints hi two times

&:even? is shorthand for { |n| n.even? }. Your own methods receive a block and call it with yield.

Classes and modules

module Greeter
  def hello
    "Hello from #{name}"
  end
end

class Account
  include Greeter          # mix in module methods
  attr_reader :name, :balance

  def initialize(name)
    @name = name           # @ marks an instance variable
    @balance = 0
  end

  def deposit(amount)
    raise ArgumentError, "amount must be positive" unless amount.positive?
    @balance += amount
    self
  end

  def to_s = "#{name}: #{balance}"
end

acc = Account.new("Ann")
acc.deposit(30).deposit(20)
puts acc        # Ann: 50
puts acc.hello  # Hello from Ann

attr_reader generates getter methods. Modules group methods that can be mixed into many classes, which Ruby uses instead of multiple inheritance. The one-line def to_s = ... form needs Ruby 3.0 or later.

Error handling

def parse_age(text)
  age = Integer(text)       # raises ArgumentError on bad input
  raise ArgumentError, "negative age" if age.negative?
  age
end

begin
  puts parse_age("41")      # 41
  puts parse_age("abc")
rescue ArgumentError => e
  puts "error: #{e.message}"  # error: invalid value for Integer(): "abc"
ensure
  puts "done"
end

rescue catches exceptions, and ensure always runs. Inside a method you can write rescue directly without begin.

A small complete program: word frequency

path = ARGV.fetch(0) { abort "usage: ruby words.rb FILE" }

begin
  text = File.read(path).downcase
rescue Errno::ENOENT
  abort "file not found: #{path}"
end

counts = text.scan(/[a-z']+/).tally
counts.max_by(5) { |_word, n| n }.each do |word, n|
  puts format("%5d  %s", n, word)
end

Run it with ruby words.rb notes.txt. tally counts equal items into a hash, and max_by(5) returns the five entries with the highest counts.

Common beginner mistakes

  • Forgetting end to close if, def, class or do blocks.
  • Mixing string keys and symbol keys: h["name"] and h[:name] are different keys.
  • Using single quotes and expecting #{} interpolation to work.
  • Calling a ! method and not realizing it changed the original object.
  • Using the old system Ruby instead of a current version installed with a version manager.

Where to go next

Start with the documentation page on ruby-lang.org, including the quick start "Ruby in Twenty Minutes", and use docs.ruby-lang.org for the core and standard library reference. Learn Bundler and gems for dependencies, RSpec or Minitest for tests, and then Ruby on Rails if you want to build web applications.

FAQ

Is Ruby still used?

Yes. Ruby on Rails runs many established web products, including GitHub and Shopify, and Ruby is common in scripting and developer tools. It is less common than Python or JavaScript for new projects, but it remains actively developed.

What is the difference between Ruby and Ruby on Rails?

Ruby is the programming language. Rails is a web framework written in Ruby that provides routing, database access and page templates.

What is a symbol in Ruby?

A symbol such as :name is an immutable, unique identifier. The same symbol is always the same object, which makes it efficient for hash keys and option names.

Ruby or Python for beginners?

Both are good first languages with readable syntax. Python has a wider range of uses, especially in data science; Ruby is very expressive and is strong for web development with Rails.

All coding tutorials