Polyglot
Languages Ruby syntax
Ruby § syntax

Syntax

Ruby’s syntax is expression-oriented (every construct produces a value), object-oriented (every value is an object — including nil, true, integers, classes, and the result of every expression), and minimally punctuated (parentheses around method arguments are typically optional, semicolons separate statements only when multiple appear on one line). Method calls dispatch dynamically; blocks (Ruby’s signature feature) attach to method calls and admit substantial inline computation. Indentation is purely conventional — Ruby uses end keywords to close blocks, methods, classes, and modules. The combination — expression-orientation, ubiquitous object-message dispatch, blocks, and the conventional readability — is the substance of Ruby’s syntactic identity.

This page covers the surface a working programmer encounters routinely.

A complete program

The classical hello world:

puts "Hello, world!"

A more substantial example:

require "json"

class Person
  attr_accessor :name, :age

  def initialize(name:, age:)
    @name = name
    @age = age
  end

  def greeting
    "Hello, #{@name}."
  end
end

people = JSON.parse(File.read("people.json"))
people.map { |p| Person.new(name: p["name"], age: p["age"]) }
  .sort_by(&:age)
  .each { |p| puts p.greeting }

The principal features visible:

  • require — loads a library.
  • class Person ... end — class definition.
  • attr_accessor :name, :age — generated getters and setters.
  • def initialize(...) — the constructor.
  • Keyword arguments name:, age:.
  • @name — instance variable.
  • "Hello, #{@name}." — string interpolation.
  • map { |p| ... } — block syntax with curly braces.
  • &:age — the symbol-to-proc shorthand.
  • Method chaining with . continuations.

Execution:

ruby hello.rb

Source character set

Ruby source is interpreted as UTF-8 by default. Identifiers may use Unicode letters; ASCII identifiers are conventional.

A magic comment admits declaring an alternate encoding:

# encoding: UTF-8
# frozen_string_literal: true

The frozen_string_literal: true magic comment is conventional in modern Ruby — it admits substantial performance gains by interning string literals.

Identifiers and naming conventions

Ruby’s naming conventions are enforced by the language (not just by style):

FormUseExample
lowercase_snake_caselocal variables, method namesuser_name, to_s
CamelCaseclasses, modulesPerson, Enumerable
UPPER_SNAKE_CASEconstantsMAX_RETRIES
@nameinstance variables@user
@@nameclass variables@@count
$nameglobal variables$stdout
:namesymbols:age, :to_s
name?predicate methods (boolean return)empty?, nil?
name!”dangerous” methods (mutate or raise)sort!, save!

The ? and ! suffixes are part of the method name — they are not separate operators. The ? is conventional for predicates; the ! for methods that mutate the receiver or have a stricter alternative.

Reserved keywords

The keywords:

__ENCODING__   __LINE__       __FILE__       BEGIN          END
alias          and            begin          break          case
class          def            defined?       do             else
elsif          end            ensure         false          for
if             in             module         next           nil
not            or             redo           rescue         retry
return         self           super          then           true
undef          unless         until          when           while
yield

Reserved words may not be used as method names without the define_method trick or other metaprogramming.

Comments

Two comment forms:

# A single-line comment, terminated by the end of the line.

=begin
A block comment.
The =begin and =end must be at the start of a line.
=end

The block-comment form is rare in idiomatic Ruby; multi-line # comments are conventional.

For documentation, RDoc parses comments preceding declarations:

# Greets the named user.
#
# @param name [String] the name to greet
# @return [String] the greeting
def greet(name)
  "Hello, #{name}"
end

The conventional documentation tooling is YARD — a more substantial RDoc replacement that admits structured annotations.

The expression-oriented language

Ruby is expression-oriented: every construct (including if, case, begin, blocks, method definitions) produces a value:

max = if a > b then a else b end                 # if as expression

result = case n
         when 0 then "zero"
         when 1..9 then "small"
         else "large"
         end                                      # case as expression

x = (1..10).map { |i| i * i }                     # block expression

The last expression in a block, method, or begin block is the value of that block:

def square(n)
  n * n                                          # implicit return
end

result = begin
  do_first
  do_second
  compute_result                                  # this value
end

The return keyword is admitted but conventional only for early returns.

Variables and assignment

Ruby admits five kinds of variables, distinguished by prefix:

x = 5                                             # local — visible in current scope
@x = 5                                            # instance — per-object
@@x = 5                                           # class — shared across instances
$x = 5                                            # global — visible everywhere
X = 5                                             # constant — uppercase initial

