Ruby Code Examples: 20 Practical Snippets for Beginners

Learn Ruby through 20 practical code examples covering strings, arrays, hashes, methods, classes, files, JSON, regex, and more.

Written by James Britt
Technically reviewed by Jim Freeze · Reviewed 3 September 2026

If you learn best by taking working code apart, this collection of Ruby code examples is designed for you. It starts with small expressions, then moves through collections, methods, classes, files, JSON, regular expressions, and a complete mini-project. Every snippet is self-contained. Copy one into a file such as example.rb , then run it from a terminal with ruby example.rb . Change the values and run it again: experimentation is usually the quickest way to make Ruby’s concise syntax feel natural.

Ruby Code Examples at a Glance

1. Print Text and Store a Value

puts prints a value followed by a newline. Ruby variables do not need a type declaration; the object assigned to the name determines what operations are available. The #{…} sections are string interpolation. Ruby evaluates each expression and inserts its result into a double-quoted string. Single-quoted strings do not interpolate values.

language = "Ruby"
year = 1995

puts "Learning #{language}"
puts "Ruby first appeared in #{year}."

2. Work With Numbers

In addition to being able to use common mathematical operations in your code, Ruby also has some rules that govern how they work. When dividing one number by another, if both numbers are integers then the result is an integer. So, you will need to include a decimal value for a result with decimals. The underscore character may be used to make large values easier to read: for example, 1_000_000 and 1000000 have the same value. Try this using whole-number values and decimal values to see how each operand’s data type determines the result of division.

subtotal = 24.50
quantity = 3
discount = 5

total = (subtotal * quantity) - discount
average = total / quantity

puts total.round(2)   # 68.5
puts average.round(2) # 22.83

3. Transform a String

Strings have standard methods for normal cleanup and formatting. This example has strip remove any leading or trailing white space, split create a list of individual words, map apply capitalize to each word, and join reconstruct the last string. When something doesn’t behave as expected with a chain, either print its value during execution or inspect the intermediate results. It is common, when things don’t seem to be working correctly, to look at what was produced by split before proceeding.

raw_title = "  ruby code examples  "

clean_title = raw_title.strip.split.map(&:capitalize).join(" ")

puts clean_title # Ruby Code Examples

4. Add, Remove, and Read Array Items

An array is an ordered collection. Indexes begin at zero, while a negative index counts backwards from the end. The shovel operator, << , appends an item to the array. Methods such as first , last , include? , and length make routine checks readable. Test the array with duplicate names and with no items at all. Notice that delete changes the existing array, while reader methods simply return information about its current contents.

frameworks = ["Rails", "Sinatra"]

frameworks << "Hanami"
frameworks.delete("Sinatra")

puts frameworks.first # Rails
puts frameworks[-1]   # Hanami
puts frameworks.length # 2

5. Store Named Values in a Hash

Hashes hold values by key. The key can be one of several types of object in Ruby; symbols are simply a common type used as names within the code. Using square brackets retrieves the value for an existing key or assigns a new value. When iterating, a two-parameter block receives the current key and its associated value. Compare how a lookup of a non-existent key returns nil with how fetch can return a default you provide. Depending on what should happen when an entry is missing, either behaviour may serve well.

book = {
  title: "The Ruby Way",
  pages: 816,
  available: true
}

puts book[:title]
book[:pages] = 820
book[:format] = "paperback"

book.each do |key, value|
  puts "#{key}: #{value}"
end

6. Generate Values From a Range

Two dots are used to define an inclusive range. Three dots define a range where the final value is excluded. Ranges can represent sequences, select slices, or test whether a value falls between two endpoints. When defining one, it is easy to make an off-by-one error by choosing the wrong form for the problem being solved.

inclusive = (1..5).to_a
exclusive = (1...5).to_a

puts inclusive.inspect # [1, 2, 3, 4, 5]
puts exclusive.inspect # [1, 2, 3, 4]
puts (1..10).include?(7) # true

7. Make a Decision With if, elsif, and else

Ruby treats only false and nil as falsey. Everything else, including 0 and an empty string, is truthy. A short condition can also follow the action: puts “Take a coat” if temperature < 10 . Run the condition with values on either side of every boundary. Testing the exact cutoff as well as nearby values is a simple habit that catches faulty comparison operators early.

temperature = 18

if temperature >= 25
  puts "Warm"
