Written by James Britt
This Ruby programming language overview follows one small program from input to output. You will meet values, arrays, hashes, methods, blocks and conditions, then change the input to see what breaks. Ruby is a general-purpose, object-oriented language with dynamic typing: objects have types, while ordinary variables do not require declared types.
You only need a Ruby installation and a text editor to run the example. No Rails application, database or downloaded gem is involved. If you are reading without a terminal, predict the results before opening the exercises further down the page.

Ruby programming language overview: the main pieces
A value represents something the program can work with. A string holds text, an integer represents a whole number, and an array keeps an ordered collection of values. A hash associates keys with values. Methods describe operations, while blocks pass a piece of behaviour to another method.
These categories become easier to recognise in a real example. A book order can use a string for the title and an integer for the quantity. A hash can hold both under named keys, and an array can hold several book-order records in sequence.
Dynamic typing does not eliminate types. The string "2" and the integer 2 are different objects with different behaviour. When a program expects a quantity, it must decide what to do with incoming text: convert it, reject it or report it for correction.
A complete program to read and run
Save this code in order_summary.rb, then run ruby order_summary.rb. The output and nine behaviour checks passed with Ruby 3.2.3. Keep the spelling of the symbol keys consistent: :title is not the same key as the string "title".
def order_summary(items)
items.map do |item|
title = item.fetch(:title)
quantity = item.fetch(:quantity)
unless title.is_a?(String) && quantity.is_a?(Integer) && quantity >= 0
raise ArgumentError, "title must be text and quantity a non-negative integer"
end
label = quantity == 1 ? "copy" : "copies"
"#{title}: #{quantity} #{label}"
end
end
items = [
{ title: "Ruby notes", quantity: 2 },
{ title: "Practice book", quantity: 1 }
]
puts order_summary(items)
The output is Ruby notes: 2 copies followed by Practice book: 1 copy on the next line. The method creates an array of summary strings. Finally, puts prints each string on its own line.
Try changing the second quantity to 0. The record remains valid and its summary says 0 copies. The rule here allows zero; another application might require at least one. Validation is a choice about the data your program accepts.
Methods: inputs, results and names
The def keyword starts a method definition and end closes it. In this example, items is a parameter: a local name for the argument supplied by the caller. The call at the bottom provides the actual array.
Ruby’s method syntax reference explains that a method normally returns the value of its last evaluated expression. Here that expression is the call to map. You do not need an explicit return at the bottom to return its array.
Keep a method’s returned value separate from what it prints. This method returns data, which makes it useful outside a terminal too. The final call to puts handles display. You could later write the returned strings to a file without changing the formatting method.
A good name helps the next reader find a responsibility. order_summary tells you more than process. Shortening code by choosing names that hide the work can make it harder to maintain.
Arrays, hashes and blocks
The outer square brackets create an array. Each pair of curly braces creates a hash containing a title and a quantity. The fetch calls request required keys; if a key is missing, Ruby raises KeyError instead of quietly supplying a default value.
The block begins with do |item|. For each element, map runs the block and collects its result in a new array. The last line inside the block builds a string with interpolation: expressions inside #{...} contribute their values.
The example reads the input records without changing them. That does not mean every call to map is free of side effects. A block can still modify objects if you put a modifying operation inside it. Our Ruby functional programming guide explores this distinction with additional examples.
Conditions and truthiness
The validation condition combines three checks with &&: a string title, an integer quantity and a non-negative value. The unless branch raises an exception when that combined condition is false. Since the checks run from left to right and short-circuit, a non-integer quantity does not reach the numeric comparison.
The next condition chooses between copy and copies. The compact conditional operator uses the form condition ? value_if_true : value_if_false. A longer decision usually reads better as an ordinary if expression.
Ruby’s control-expression documentation makes an important rule explicit: only false and nil are falsey. Both zero and an empty string are truthy. Therefore, checking whether a quantity exists is different from checking whether it is positive.
Predict the result, then reveal it
Change one thing at a time. Before running the program, predict whether it will produce normal output, an empty result or an exception.
What happens with an empty items array?
The method returns an empty array because there are no elements for the block to transform. The final puts call prints no summary lines. An empty input is valid for this method.
What happens if quantity becomes the string “2”?
The integer check fails, so the method raises ArgumentError. It does not automatically treat numeric-looking text as a valid quantity. Decide on an explicit conversion policy when reading outside input.
What happens if the quantity key is missing?
The fetch call raises KeyError before the later validation condition runs. Missing data and data of the wrong type are different failures, even if the interface eventually explains both as an invalid record.
Complete your Ruby programming language overview: errors
The example deliberately stops on invalid input. In a larger application, the calling code could catch an expected error and show a useful message. Avoid rescuing every possible failure just to print success; that can hide programming mistakes as well as bad input.
For practice, add another valid record, remove a required key and supply a negative quantity. Check each case independently. Our Ruby code examples give you more material once you can explain the path through this program.
You do not need to memorise the entire language before writing something useful. Start by identifying the input, the transformation and the result. Then make the rules for missing or unexpected data visible in the code.