Local variables are not declared — assignment creates them. The first assignment to a name introduces it in the current scope.

Multiple assignment:

a, b = 1, 2
a, b = b, a                                       # swap
first, *rest = [1, 2, 3, 4]                       # first=1, rest=[2,3,4]
*init, last = [1, 2, 3, 4]                        # init=[1,2,3], last=4
a, b, c = [10, 20, 30]                            # destructure array

The splat (*) collects remaining elements; conventional for variable-length unpacking.

Method calls

Method calls dispatch via the . operator:

puts "hello"                                      # method call on Kernel
"hello".upcase                                    # method on String
[1, 2, 3].sum                                     # method on Array
5.times { puts "hi" }                             # method on Integer

obj.method_name(arg1, arg2)
obj.method_name arg1, arg2                        # parens optional
obj.method_name                                    # no args (no parens)

Method calls without parentheses are admitted; the conventional discipline includes parentheses for methods with arguments and omits them for argument-less methods.

The . precedes the method name; alternatively, the safe navigation &. returns nil if the receiver is nil:

user.name                                         # raises if user is nil
user&.name                                        # returns nil if user is nil

Blocks

Blocks are anonymous code passed to method calls. Two forms:

# Curly braces (single-expression):
[1, 2, 3].map { |n| n * 2 }                       # [2, 4, 6]

# do...end (multi-line):
[1, 2, 3].each do |n|
  puts n
  puts n * 2
end

The conventional discipline:

  • { } for single-expression blocks.
  • do...end for multi-line or method-chained blocks.

Blocks are passed implicitly — they are not part of the argument list. The receiving method calls yield to invoke them or accepts an explicit &block parameter. Treated in Blocks and procs.

Statements and statement separators

Newlines terminate statements; semicolons admit multiple statements on one line:

x = 5
y = 10
z = x + y

# Equivalent:
x = 5; y = 10; z = x + y

Statements may continue across multiple lines via:

  • A trailing \ (line continuation).
  • A trailing operator (+, ,, &&, etc.).
  • An open paren, bracket, or brace.
  • A trailing . (method chain continuation).
result = compute_a +
         compute_b +
         compute_c

users.filter { |u| u.active }
     .map(&:name)
     .sort

The trailing-. form is conventional for method chains.

Modifier conditional and loops

Ruby admits modifier (postfix) forms for if, unless, while, until:

puts "found" if found
return unless valid?

n -= 1 while n > 0

The modifier forms are conventional for short, single-statement conditionals.

Method definition

The def keyword introduces a method:

def add(a, b)
  a + b
end

def greet(name = "world")                         # default argument
  puts "Hello, #{name}"
end

def with_keywords(name:, age:)                    # keyword arguments
  puts "#{name} is #{age}"
end

def with_splat(*args)                             # variadic positional
  args.sum
end

def with_block(&block)                            # capture block
  block.call(42)
end

The form: def name(params)\n body\nend. The last expression of the body is the implicit return value. Parentheses around parameters are optional:

def add a, b                                     # admitted but rare
  a + b
end

The conventional discipline includes parentheses around parameters.

Treated in Methods.

Classes

The class keyword introduces a class:

class Counter
  def initialize(initial = 0)
    @count = initial
  end

  def increment
    @count += 1
  end

  def to_s
    "Counter(#{@count})"
  end
end

c = Counter.new
c.increment
puts c                                            # "Counter(1)"

Treated in Classes and OOP.

A note on what Ruby admits

Several distinguishing features:

  • Everything is an object — including nil, true, classes, integers.
  • Open classes — existing classes can be reopened and extended.
  • Optional parenthesesputs "hello" and puts("hello") are equivalent.
  • Implicit returns — the last expression’s value is returned.
  • No type declarations — types are inferred at runtime.
  • Blocks — first-class anonymous code attached to method calls.
  • Symbols — interned strings used as identifiers.
  • Method missing — dynamic method dispatch via method_missing.
  • No primitive vs. object distinction5 is a Integer object.
  • Garbage collection — automatic memory management.
  • No type coercion in comparisons1 == "1" is false.
  • Multiple inheritance via mixinsinclude admits combining modules.

The combination — pure object orientation, blocks, dynamic dispatch, the substantial standard library, the metaprogramming facilities — is the substance of Ruby’s identity. The discipline is largely about expressing intent through idiomatic message-passing rather than through structural types or static analysis.