elsif temperature >= 15
  puts "Mild"
else
  puts "Cool"
end

8. Match Several Possibilities With case

A case expression generally reads better than many equality checks chained in succession; it also allows for direct assignment of the result. Because Ruby employs case-equality behaviour rather than simply comparing values on a branch-by-branch basis, range branches can keep related status codes together while still allowing an individual branch when you need a unique message for one status.

status_code = 404

message = case status_code
          when 200..299 then "Success"
          when 400      then "Bad request"
          when 404      then "Not found"
          when 500..599 then "Server error"
          else "Unknown status"
          end

puts message

9. Loop With each and each_with_index

Ruby programs usually iterate over a collection instead of maintaining a counter manually. The block between do and end runs once per item. each_with_index supplies both the item and its zero-based position. Remove the index adjustment and compare the output. The collection position begins at zero, but numbered text intended for readers will usually begin at one.

topics = ["strings", "arrays", "hashes"]

topics.each_with_index do |topic, index|
  puts "#{index + 1}. #{topic.capitalize}"
end

10. Build a New Array With map

Use map when all input items will be used to create one output item. Map creates an entirely new collection. As you see in the example, prices_with_vat is a variable and not a method. After assigning prices_with_vat, the old prices remain unchanged in the original array. This is because the returned list of numbers from map was created as a new array through a separate assignment.

prices = [8.00, 12.50, 20.00]

prices_with_vat = prices.map do |price|
  (price * 1.20).round(2)
end

p prices_with_vat # [9.6, 15.0, 24.0]

11. Filter Items With select and reject

select keeps items for which the block is truthy. reject does the opposite. Replace even? with another predicate and watch which values survive. Thinking of select as ‘keep matching items’ and reject as ‘discard matching items’ makes the pair easier to remember. Work slowly enough to connect the output with the specific expression that produced it.

numbers = [3, 8, 11, 14, 19, 22]

even_numbers = numbers.select(&:even?)
small_numbers = numbers.reject { |number| number >= 10 }

p even_numbers  # [8, 14, 22]
p small_numbers # [3, 8]

12. Combine Values With reduce

reduce , also known as inject , carries an accumulated value through the collection. It is handy for totals and other single-result calculations. Change the starting accumulator and follow the sum after each iteration. The initial value matters because it becomes the first left-hand input supplied to the block. Work slowly enough to connect the output with the specific expression that produced it.

basket = { keyboard: 45.00, mouse: 20.00, cable: 6.50 }

total = basket.values.reduce(0) do |sum, price|
  sum + price
end

puts format("£%.2f", total) # £71.50

13. Define a Method With Keyword Arguments

Methods package behaviour behind a useful name. Keyword arguments make calls easier to understand and allow defaults for optional settings. Ruby returns the last evaluated expression automatically, so an explicit return is unnecessary here. Call the method with keyword arguments in a different order. Their names, rather than their positions, identify the values, which makes a call with several options much easier to read.

def greeting(name:, formal: false)
  formal ? "Good morning, #{name}." : "Hi, #{name}!"
end

puts greeting(name: "Mina")
puts greeting(name: "Mina", formal: true)

14. Handle a Missing Value Safely

The safe-navigation operator, &. , calls a method when the receiver is not nil . If it is nil , the expression returns nil instead of raising NoMethodError . Use safe navigation deliberately. If a missing object indicates a programming error, allowing an exception may be more useful than silently substituting a fallback.

user = { profile: nil }

name = user[:profile]&.fetch(:name, nil) || "Guest"

puts name # Guest

15. Create a Class

A class groups state and behaviour. The initialize method runs when new creates an instance, and attr_reader generates getter methods. Names beginning with @ are instance variables. Each BankAccount object keeps its own values. Attempt a negative deposit and read the resulting exception. Validation inside the object protects its state, regardless of which part of the program calls the method.

class BankAccount
  attr_reader :owner, :balance

  def initialize(owner, opening_balance = 0)
    @owner = owner
    @balance = opening_balance
  end

  def deposit(amount)
    raise ArgumentError, "Amount must be positive" unless amount.positive?

    @balance += amount
  end
end

account = BankAccount.new("Ari", 100)
account.deposit(25)

puts account.balance # 125

16. Share Behaviour With a Module

A module can act as a namespace or provide reusable behaviour. Including it in a class adds its instance methods to objects of that class. Give the article name punctuation and repeated spaces. The module keeps slug-building behaviour in one place, ready for another class that exposes the same name method.

