Ruby — Reference

Source: https://www.ruby-lang.org/en/documentation/

Ruby

  • Created: 1995 by Yukihiro “Matz” Matsumoto (1.0 in 1996)
  • Latest stable: 4.0.3 (as of 2026-05; the Ruby 4.0 series is current — Ruby has shipped a year-end major release every December since 2013, the so-called “Christmas Ruby”)
  • Paradigms: Multi-paradigm — pure object-oriented (everything is an object, including nil, integers, classes), functional-leaning (blocks, procs, lambdas, immutability via freeze), reflective, dynamic
  • Typing: Dynamic, strong, duck-typed; gradual typing via Sorbet (community) or RBS + Steep / TypeProf (official, since Ruby 3.0)
  • Memory: Garbage-collected — generational, incremental mark-and-sweep with compaction (GC.compact since 2.7); since Ruby 3.3 the MMTk GC is pluggable as an experimental alternative
  • Compilation: Bytecode-interpreted by YARV (Yet Another Ruby VM); YJIT (Shopify’s lazy basic-block versioning JIT, written in Rust) is production-ready and default-on for many workloads since 3.3
  • Primary domains: Web (Rails, Sinatra), DevOps (Chef, Puppet, Vagrant), automation/scripting, CI tooling, internal DSLs, packaging (Homebrew is Ruby)
  • Official docs: https://docs.ruby-lang.org/en/master/

At a glance

  • Steward: Matz + the ruby-core team; design discussion happens at the Ruby Developer Meeting and on bugs.ruby-lang.org. The reference implementation is CRuby / MRI.
  • Other implementations: JRuby (JVM, real threads), TruffleRuby (GraalVM, often the fastest), mruby (embeddable), historically Rubinius and IronRuby.
  • License: BSD-style (Ruby License or 2-clause BSD). Community-driven, no single corporate owner.
  • Annual major release every December 25 — Ruby 3.0 (2020-12-25) introduced Ractors, Fibers scheduler, RBS, type-narrowing; 3.3 (2023-12-25) made YJIT production-grade. Ruby 4.0 dropped December 2025.

