Ruby
Begin with one complete Ruby file and one exact result. Twenty-one chapters add one language or system boundary at a time, while Bundler, gems, RSpec, database drivers, live HTTP, Rails, packaging, signing, deployment, and production remain explicit adapters that must produce their own evidence.
Interpreter, gems, and project setup
Objective Run Ruby reproducibly and organize an executable project.
Core explanation
Ruby source runs on a Ruby implementation such as CRuby. Pin the Ruby version, use Bundler with Gemfile.lock, keep source under lib, executables under bin, and tests under test or spec. Warnings, formatting, and static checks belong in CI. Ruby implementations differ in concurrency, JIT, native extension, and platform behavior, so state and test the supported runtime rather than relying only on language syntax. Pin Ruby with the project tool, commit Gemfile.lock for applications, run every command through bundle exec, and inspect the resolved platform graph. Keep the load path explicit, avoid depending on the current directory, enable warnings, and separate source, executables, tests, configuration, generated files, and runtime data. CI should start from a clean checkout, verify the lock and native build requirements, and produce one traceable artifact. The required command is a complete local Ruby program. It proves syntax compatibility, warnings, output, and status only; selecting and installing a maintained modern Ruby, Bundler, native libraries, and a deployable environment requires separate setup evidence.
Know which Ruby and executable actually run
Ruby source executes on an implementation such as CRuby, whose version, platform, compile options, standard library, native extensions, environment, load path, working directory, and process limits shape behavior. A version manager can select Ruby for development, but the project contract must also pin it for CI and production. Record RUBY_VERSION, engine, platform, Bundler, lock hash, source revision, command, and artifact identity.
The ruby command interprets a file; an executable script can use a shebang but should still run under the project-selected Ruby. $LOAD_PATH controls library discovery, require loads a feature once by its logical path, and require_relative resolves from the current file. Avoid changing the load path unpredictably or depending on the shell directory. A packaged command should start correctly from an unrelated working directory.
Build a conventional isolated Bundler project
A Gemfile declares dependency intent and Gemfile.lock records the exact application graph. Commit the lock for applications and install from it without updating during deployment. Place reusable code under lib with matching constants and paths, commands under bin, tests under spec or test, configuration at the composition boundary, and generated or runtime data outside source. Use bundle exec so activated gems match the lock.
Bundler groups can exclude development tools from production, but required runtime gems must not be loaded only by accidental development state. Validate the Ruby platform and native build dependencies in the target environment. A clean install must not rely on user-global gems, mutable Git branches, undeclared system libraries, or a previously compiled extension. Preserve license and source information for the resolved graph.
Separate syntax, loading, runtime, and environment failures
A parser error happens before ordinary execution; LoadError indicates feature discovery or native loading; NameError can reveal missing constants or local-variable ambiguity; argument and type errors describe call mismatches; domain and external exceptions occur later. Identify the earliest layer before editing code. Warnings should be enabled under an owned policy and deprecations resolved before the runtime upgrade that makes them fatal.
Standard output carries requested results and standard error carries diagnostics. A top-level CLI boundary maps known failures to documented exit codes, records safe context, and leaves programmer defects visible for supervision. Do not rescue Exception or every StandardError and report success. Signals, encoding, locale, timezone, permissions, file descriptors, and terminal behavior are external inputs that a production command must either control or document.
Laboratory: package and diagnose a Ruby command
Create academy-core code under lib, a bin/academy command, a test suite, a locked Gemfile, format and lint configuration, and a small build task. Add version and doctor subcommands that report safe runtime facts. Run from a clean environment and a different directory with standard streams redirected, then package the exact source and lock metadata used for verification.
Seed wrong constant path case, missing require, global gem dependency, native extension mismatch, syntax error, warning, invalid configuration, unwritable output, interrupt, and exception after partial file creation. Acceptance classifies each failure, cleans temporary resources, returns stable exit status, leaks no secret, passes on the supported Ruby matrix, and lets another maintainer add one command without changing load-path magic.