module Sluggable
  def slug
    name.downcase.strip.gsub(/[^a-z0-9]+/, "-").gsub(/^-|-$/, "")
  end
end

class Article
  include Sluggable
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

article = Article.new("20 Useful Ruby Examples")
puts article.slug # 20-useful-ruby-examples

17. Rescue an Expected Exception

Rescue errors you know how to handle. Avoid catching every possible exception without a clear recovery path, because that can hide genuine bugs. Integer is preferable to to_i when invalid input must be detected: “hello”.to_i quietly returns zero, while Integer(“hello”) raises ArgumentError . Compare a valid zero with a word that cannot be converted. Returning nil for the latter lets the caller distinguish bad input from the perfectly legitimate number zero.

def integer_from(text)
  Integer(text, 10)
rescue ArgumentError
  nil
end

value = integer_from("42")
invalid = integer_from("forty-two")

p value   # 42
p invalid # nil

18. Write and Read a Text File

File.write replaces a file’s contents, while File.read returns the entire file as a string. For large files, process lines incrementally instead. chomp removes the record separator at the end of each line. File.foreach is memory-friendly because it does not load the whole file at once. Use a disposable file while experimenting, and inspect it between runs. File operations affect the filesystem outside the Ruby process, so cleanup and path choice deserve deliberate attention.

path = "notes.txt"

File.write(path, "Arrays\nHashes\nMethods\n")

File.foreach(path).with_index(1) do |line, number|
  puts "#{number}: #{line.chomp}"
end

19. Parse and Generate JSON

Ruby’s standard JSON library converts JSON objects to hashes and JSON arrays to Ruby arrays. Require it before use. Never treat JSON from an external source as trusted simply because it parsed successfully. Validate the fields and types your program expects. Remove symbolize_names and inspect the keys returned by the parser. JSON object names are strings by default, which changes how the resulting hash must be accessed.

require "json"

json = '{"name":"Ruby","dynamic":true}'
data = JSON.parse(json, symbolize_names: true)

puts data[:name]

output = JSON.generate(language: data[:name], topics: ["web", "scripts"])
puts output

20. Extract Data With a Regular Expression

A regular expression is useful when the input has a predictable textual pattern. Named captures make the extracted pieces self-explanatory. For structured formats such as JSON or CSV, use a parser rather than a regular expression. Regex is best reserved for genuinely textual patterns. Try a line that does not match and confirm that the conditional body is skipped. Real input often contains partial or malformed records, so a non-match should be an expected case.

log_line = "2026-09-02 level=ERROR request_id=abc123"
pattern = /level=(?<level>\w+) request_id=(?<request_id>\w+)/

if match = log_line.match(pattern)
  puts match[:level]
  puts match[:request_id]
end

Mini-Project: Count Word Frequency

This final example combines strings, arrays, hashes, iteration, sorting, and formatted output. It counts words without caring about capitalisation or basic punctuation. Hash.new(0) gives an unseen word an initial count of zero. The sort key uses the negative count to put frequent words first, then sorts words alphabetically when their counts match.

text = <<~TEXT
  Ruby is expressive. Ruby is readable.
  Readable code is easier to maintain.
TEXT

words = text.downcase.scan(/[a-z]+/)
counts = Hash.new(0)

words.each do |word|
  counts[word] += 1
end

counts
  .sort_by { |word, count| [-count, word] }
  .each { |word, count| puts format("%-10s %d", word, count) }

How to Practise These Ruby Code Examples

The best next step is to combine two or three snippets into something personally useful: a file renamer, expense total, log analyser, or command-line checklist. A small program you actually want tends to teach more than a large example copied without modification. Use the examples as starting points rather than finished answers. Predict one change, run it, and explain the result in your own words before moving on to a larger exercise.

  1. Run each example unchanged so you know its starting behaviour.
  2. Predict what one small edit will do before running the program again.
  3. Turn fixed values into method arguments or terminal input.
  4. Add an edge case, such as an empty array, missing key, or invalid number.
  5. Read the exception rather than immediately deleting the line that caused it.

Official Ruby References

For deeper detail, use the official documentation for Ruby literals, methods, Enumerable, File, and JSON. The documentation describes the full API; the Ruby code examples above show how the pieces fit into everyday programs.