Anatomy of a Ruby file
Ruby is a dynamic, open-source programming language with a focus on simplicity and productivity, famous for the Ruby on Rails web framework.
File extensions: .rb
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Ruby file
Shebang
Specifies the interpreter path.
The shebang (#!) must be the very first line of the file. On Unix-like systems the kernel reads it to choose which interpreter runs the script. /usr/bin/env ruby asks env to find ruby on the current PATH, which is more portable than hard-coding a path like /usr/bin/ruby. It is only consulted when the file is executed directly, e.g. ./script.rb after chmod +x. Running the file with ruby script.rb ignores the shebang entirely, and Windows has no concept of it at all.
Comment
Single-line (#) or block (=begin...=end), ignored by the interpreter.
# starts a comment that runs to the end of the line, the most common style in idiomatic Ruby. A =begin/=end pair brackets a block comment spanning multiple lines; both markers must start at the beginning of a line, with no leading whitespace. Block comments are rarely used in practice: most style guides prefer stacking # lines even for long explanations, since =begin/=end are easy to misplace and some editors highlight them poorly. Both forms are stripped before execution and have no runtime effect.
Require statement
Imports modules or libraries.
require 'date' loads a standard library or gem by name, executing it once and caching the result so a second require of the same file is a no-op. require_relative does the same for a file path relative to the current file, which is the usual way to pull in another file from your own project. Unlike some languages, require is a normal method call, not special syntax. It can appear anywhere, though convention places all requires at the top of the file. Bundler and Gemfiles manage which gem versions are available to require in a given project.
Class definition
Blueprint for objects (class ... end).
A class block defines a new type; in Ruby, every value (including numbers, strings, and even classes themselves) is an object, and every object is an instance of some class. class Person ... end opens the class body, where method definitions, constants, and macro-like calls such as attr_accessor are evaluated. Ruby classes are "open": reopening class Person later, even in another file, adds to the same class rather than redefining it. This lets libraries and application code alike patch existing classes (including Ruby's own built-ins), a flexibility that is powerful but easy to overuse.
Attribute accessor
attr_accessor generates getter and setter methods.
attr_accessor :name, :age is shorthand for defining both a reader (name) and a writer (name=) method for each instance variable named, so person.name and person.name = 'Alice' both work without hand-writing the methods. attr_reader generates only the getter and attr_writer only the setter, for when one direction should stay private. Under the hood these are just regular method definitions created at class-body evaluation time. attr_accessor is a plain method call (a common Ruby idiom sometimes called a 'macro'), not special syntax, and takes a comma-separated list of symbols.
Constructor method
The initialize method runs automatically when a new object is created.
Person.new("Alice", 30) allocates a new object and calls its initialize method with the given arguments, so initialize is where you set up instance state. It is a regular instance method, the only thing special about it is that .new calls it for you and cannot be invoked directly. initialize is private by convention (Ruby marks it private automatically), reinforcing that objects should only be built through .new. Like any method it can take default arguments, keyword arguments, or a splat, giving .new the same flexible calling conventions as any other method.
Instance variable
Data stored within an object instance, prefixed with @.
A name starting with @, like @name, is an instance variable: it belongs to a specific object and lives as long as that object does. Instance variables need no declaration (referencing an unset one simply evaluates to nil) and they are invisible outside the object unless exposed through a method like an attr_accessor-generated getter. Each object keeps its own independent set of instance variables even when many objects share the same class, which is exactly how alice = Person.new("Alice", 30) and a second Person instance can hold different names without interfering with each other.
Method definition
Defines a reusable block of code with def ... end.
A def inside a class body defines an instance method, callable on any instance of that class; a def at the top level of a file defines a method on Object itself, effectively a global function. Either way the body runs until end, and the value of the last evaluated expression is returned automatically. An explicit return is only needed to exit early. Ruby methods accept positional, keyword, default, and splat (*args) parameters, and because Ruby uses duck typing there are no parameter type declarations: any object responding to the right methods can be passed in.
String interpolation
Embeds expressions in double-quoted strings using #{}.
"Hello, my name is #{@name}" evaluates the expression inside #{...} and inserts its to_s representation directly into the surrounding string. Interpolation only works inside double-quoted strings (or %Q{}/heredocs), single-quoted strings treat #{} as literal text. Any expression can go inside the braces, not just a variable name: method calls, arithmetic, and even multi-statement logic are all valid, e.g. "Total: #{price * qty}". This is generally preferred over string concatenation with + for both readability and performance.
Object instantiation & method call
Creating an instance and executing methods on it.
Person.new("Alice", 30) creates a new Person object, passing its arguments through to initialize, and binds the result to the local variable alice. From then on, alice.greet looks up the greet method on alice's class and calls it with alice as the implicit receiver (self inside the method). Method calls in Ruby can drop parentheses when unambiguous (alice.greet and alice.greet() are equivalent) and dot-chaining multiple calls together (alice.greet.upcase) is idiomatic for building small transformations out of simple methods.
Control flow (conditional)
Executes code based on a condition (if, else, elsif, case).
if ... else ... end branches on truthiness; in Ruby only nil and false are falsy, so 0 and "" (unlike in some other languages) both count as true. elsif chains additional conditions, and the whole construct evaluates to a value, so x = if cond then a else b end is valid. Ruby also offers a trailing modifier form, puts "big" if x > 10, and case/when for matching a value against several patterns, often clearer than a long elsif chain. unless is the negated counterpart of if.
Control flow (loop / iterator)
Executes code repeatedly (each, map, ranges, blocks).
(1..3).each do |i| ... end builds a Range and calls each on it, passing a block that runs once per element with i bound to the current value. Blocks (do ... end or { ... }) are central to idiomatic Ruby: methods like each, map, and select take a block and yield to it internally, rather than the caller managing an explicit loop counter. Ruby has while and for keywords too, but iterator methods with blocks are generally preferred, since they compose well and keep the loop variable's scope contained to the block. |i| is the block's parameter list, delimited by pipes rather than parentheses.
Official Ruby site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.