Getting started

  • Install / version manager: rbenv + ruby-build (most common), chruby, RVM, asdf, or mise. Windows: RubyInstaller (https://rubyinstaller.org/). macOS: brew install ruby works but most devs use rbenv to avoid the system Ruby.

  • Hello world (hello.rb):

    puts "Hello, world!"

    Run: ruby hello.rb.

  • Project layout (Bundler / standard gem skeleton): lib/<gem_name>.rb, lib/<gem_name>/version.rb, spec/ or test/, Gemfile, <name>.gemspec, Rakefile. Generated by bundle gem <name>.

  • Package/build tool: RubyGems is the package format and registry (https://rubygems.org); Bundler (Gemfile + Gemfile.lock) is the resolver/locker. Both ship with Ruby. Rake (Ruby make) is the canonical task runner.

  • REPL: irb (ships with Ruby; gained autocomplete + multi-line editing in 3.1 via Reline). pry (third-party) is the power-user alternative with binding.pry for breakpoints. debug gem is the official replacement for byebug since 3.1. (https://docs.ruby-lang.org/en/master/IRB.html)

Basics

  • Types/literals: Integer (arbitrary precision; Bignum/Fixnum unified in 2.4), Float, Rational (1/2r), Complex (1+2i), String, Symbol (:foo, interned), Array, Hash, Range (1..10, 1...10), nil, true, false. Numeric literals: 1_000_000, 0xFF, 0b1010, 0o777.
  • Variables: Sigil-based scoping — local, @instance, @@class, $global, CONSTANT (capitalized). No var/let — first assignment defines.
  • Scoping: Methods do not close over enclosing locals (they create a fresh scope); blocks/procs/lambdas do close over them. def opens a new scope; do...end / {} blocks inherit.
  • Control flow: if/unless/while/until (also as suffix modifiers: puts x if x); case/when (uses === for matching) — gained pattern matching in 2.7 (experimental) and stabilized in 3.0 (case obj in [a, b, *]). Exceptions: begin/rescue/ensure/end. loop do ... end for infinite.
  • Functions: def name(args); ...; end. Implicit return of last expression. Default args, keyword args (def f(x:, y: 0)), splat (*args), double-splat (**kwargs), block param (&block). Method visibility: public (default), private, protected. Methods are not first-class — use method(:name) to get a Method object, or Proc/lambda for closures.
  • Strings: Double-quoted with #{expr} interpolation; single-quoted (literal, no interpolation). Heredocs (<<~END for indent-stripped). Encoded as UTF-8 by default since 2.0; per-string String#encoding. Frozen string literals via # frozen_string_literal: true magic comment (default-on planned for 4.x).
  • Collections: Array ([1,2,3]), Hash ({a: 1, b: 2}), Set (require "set"). All support Enumerable mixin: map, select, reject, reduce/inject, each_with_object, group_by, chunk_while, lazy for lazy chains.

Intermediate

  • Type system depth: Dynamic + duck-typed by default. RBS (Ruby Signature) is the official type-declaration language living in .rbs files alongside .rb. Steep does static checking; TypeProf does abstract-interpretation type inference. Sorbet (Stripe) is a competing ecosystem with inline # typed: true sigs and a faster checker. (https://github.com/ruby/rbs)
  • Modules: Files are required (loaded once) or loaded (re-runs every call). Bundler’s bundle exec ensures the right gem versions. Module is a namespace + mixin mechanism — include adds instance methods, extend adds class methods, prepend (Ruby 2.0+) puts the module before the class in the ancestor chain.
  • Error handling: Exception class hierarchy rooted at ExceptionStandardError → … rescue without args catches StandardError (not Exception — this is intentional, to not catch SystemExit/Interrupt). raise, retry, ensure, else. Custom errors subclass StandardError.
  • Concurrency primitives:
    • Threads: real OS threads but constrained by the GVL (Global VM Lock) — only one thread executes Ruby code at a time. I/O releases the GVL.
    • Fibers: coroutines (cooperative). Since 3.0, Fiber.scheduler lets gems like async (socketry/async) plug in event-loop scheduling — sleep, Net::HTTP, etc., become non-blocking transparently.
    • Ractors: truly parallel actors with no shared mutable state, introduced experimentally in 3.0 — share only frozen/copied/movable objects. Still considered experimental in 4.0 (many gems are not Ractor-safe).
    • Process for true parallelism (fork-based; Process.fork).
  • I/O: File.read, File.open, IO.copy_stream, Pathname (object-oriented paths), StringIO for in-memory streams. Net::HTTP in stdlib (low-level); URI.open for quick fetches. The socket lib for TCP/UDP/Unix.
  • Stdlib highlights: JSON, YAML (Psych), CSV, OpenStruct, Set, Date/DateTime/Time, OptionParser (CLI flags), Logger, Tempfile, Tmpdir, Open3 (subprocess with stdout/stderr/exit), Forwardable (delegation DSL), Comparable/Enumerable mixins.

Advanced

  • Memory/GC: Tri-color incremental mark-and-sweep, generational since 2.1, compacting since 2.7 (GC.compact), object-shape-aware allocation since 3.2. MMTk (Memory Management Toolkit, from the JVM/Java research lineage) was integrated as a pluggable GC in 3.3 — build with --with-mmtk. Heap is divided into pages of 40-byte slots (RVALUE); larger objects allocate auxiliary memory via xmalloc. (https://shopify.engineering/understanding-ruby-gc-through-gc-stat)
  • Concurrency deep dive:
    • GVL is per-Ractor (not per-process), so Ractors get true parallelism. Within a Ractor, threads share a GVL.
    • YJIT is GVL-aware and ships in the CRuby binary; enable with --yjit or RUBY_YJIT_ENABLE=1. Stats via RubyVM::YJIT.runtime_stats.
    • Async (https://github.com/socketry/async) is the leading Fiber-scheduler implementation — Async { ... } blocks become cooperative.
  • FFI: fiddle (stdlib, libffi-based). The third-party ffi gem is more popular and works on JRuby too. C extensions (.c + extconf.rb + Init_<name>) are still common for high-perf gems (Nokogiri, OJ).
  • Reflection: Ruby’s metaprogramming is the headline feature — Object#class, Object#methods, Method#source_location, instance_variable_get/set, define_method, class_eval, instance_eval, Module#prepend, ObjectSpace.each_object, Kernel#caller. TracePoint for instrumentation.
  • Performance tools: benchmark (stdlib), benchmark-ips (gem), stackprof (sampling profiler, the Rails-world standard), ruby-prof, vernier (newer, Ractor-aware), memory_profiler, derailed_benchmarks (Rails), rbspy (out-of-process sampler, written in Rust).

God mode

  • method_missing + respond_to_missing?: the canonical metaprogramming hook — intercept any unknown method call. Always pair them; only overriding method_missing breaks respond_to?/duck-typing checks.
  • Refinements: scoped monkey-patches (refine String do ... end + using StringExt in a file/module). Avoids polluting global classes; lexically scoped per file.
  • BasicObject: the truly empty parent class (no Kernel, no Object methods). Used for Delegator, transparent proxies, DSL evaluators.
  • TracePoint: subscribe to events (:call, :return, :line, :raise, :b_call) at runtime — powers byebug, ruby-debug, coverage tools, and can be used for AOP. (https://docs.ruby-lang.org/en/master/TracePoint.html)
  • binding: captures the local scope as an object; binding.eval("x") evaluates a string in that scope; binding.irb (built-in since 2.5) and binding.pry drop into a REPL with full local access at the call site.
  • RubyVM::InstructionSequence: compile and inspect YARV bytecode — RubyVM::InstructionSequence.compile("1+1").disasm prints opcodes. Lets you build code at runtime, cache compiled bytecode (Bootsnap), or write custom interpreters.
  • MJIT → YJIT → ZJIT: MJIT (the original method-based JIT, generated C and called the system compiler) was deprecated in 3.3 in favor of YJIT (Shopify’s basic-block-versioning JIT in Rust). ZJIT is the next-gen IR-based JIT in development for the 4.x series.
  • GVL hacks: rb_thread_call_without_gvl in C extensions releases the lock; Thread#priority is mostly cosmetic. Use Process.fork or Ractors for true parallelism.
  • Frozen string optimization: # frozen_string_literal: true magic comment freezes all string literals in the file at parse time, allowing the VM to deduplicate and skip allocation. Performance + safety win; default on the roadmap for 4.x.
  • Ripper: stdlib SAX-like parser that exposes the lexer/parser events (Ripper.sexp, Ripper.lex, Ripper.tokenize). Powers RuboCop’s AST (via the parser gem, which is a more user-friendly wrapper). Lets you build linters, formatters, refactoring tools.
  • Prism parser: new universal Ruby parser written in C, replacing the legacy parse.y grammar — adopted by Ruby 3.3+ as a parallel parser, default in 3.4 / 4.0. Used by Sorbet, RuboCop, Solargraph, IRB.
  • Object shapes: since 3.2, instance variable layout is tracked via a shape tree (similar to V8 hidden classes) — drastically faster @ivar access. Run RubyVM::Shape.find_by_id(obj.shape_id) to inspect.

Idioms & style

  • Naming: snake_case for methods/locals, CamelCase for classes/modules, SCREAMING_SNAKE_CASE for constants. Predicates end in ? (empty?), mutating methods end in ! (sort!).
  • Formatter / linter: RuboCop (https://docs.rubocop.org/) — both linter and formatter, with the official Ruby Style Guide at https://rubystyle.guide. Standard (Ruby Standard) is a zero-config RuboCop preset. rubocop -a for auto-fix.
  • Idiomatic patterns: prefer iterators (each, map) over indexed for; use blocks over explicit Procs; “tell, don’t ask”; Enumerable mixin everywhere; small methods (Sandi Metz’s “five rules”); guard clauses (return unless x); ||= for memoization; tap for side-effects in chains; then/yield_self for pipelining.
  • Expert review focus: monkey-patching of stdlib without refinements, eval/instance_eval of user input, N+1 queries (Rails), unfrozen string literals in hot paths, blocking I/O inside Async/Ractor contexts, Ractor-unsafe constants, mutable default keyword args.

Ecosystem

  • Web/server: Rails (the dominant full-stack framework — convention over configuration), Sinatra (microframework), Hanami (clean-architecture alternative), Roda (routing tree). App servers: Puma (default since Rails 5), Unicorn, Falcon (Async-based), Rack is the universal web server interface.
  • API: Grape, Rails API mode.
  • Background jobs: Sidekiq (Redis-backed, threads), GoodJob (Postgres-backed, Rails-native), Resque, Solid Queue (Rails 8+ default).
  • DevOps / infra: Chef, Puppet, Vagrant, Fastlane (iOS/Android automation), Capistrano (deploy), Berkshelf.
  • Data: ActiveRecord (Rails ORM), Sequel (lighter ORM), ROM-rb (data mapper), pg / mysql2 / sqlite3 native gems.
  • Testing: RSpec (BDD, dominant), Minitest (stdlib, Rails default), Cucumber (Gherkin), Capybara (browser/system tests), VCR (record HTTP), WebMock, factory_bot, shoulda-matchers.
  • Docs: YARD (yardoc) — the de-facto documentation generator; RDoc ships with Ruby. Public hosting at https://rubydoc.info.
  • Notable users: GitHub (Rails monolith), Shopify (Rails + sponsors much of CRuby/YJIT), Airbnb, Stripe (Sorbet), Basecamp/37signals (Rails inventors), Heroku, Twitch (originally), Square.

Gotchas

  • nil, false are the only falsy values — 0, "", [] are all truthy.
  • == vs eql? vs equal?: == is value equality (overridable), eql? is stricter (1 == 1.0 true, 1.eql?(1.0) false), equal? is object identity. Don’t override equal?.
  • String#+ allocates; use String#<< or String#concat to mutate in-place. Frozen strings can’t be mutated — FrozenError.
  • Default keyword args / hash params evaluated once at definition if mutable — def f(arr = []); arr << 1; arr; end does not leak (unlike Python), because Ruby re-evaluates default expressions per call.
  • proc vs lambda: lambdas check arity strictly and return returns from the lambda; procs are lax and return returns from the enclosing method.
  • attr_accessor :name + name = "x" inside an instance method creates a local — must write self.name = "x".
  • Ractors don’t share class-level state — @@class_var, mutable constants, ENV writes — reads are usually fine, writes raise.
  • Monkey-patching is easy and dangerous; use refinements when possible.
  • require paths are relative to $LOAD_PATH, not the calling file — use require_relative for sibling files.
  • ||= short-circuits on nil/false, so flag ||= true won’t reset flag = false.
  • each returns the original collection; map returns a new array — confusing the two is a classic bug.
  • YJIT must be explicitly enabled in older 3.x; default-on for many apps in 3.3+ but check RubyVM::YJIT.enabled?.

Citations