Variables & Data Types
📦 Variables & Data Types
nil actually behaves in a boolean context.
✏️ Variables — No Declaration Keyword
Ruby variables need no declaration keyword and no sigil — just assign a name and Ruby infers the type from the value, exactly like Python. This is a step further than PHP, which requires the $ sigil on every variable reference, not just at declaration:
Ruby variable names use snake_case by convention (like Python; PHP's convention varies between snake_case and camelCase depending on the codebase). Variable names are entirely lowercase by convention too — a name starting with a capital letter means something different in Ruby, as you'll see once classes and constants show up in later chapters.
🔤 Strings — Single vs Double Quotes Actually Matter
Both single and double quotes create strings, but unlike PHP (where the same distinction exists) they aren't interchangeable — only double quotes support interpolation and escape sequences:
String Interpolation: PHP vs Python vs Ruby
| PHP | Python | Ruby | |
|---|---|---|---|
| Interpolating syntax | "Hi $name" | f"Hi {name}" | "Hi #{name}" |
| Requires special prefix? | No | Yes — f-string | No — plain double quotes |
| Non-interpolating quote | '...' (literal) | Any plain string | '...' (literal) |
Common string methods: "ruby".upcase → "RUBY", "RUBY".downcase → "ruby", "ruby".length → 4, "ruby".reverse → "ybur", and concatenation with + or repetition with * ("ab" * 3 → "ababab").
🔢 Numbers — Watch Out for Integer Division
In PHP, 10 / 3 automatically produces a float (3.3333...) — there's no separate integer-division operator by default. In Python 3, / is always float division (10 / 3 → 3.333...), and you'd deliberately reach for // to truncate. Ruby does the opposite of both: plain / between two integers truncates, silently, with no warning. Forgetting this is one of the most common early Ruby bugs for anyone coming from either language.
🔖 Symbols — The New Concept
A symbol is a lightweight, immutable identifier written with a leading colon: :name, :status, :pending. Neither PHP nor Python has a direct equivalent — the closest PHP gets is a bare string constant, and the closest Python gets is an enum member, but neither carries Ruby's specific guarantee: every instance of the same symbol in a running program is the exact same object in memory.
Why symbols exist
Because a symbol is always the same object, comparing two symbols for equality is a fast identity check rather than a character-by-character string comparison. That makes symbols the natural choice for things that name something — hash keys, method names, status values — rather than for text that gets displayed or manipulated.
Symbols vs strings, in practice
Use a symbol (:active) when the value is really an internal label or identifier. Use a string ("active") when it's actual text — something you'll display, concatenate, or manipulate character by character. This distinction shows up constantly once hashes are in play, just below.
📚 Arrays
Ruby arrays are ordered, can hold mixed types in the same array (like PHP and Python lists), and the << "shovel" operator for appending is Ruby-specific — PHP uses $arr[] = $value, Python uses .append(value).
🗝️ Hashes
A hash is Ruby's key-value structure — directly comparable to a PHP associative array or a Python dict. Modern Ruby code almost always uses symbol keys with a shorthand syntax:
Key-Value Structures: PHP vs Python vs Ruby
| PHP | Python | Ruby | |
|---|---|---|---|
| Literal syntax | ["name" => "Philip"] | {"name": "Philip"} | { name: "Philip" } |
| Read a value | $arr["name"] | d["name"] | hash[:name] |
| Arrays and maps | Same type (associative array) | Separate: list vs dict | Separate: Array vs Hash |
🕳️ nil — Ruby's "Nothing"
nil is Ruby's equivalent of PHP's null or Python's None — it represents the absence of a value. The important, easy-to-miss detail: in a boolean context, Ruby treats only two values as falsy — nil and false. Everything else, including 0 and "" (empty string), is truthy.
PHP treats 0, "0", and "" as falsy in a boolean context. Python treats 0, 0.0, and empty collections/strings as falsy too. Ruby treats only nil and false as falsy — 0 and "" are both truthy. An if 0 check that would skip a block in PHP or Python will run it in Ruby.
💻 Coding Challenges
Challenge 1: Interpolation vs Concatenation
Create variables for a product name (string) and a price (float). Build the same sentence two ways: once using string interpolation with double quotes, and once using + concatenation (you'll need .to_s on the price). Print both and compare how much more readable the interpolated version is.
Goal: Get comfortable with #{ } interpolation as the default, idiomatic choice.
Challenge 2: Symbols Are Identical Objects
In irb, create the symbol :status twice and compare their .object_id — confirm they match. Then create the string "status" twice and compare those .object_ids — confirm they don't match. Write one sentence explaining why this matters for hash keys.
Goal: Directly observe symbols' defining property rather than just reading about it.
Challenge 3: An Array of Hashes
Build an array containing three hashes, each with symbol keys :name and :age, representing three people. Loop over the array with .each and print a sentence for each person using string interpolation to pull values out of the hash.
Goal: Combine arrays, hashes, and symbol keys the way they'll actually be used together in real Ruby code.
If you're ever unsure whether something should be a symbol or a string, ask: "will this value ever be displayed to a user, or manipulated as text?" If yes, it's a string. If it's really just a label your own code uses internally — a hash key, a status, a type name — reach for a symbol. This one habit will make your hashes and method calls look like idiomatic Ruby rather than PHP or Python translated line-by-line.
🎯 What's Next
Next chapter: Control Flow — if/unless, case/when, loops (while, until, for, each), and what it means for Ruby to be expression-oriented — where even an if statement returns a value you can assign.