LEARN RAILS WEB FRAMEWORKS
Learn Ruby on Rails: From Surface to Core
Goal: Deeply understand how Ruby on Rails works under the hood—from the Rack interface to ActiveRecord magic to metaprogramming—and how it compares to other major web frameworks.
Why Learn Rails Internals?
Ruby on Rails didn’t just create a framework; it created a philosophy that influenced every major web framework that came after it. Django, Laravel, Phoenix, Next.js—they all borrowed ideas from Rails.
Understanding Rails deeply teaches you:
- Convention over Configuration - The philosophy that changed web development
- Ruby metaprogramming - How “magic” is actually implemented
- ORM design patterns - ActiveRecord and its trade-offs
- MVC architecture - The dominant web application pattern
- Middleware pipelines - How requests flow through frameworks
- Web framework design - Principles applicable to any language
After completing these projects, you will:
- Understand every layer from HTTP request to database query to HTML response
- Know how Rails “magic” works (it’s metaprogramming, not magic)
- Be able to build your own web framework from scratch
- Appreciate the trade-offs between Rails, Django, Laravel, and Phoenix
- Debug Rails applications at any level of the stack
Core Concept Analysis
The Rails Philosophy
┌─────────────────────────────────────────────────────────────────────────────┐
│ THE RAILS DOCTRINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. CONVENTION OVER CONFIGURATION (CoC) │
│ └── Sensible defaults eliminate boilerplate │
│ └── "If you name things consistently, Rails just works" │
│ │
│ 2. DON'T REPEAT YOURSELF (DRY) │
│ └── Every piece of knowledge in one place │
│ └── Metaprogramming enables this without copy-paste │
│ │
│ 3. PROGRAMMER HAPPINESS │
│ └── Optimize for developer experience │
│ └── Beautiful code over premature optimization │
│ │
│ 4. THE MENU IS OMAKASE │
│ └── Rails makes choices for you (but you can override) │
│ └── One way to do things = shared vocabulary │
│ │
│ 5. INTEGRATED SYSTEMS │
│ └── Full-stack: database to HTML, all coherent │
│ └── Not just a "micro" framework │
│ │
│ 6. PROVIDE SHARP KNIVES │
│ └── Power users get powerful tools │
│ └── Trust developers to use them wisely │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Request Lifecycle: From HTTP to Response
┌─────────────────────────────────────────────────────────────────────────────┐
│ RAILS REQUEST LIFECYCLE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ HTTP Request │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ WEB SERVER (Puma, Unicorn, Passenger) │ │
│ │ • Parses HTTP request │ │
│ │ • Creates Rack env hash │ │
│ │ • Hands off to Rack interface │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MIDDLEWARE STACK (runs top to bottom) │ │
│ │ • ActionDispatch::Static (serve static files) │ │
│ │ • Rack::Sendfile (optimize file serving) │ │
│ │ • ActionDispatch::Executor (manage autoloading) │ │
│ │ • ActiveSupport::Cache (caching layer) │ │
│ │ • Rack::Session::Cookie (session handling) │ │
│ │ • ActionDispatch::Flash (flash messages) │ │
│ │ • ActionDispatch::ContentSecurityPolicy │ │
│ │ • ActionDispatch::Callbacks │ │
│ │ • ActionDispatch::Cookies (cookie handling) │ │
│ │ • ActionDispatch::Session (session store) │ │
│ │ • ActionDispatch::RemoteIp (client IP detection) │ │
│ │ • Rack::MethodOverride (RESTful form methods) │ │
│ │ • ActionDispatch::ShowExceptions (error pages) │ │
│ │ • ... (20+ more middleware) │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ROUTER (ActionDispatch::Routing::RouteSet) │ │
│ │ • Matches URL pattern to route │ │
│ │ • Extracts params from URL (/users/:id → params[:id]) │ │
│ │ • Identifies controller and action │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONTROLLER (ActionController::Base) │ │
│ │ • before_action callbacks run │ │
│ │ • Action method executes │ │
│ │ • Interacts with Model layer │ │
│ │ • Sets instance variables for view │ │
│ │ • after_action callbacks run │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ┌─────────┴─────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
│ │ MODEL (ActiveRecord) │ │ VIEW (ActionView) │ │
│ │ • Query database │ │ • ERB/HAML template │ │
│ │ • Run validations │ │ • Render partials │ │
│ │ • Execute callbacks │ │ • Apply layouts │ │
│ │ • Return objects │ │ • Generate HTML │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RESPONSE │ │
│ │ • [status, headers, body] (Rack format) │ │
│ │ • Flows back through middleware (reverse order) │ │
│ │ • Web server sends to client │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Rails Component Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ RAILS COMPONENTS (GEMS) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ RAILTIES │ │
│ │ • Rails::Application │ │
│ │ • Configuration & initialization │ │
│ │ • Generators │ │
│ │ • Rake tasks │ │
│ │ • The "glue" that holds everything together │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┼────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ACTION PACK │ │ ACTIVE RECORD │ │ ACTION VIEW │ │
│ │ │ │ │ │ │ │
│ │ • Controllers │ │ • ORM │ │ • Templates │ │
│ │ • Routing │ │ • Migrations │ │ • Helpers │ │
│ │ • Middleware │ │ • Validations │ │ • Partials │ │
│ │ • Request/Resp │ │ • Callbacks │ │ • Layouts │ │
│ │ • Parameters │ │ • Associations │ │ • Form builders │ │
│ └─────────────────┘ │ • Querying │ └─────────────────┘ │
│ │ • Transactions │ │
│ └─────────────────┘ │
│ │ │
│ ┌────────────────────────┼────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ACTIVE SUPPORT │ │ ACTION MAILER │ │ ACTION CABLE │ │
│ │ │ │ │ │ │ │
│ │ • Extensions │ │ • Email sending │ │ • WebSockets │ │
│ │ • Inflections │ │ • Views/layouts │ │ • Channels │ │
│ │ • Time zones │ │ • Attachments │ │ • Pub/Sub │ │
│ │ • Caching │ │ • Previews │ │ • Real-time │ │
│ │ • Notifications │ └─────────────────┘ └─────────────────┘ │
│ │ • Dependencies │ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ACTIVE JOB │ │ ACTIVE STORAGE │ │ ACTION TEXT │ │
│ │ │ │ │ │ │ │
│ │ • Background │ │ • File uploads │ │ • Rich text │ │
│ │ • Queues │ │ • Cloud storage │ │ • Trix editor │ │
│ │ • Adapters │ │ • Variants │ │ • Attachments │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Ruby Metaprogramming: The “Magic” Behind Rails
# How Rails turns this:
class User < ApplicationRecord
belongs_to :company
has_many :posts
end
# Into this (conceptually):
class User < ApplicationRecord
def company
Company.find(company_id)
end
def company=(company)
self.company_id = company.id
end
def posts
Post.where(user_id: id)
end
def posts<<(post)
post.user_id = id
post.save
end
# ... and dozens more methods
end
Key metaprogramming techniques Rails uses:
| Technique | What it does | Rails usage |
|---|---|---|
method_missing |
Intercepts calls to undefined methods | Dynamic finders (find_by_email) |
define_method |
Creates methods at runtime | Association methods, attribute accessors |
class_eval |
Evaluates code in class context | Adding methods to models |
instance_eval |
Evaluates code in instance context | DSL blocks (config.do_something) |
const_missing |
Intercepts undefined constant access | Autoloading classes |
included/extended |
Hooks when module is mixed in | Concerns, ActiveSupport::Concern |
inherited |
Hook when class is subclassed | ApplicationRecord setup |
ActiveRecord Internals
┌─────────────────────────────────────────────────────────────────────────────┐
│ ACTIVERECORD ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User.where(active: true).order(:name).limit(10) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ActiveRecord::Relation (lazy, chainable query builder) │ │
│ │ │ │
│ │ • Doesn't execute until needed (lazy evaluation) │ │
│ │ • Each method returns a new Relation │ │
│ │ • Stores query components: where_clause, order_values, etc. │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ .to_a, .each, .first, etc. │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Arel (Abstract Relational Algebra) │ │
│ │ │ │
│ │ • Builds an Abstract Syntax Tree (AST) for SQL │ │
│ │ • Database-agnostic representation │ │
│ │ • Example: Arel::Nodes::SelectStatement │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Connection Adapter (PostgreSQLAdapter, Mysql2Adapter, etc.) │ │
│ │ │ │
│ │ • Converts Arel AST to database-specific SQL │ │
│ │ • Manages connection pooling │ │
│ │ • Handles prepared statements │ │
│ │ • Type casting (Ruby ↔ Database) │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Database │ │
│ │ SELECT "users".* FROM "users" │ │
│ │ WHERE "users"."active" = true │ │
│ │ ORDER BY "users"."name" ASC │ │
│ │ LIMIT 10 │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Instantiation │ │
│ │ │ │
│ │ • Each row → User instance │ │
│ │ • Type casting (DB types → Ruby types) │ │
│ │ • Attribute assignment │ │
│ │ • Returns array of User objects │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Framework Comparison Overview
| Aspect | Rails (Ruby) | Django (Python) | Laravel (PHP) | Phoenix (Elixir) |
|---|---|---|---|---|
| Philosophy | Convention over Config | Explicit is better | Elegant syntax | Functional + OTP |
| ORM | ActiveRecord (Active Record pattern) | Django ORM (Active Record-ish) | Eloquent (Active Record) | Ecto (Repository pattern) |
| Real-time | Action Cable (WebSockets) | Channels (Django 3+) | Laravel Echo | Phoenix Channels (built-in) |
| Templates | ERB, HAML | Django Templates | Blade | EEx, HEEx |
| Admin | Custom/Gems (Administrate) | Django Admin (built-in) | Nova (paid), Voyager | Custom |
| Job Queue | Active Job + Sidekiq | Celery | Laravel Queues | Oban, Broadway |
| Auth | Devise (gem) | django.contrib.auth | Laravel Breeze/Jetstream | Custom/Libraries |
| Concurrency | Multi-process (Puma threads) | Multi-process (WSGI/ASGI) | Multi-process (PHP-FPM) | BEAM VM (millions of processes) |
| Learning Curve | Moderate | Moderate | Easy | Steeper (functional) |
| Performance | Good | Good | Good | Excellent |
Project List
Projects are ordered from fundamentals to advanced implementations, building deep understanding of Rails and web frameworks in general.
Project 1: Rack Application from Scratch (The Foundation)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby
- Alternative Programming Languages: N/A (Rack is Ruby-specific)
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 2: Intermediate
- Knowledge Area: Web Servers / HTTP Protocol
- Software or Tool: Rack, Puma/WEBrick
- Main Book: “The Well-Grounded Rubyist” by David A. Black
What you’ll build: A minimal web application using only Rack—no Rails, no Sinatra—that handles HTTP requests, parses parameters, manages sessions, and returns responses.
Why it teaches web framework fundamentals: Every Ruby web framework (Rails, Sinatra, Hanami) is built on Rack. Understanding Rack means understanding the foundation everything else is built upon.
Core challenges you’ll face:
- Understanding the Rack interface → maps to the call method and env hash
- Parsing HTTP requests → maps to query strings, form data, headers
- Managing state → maps to cookies and sessions
- Composing middleware → maps to how Rails middleware stack works
Key Concepts:
- Rack Specification: Rack GitHub
- HTTP Basics: “HTTP: The Definitive Guide” by Gourley & Totty
- Ruby Blocks: “The Well-Grounded Rubyist” Chapter 6 - David A. Black
Difficulty: Intermediate Time estimate: 1 week Prerequisites: Ruby basics, understanding of HTTP
Real world outcome:
# Your minimal Rack app running:
$ rackup config.ru
Puma starting...
* Listening on http://127.0.0.1:9292
# In browser or curl:
$ curl "http://localhost:9292/hello?name=World"
Hello, World!
$ curl -X POST -d "username=alice&password=secret" http://localhost:9292/login
Welcome, alice! Your session ID is abc123
$ curl -b "session_id=abc123" http://localhost:9292/dashboard
Dashboard for alice (authenticated)
Implementation Hints:
The Rack interface is simple—one method, three return values:
# A valid Rack app is ANY object that responds to `call`
class MyApp
def call(env)
# env is a Hash with request info:
# - 'REQUEST_METHOD' => 'GET'
# - 'PATH_INFO' => '/hello'
# - 'QUERY_STRING' => 'name=World'
# - 'rack.input' => IO object (POST body)
# - ... many more
status = 200
headers = { 'Content-Type' => 'text/html' }
body = ['<h1>Hello, World!</h1>']
[status, headers, body]
end
end
# config.ru
run MyApp.new
Building middleware:
# Middleware wraps another app
class Logger
def initialize(app)
@app = app
end
def call(env)
puts "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
start = Time.now
status, headers, body = @app.call(env)
puts "Completed in #{Time.now - start}s"
[status, headers, body]
end
end
# Stack middleware:
use Logger
run MyApp.new
Questions to guide your implementation:
- What’s in the
envhash? (Usepp envto explore) - How do you parse query parameters and POST bodies?
- How do you set and read cookies?
- How do you implement session storage (in-memory first, then cookies)?
Learning milestones:
- Basic request/response works → You understand the Rack interface
- You parse GET and POST parameters → You understand HTTP message formats
- Sessions persist across requests → You understand cookies/state management
- You compose multiple middleware → You understand the middleware pattern
Project 2: Build a Minimal MVC Framework (Understanding Rails)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 3: Advanced
- Knowledge Area: Web Framework Design / MVC Pattern
- Software or Tool: Rack, ERB
- Main Book: “Rebuilding Rails” by Noah Gibbs
What you’ll build: A mini Rails-like framework with routing, controllers, views (ERB), a basic ORM, and the convention-over-configuration philosophy.
Why it teaches Rails internals: “The most effective way to really understand how things work in Rails is to rebuild it from scratch.” This project demystifies Rails magic by implementing it yourself.
Core challenges you’ll face:
- Routing DSL → maps to parsing route definitions, matching requests
- Controller dispatch → maps to instantiating controllers, calling actions
- View rendering → maps to ERB templates, instance variable passing
- Convention over Configuration → maps to inferring names, locations
Resources for key challenges:
- mkdev - Build a Minimal Ruby MVC Framework
- Rebuilding Rails (book)
- Building an MVC Framework with Ruby
Key Concepts:
- MVC Pattern: “Patterns of Enterprise Application Architecture” by Martin Fowler
- DSL Design: “Metaprogramming Ruby 2” by Paolo Perrotta
- Template Engines: Ruby ERB documentation
Difficulty: Advanced Time estimate: 3-4 weeks Prerequisites: Project 1, Ruby metaprogramming basics
Real world outcome:
# Your framework in action:
# config/routes.rb
MyFramework.routes do
get '/', to: 'home#index'
get '/users', to: 'users#index'
get '/users/:id', to: 'users#show'
post '/users', to: 'users#create'
end
# app/controllers/users_controller.rb
class UsersController < MyFramework::Controller
def index
@users = User.all
render :index
end
def show
@user = User.find(params[:id])
render :show
end
end
# app/views/users/index.html.erb
<h1>Users</h1>
<% @users.each do |user| %>
<p><%= user.name %></p>
<% end %>
# It works!
$ curl http://localhost:9292/users/42
<h1>User Profile</h1>
<p>Name: Alice</p>
Implementation Hints:
Router design:
class Router
def initialize
@routes = []
end
def get(path, to:)
controller, action = to.split('#')
@routes << {
method: 'GET',
path: compile_path(path), # Convert /users/:id to regex
controller: controller,
action: action
}
end
def match(request)
@routes.find do |route|
route[:method] == request.method &&
route[:path].match?(request.path)
end
end
private
def compile_path(path)
# /users/:id → /users/(?<id>[^/]+)
regex_str = path.gsub(/:(\w+)/, '(?<\1>[^/]+)')
Regexp.new("^#{regex_str}$")
end
end
Controller base class:
class Controller
attr_reader :request, :params
def initialize(request, params)
@request = request
@params = params
end
def render(template)
# Find template file based on controller name and template
controller_name = self.class.name.gsub('Controller', '').downcase
path = "app/views/#{controller_name}/#{template}.html.erb"
# Pass instance variables to template
erb = ERB.new(File.read(path))
erb.result(binding) # `binding` passes all instance vars!
end
end
Questions to guide your implementation:
- How do you extract
:idfrom/users/42and put it inparams? - How does
renderfind the right template file? - How do instance variables from controller become available in view?
- How would you implement
before_actioncallbacks?
Learning milestones:
- Routes match and dispatch → You understand routing
- Controllers render views → You understand MVC flow
- ERB templates work with variables → You understand view binding
- Conventions reduce configuration → You understand Rails philosophy
Project 3: Build an ORM (Understanding ActiveRecord)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 4: Expert
- Knowledge Area: ORM Design / Database Patterns
- Software or Tool: SQLite, Ruby
- Main Book: “Patterns of Enterprise Application Architecture” by Martin Fowler
What you’ll build: A minimal ORM implementing the Active Record pattern—table-to-class mapping, CRUD operations, dynamic finders, associations (belongs_to, has_many), and basic validations.
Why it teaches ActiveRecord: “The cost of understanding ORM is much higher than simply making use of it.” Building one forces you to understand every abstraction.
Core challenges you’ll face:
- Table-to-class mapping → maps to inferring table names, column types
- Dynamic method generation → maps to attribute accessors, finders
- Query building → maps to chainable where/order/limit
- Associations → maps to belongs_to, has_many implementation
Key Concepts:
- Active Record Pattern: “Patterns of Enterprise Application Architecture” Chapter 10 - Fowler
- Metaprogramming: “Metaprogramming Ruby 2” by Paolo Perrotta
- SQL Basics: “Learning SQL” by Alan Beaulieu
Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 1-2, SQL knowledge, Ruby metaprogramming
Real world outcome:
# Your ORM in action:
class User < MyORM::Base
has_many :posts
belongs_to :company
validates :email, presence: true, format: /@/
end
class Post < MyORM::Base
belongs_to :user
end
# CRUD operations
user = User.create(name: 'Alice', email: 'alice@example.com')
user.update(name: 'Alice Smith')
# Dynamic finders
user = User.find_by_email('alice@example.com')
users = User.where(active: true).order(:name).limit(10)
# Associations
user.posts # → [Post, Post, ...]
post.user # → User
# Validations
user = User.new(email: 'invalid')
user.valid? # → false
user.errors # → { email: ['is invalid'] }
Implementation Hints:
Base class with table inference:
module MyORM
class Base
class << self
def table_name
# User → users, BlogPost → blog_posts
name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase + 's'
end
def columns
@columns ||= db.execute("PRAGMA table_info(#{table_name})").map do |row|
{ name: row['name'], type: row['type'] }
end
end
def find(id)
row = db.execute("SELECT * FROM #{table_name} WHERE id = ?", id).first
row ? new(row) : nil
end
def all
db.execute("SELECT * FROM #{table_name}").map { |row| new(row) }
end
# Dynamic finders via method_missing
def method_missing(name, *args)
if name.to_s.start_with?('find_by_')
column = name.to_s.sub('find_by_', '')
where(column.to_sym => args.first).first
else
super
end
end
end
def initialize(attributes = {})
attributes.each { |k, v| send("#{k}=", v) }
end
end
end
Generating attribute accessors dynamically:
class Base
def self.inherited(subclass)
# When a class inherits from Base, set up its attributes
subclass.instance_eval do
columns.each do |col|
attr_accessor col[:name].to_sym
end
end
end
end
Chainable query builder:
class Relation
def initialize(klass)
@klass = klass
@where_clauses = []
@order_clause = nil
@limit_value = nil
end
def where(conditions)
conditions.each { |k, v| @where_clauses << "#{k} = '#{v}'" }
self # Return self for chaining
end
def order(column)
@order_clause = column
self
end
def limit(n)
@limit_value = n
self
end
def to_sql
sql = "SELECT * FROM #{@klass.table_name}"
sql += " WHERE #{@where_clauses.join(' AND ')}" if @where_clauses.any?
sql += " ORDER BY #{@order_clause}" if @order_clause
sql += " LIMIT #{@limit_value}" if @limit_value
sql
end
def to_a
@klass.db.execute(to_sql).map { |row| @klass.new(row) }
end
# Delegate enumerable methods
def each(&block)
to_a.each(&block)
end
end
Learning milestones:
- Basic CRUD works → You understand SQL generation
- Dynamic finders work → You understand
method_missing - Chainable queries work → You understand lazy evaluation
- Associations work → You understand the Active Record pattern fully
Project 4: Ruby Metaprogramming Deep Dive (The “Magic”)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 3: Advanced
- Knowledge Area: Ruby Internals / Metaprogramming
- Software or Tool: Ruby, IRB
- Main Book: “Metaprogramming Ruby 2” by Paolo Perrotta
What you’ll build: A collection of Ruby metaprogramming exercises that recreate Rails idioms: attr_accessor, concerns, DSLs like has_many, and method hooks.
Why it teaches Rails magic: When you understand that Rails magic is “just” define_method, method_missing, and class_eval, you can debug anything and extend Rails confidently.
Core challenges you’ll face:
- Understanding self → maps to class context vs instance context
- Dynamic method definition → maps to define_method, class_eval
- Method interception → maps to method_missing, prepend
- DSL creation → maps to instance_eval, block handling
Key Concepts:
- Ruby Object Model: “Metaprogramming Ruby 2” Chapters 1-3 - Perrotta
- Closures and Bindings: “The Well-Grounded Rubyist” Chapter 6 - Black
- Method Lookup: “Metaprogramming Ruby 2” Chapter 2 - Perrotta
Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Ruby fundamentals, comfort with blocks/procs
Real world outcome:
# You'll implement these from scratch:
# 1. Your own attr_accessor
class Person
my_attr_accessor :name, :age
end
p = Person.new
p.name = "Alice"
p.name # → "Alice"
# 2. Your own has_many/belongs_to
class User
my_has_many :posts
end
class Post
my_belongs_to :user
end
user.posts # works!
post.user # works!
# 3. Your own ActiveSupport::Concern
module Taggable
extend MyConcern
included do
has_many :tags
end
def tag_names
tags.map(&:name)
end
end
# 4. Your own before_action
class MyController
my_before_action :authenticate!, only: [:create, :update]
end
Implementation Hints:
Implementing attr_accessor:
class Module
def my_attr_accessor(*names)
names.each do |name|
# Define getter
define_method(name) do
instance_variable_get("@#{name}")
end
# Define setter
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
end
end
end
end
Implementing has_many:
class ActiveRecordBase
def self.my_has_many(association_name)
# has_many :posts → defines posts method
define_method(association_name) do
# Infer class: :posts → Post
klass = association_name.to_s.singularize.capitalize.constantize
# Infer foreign key: User → user_id
foreign_key = "#{self.class.name.downcase}_id"
# Query
klass.where(foreign_key => id)
end
end
end
Understanding self in different contexts:
class Foo
puts self # → Foo (class context)
def bar
puts self # → #<Foo:0x...> (instance context)
end
class << self
puts self # → #<Class:Foo> (singleton class)
end
end
Method lookup chain:
# Ruby looks for methods in this order:
# 1. Singleton class (eigenclass)
# 2. Prepended modules
# 3. The class itself
# 4. Included modules
# 5. Superclass (repeat 1-4)
# 6. BasicObject
# 7. method_missing
Learning milestones:
- You implement attr_accessor → You understand define_method
- You implement has_many → You understand class-level DSLs
- You implement concerns → You understand module hooks
- You can explain self in any context → You understand Ruby’s object model
Project 5: Rails from Scratch (Putting It Together)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby
- Alternative Programming Languages: N/A
- Coolness Level: Level 5: Pure Magic
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 4: Expert
- Knowledge Area: Full Web Framework Implementation
- Software or Tool: Ruby, Rack, SQLite
- Main Book: “Rebuilding Rails” by Noah Gibbs
What you’ll build: Combine Projects 1-4 into a complete mini-framework: Rack foundation, routing, controllers, views, ORM, migrations, generators, and a CLI.
Why this is the ultimate Rails-understanding project: You’ll have built every major component of Rails yourself. Nothing will be “magic” anymore.
Core challenges you’ll face:
- Integrating all components → maps to making pieces work together
- Adding migrations → maps to schema versioning
- Building generators → maps to code generation
- Creating a CLI → maps to command-line interface design
Key Concepts:
- Integration: How components communicate
- CLI Design: Thor gem or custom
- Code Generation: Template-based generators
Difficulty: Expert Time estimate: 4-6 weeks Prerequisites: Projects 1-4 completed
Real world outcome:
# Your framework works like Rails!
$ ./bin/myframework new blog
Creating new application: blog
create blog/
create blog/app/controllers/
create blog/app/models/
create blog/app/views/
create blog/config/routes.rb
create blog/db/
create blog/Gemfile
Done!
$ cd blog
$ ./bin/myframework generate model Post title:string body:text
create app/models/post.rb
create db/migrate/20240101_create_posts.rb
$ ./bin/myframework db:migrate
Migrating...
20240101_create_posts.rb
Done!
$ ./bin/myframework server
Starting server on http://localhost:3000
Implementation Hints:
CLI structure:
# bin/myframework
#!/usr/bin/env ruby
require_relative '../lib/my_framework'
command = ARGV[0]
case command
when 'new'
MyFramework::Generators::AppGenerator.new(ARGV[1]).run
when 'generate', 'g'
type = ARGV[1] # 'model', 'controller', etc.
MyFramework::Generators.run(type, ARGV[2..])
when 'server', 's'
MyFramework::Server.start
when 'db:migrate'
MyFramework::Migrator.run
end
Migration system:
class Migrator
def self.run
pending_migrations.each do |migration_file|
require migration_file
# File: 20240101_create_posts.rb defines CreatePosts
class_name = File.basename(migration_file, '.rb')
.split('_')[1..].map(&:capitalize).join
klass = Object.const_get(class_name)
klass.new.up
record_migration(migration_file)
end
end
def self.pending_migrations
all_migrations - completed_migrations
end
end
Autoloading (simplified Zeitwerk):
# When a constant is missing, try to load it
def Object.const_missing(name)
# User → app/models/user.rb
path = name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
require "app/models/#{path}"
const_get(name)
rescue LoadError
super
end
Learning milestones:
- New app generator works → You understand project structure
- Migrations create tables → You understand schema management
- Server runs your app → You’ve integrated everything
- You can build a blog → Your framework is functional!
Project 6: Compare by Building (Django-style Framework)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Python
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 3: Advanced
- Knowledge Area: Framework Comparison / Python
- Software or Tool: Python, WSGI
- Main Book: “Two Scoops of Django” by Greenfeld & Roy
What you’ll build: A minimal Django-like framework in Python—WSGI interface, URL routing, views (functions or classes), templates, and ORM—to understand Django’s design choices.
Why it teaches framework design: Django makes different choices than Rails. Building a Django-style framework reveals why those choices exist and their trade-offs.
Core challenges you’ll face:
- WSGI vs Rack → maps to similar concepts, different syntax
- Explicit over implicit → maps to Django’s philosophy vs Rails’ CoC
- URLconf routing → maps to URL patterns and view dispatch
- Template language → maps to Django’s sandboxed templates vs ERB
Key Concepts:
- WSGI: PEP 3333
- Django Philosophy: “Two Scoops of Django” Introduction
- Python ORM patterns: Django ORM documentation
Difficulty: Advanced Time estimate: 3 weeks Prerequisites: Projects 1-5, Python basics
Real world outcome:
# Your Django-style framework:
# urls.py
urlpatterns = [
path('', views.home, name='home'),
path('users/', views.user_list, name='user-list'),
path('users/<int:id>/', views.user_detail, name='user-detail'),
]
# views.py
def user_list(request):
users = User.objects.all()
return render(request, 'users/list.html', {'users': users})
def user_detail(request, id):
user = User.objects.get(id=id)
return render(request, 'users/detail.html', {'user': user})
# models.py
class User(Model):
name = CharField(max_length=100)
email = EmailField()
class Meta:
db_table = 'users'
Implementation Hints:
WSGI application:
def application(environ, start_response):
# environ is like Rack's env hash
path = environ['PATH_INFO']
method = environ['REQUEST_METHOD']
# Match URL to view
view, kwargs = router.match(path)
request = Request(environ)
response = view(request, **kwargs)
start_response(response.status, response.headers)
return [response.body.encode()]
Key differences to note:
Rails (Ruby) Django (Python)
────────────────────────────── ──────────────────────────────
Convention over Configuration Explicit is better than implicit
routes.rb (DSL) urls.py (Python code)
Controllers are classes Views are functions (or classes)
ERB (full Ruby access) Templates (sandboxed, limited)
Implicit rendering Explicit return render()
Migrations auto-detect Migrations from makemigrations
belongs_to :user user = ForeignKey(User, ...)
Learning milestones:
- WSGI app works → You understand Python’s web interface
- URL routing works → You see Django’s pattern system
- Templates work → You understand sandboxed templates
- ORM works → You see explicit field definitions
Project 7: Compare by Building (Laravel-style in PHP)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: PHP
- Alternative Programming Languages: N/A
- Coolness Level: Level 3: Genuinely Clever
- Business Potential: 2. The “Micro-SaaS / Pro Tool”
- Difficulty: Level 3: Advanced
- Knowledge Area: Framework Comparison / PHP
- Software or Tool: PHP 8+, Composer
- Main Book: “Laravel: Up & Running” by Matt Stauffer
What you’ll build: A minimal Laravel-like framework—routing, controllers, Blade-like templates, Eloquent-like ORM—to understand Laravel’s refinements on Rails ideas.
Why it teaches framework evolution: Laravel took Rails ideas and refined them for PHP developers. Understanding Laravel’s choices shows how frameworks evolve.
Core challenges you’ll face:
- PHP’s unique characteristics → maps to request lifecycle, autoloading
- Facades → maps to static-like access to services
- Blade templates → maps to compilation-based templates
- Eloquent → maps to Active Record with modern PHP
Key Concepts:
- PHP Autoloading: PSR-4 standard
- Service Container: Dependency injection
- Laravel Design: “Laravel: Up & Running” Chapters 1-3
Difficulty: Advanced Time estimate: 3 weeks Prerequisites: Projects 1-5, PHP basics
Real world outcome:
// Your Laravel-style framework:
// routes/web.php
Route::get('/', [HomeController::class, 'index']);
Route::resource('users', UserController::class);
// app/Controllers/UserController.php
class UserController extends Controller
{
public function index()
{
$users = User::all();
return view('users.index', ['users' => $users]);
}
public function show($id)
{
$user = User::findOrFail($id);
return view('users.show', ['user' => $user]);
}
}
// app/Models/User.php
class User extends Model
{
protected $fillable = ['name', 'email'];
public function posts()
{
return $this->hasMany(Post::class);
}
}
Implementation Hints:
Service Container (simplified):
class Container {
private array $bindings = [];
public function bind(string $abstract, callable $concrete) {
$this->bindings[$abstract] = $concrete;
}
public function make(string $abstract) {
if (isset($this->bindings[$abstract])) {
return $this->bindings[$abstract]($this);
}
// Auto-resolve via reflection
return $this->resolve($abstract);
}
private function resolve(string $class) {
$reflection = new ReflectionClass($class);
$constructor = $reflection->getConstructor();
// Recursively resolve dependencies...
}
}
Facades (static access to container):
class Facade {
protected static Container $container;
public static function __callStatic($method, $args) {
$instance = static::$container->make(static::getFacadeAccessor());
return $instance->$method(...$args);
}
}
class DB extends Facade {
protected static function getFacadeAccessor() {
return 'database';
}
}
// Usage: DB::table('users')->get()
// Actually: $container->make('database')->table('users')->get()
Learning milestones:
- Router works → You understand PHP’s request handling
- Service container works → You understand DI containers
- Eloquent ORM works → You see modern PHP ORM design
- You compare with Rails → You understand trade-offs
Project 8: Compare by Building (Phoenix-style in Elixir)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Elixir
- Alternative Programming Languages: N/A
- Coolness Level: Level 5: Pure Magic
- Business Potential: 1. The “Resume Gold”
- Difficulty: Level 4: Expert
- Knowledge Area: Functional Web Frameworks / Concurrency
- Software or Tool: Elixir, Phoenix, Ecto
- Main Book: “Programming Phoenix” by Chris McCord
What you’ll build: A minimal Phoenix-like framework understanding—Plug (like Rack), Router, Controllers, Templates (EEx), and Ecto (repository pattern ORM).
Why it teaches modern framework design: Phoenix represents a different paradigm—functional programming, immutability, and the BEAM’s concurrency model. It shows how to rethink Rails ideas for a functional language.
Core challenges you’ll face:
- Functional programming → maps to immutability, pipelines
- Plug middleware → maps to Elixir’s Rack equivalent
- Ecto’s Repository pattern → maps to different ORM approach than Active Record
- Concurrency model → maps to actors, processes, fault tolerance
Key Concepts:
- Elixir Basics: “Programming Elixir” by Dave Thomas
- Phoenix Architecture: “Programming Phoenix” by McCord et al.
- Ecto: “Programming Ecto” by Darin Wilson
Difficulty: Expert Time estimate: 4 weeks Prerequisites: Projects 1-7, functional programming concepts
Real world outcome:
# Your understanding of Phoenix patterns:
# Router
scope "/", MyApp do
pipe_through :browser
get "/", PageController, :index
resources "/users", UserController
end
# Controller
defmodule MyApp.UserController do
use MyApp, :controller
def index(conn, _params) do
users = Accounts.list_users()
render(conn, :index, users: users)
end
def show(conn, %{"id" => id}) do
user = Accounts.get_user!(id)
render(conn, :show, user: user)
end
end
# Context (not controller!)
defmodule MyApp.Accounts do
def list_users do
Repo.all(User)
end
def get_user!(id) do
Repo.get!(User, id)
end
def create_user(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
end
Key differences from Rails:
Rails (Ruby, OOP) Phoenix (Elixir, FP)
────────────────────────────── ──────────────────────────────
ActiveRecord (Active Record) Ecto (Repository pattern)
user.save Repo.insert(changeset)
Mutable objects Immutable data
@user in controller/view Explicit assigns
Classes with state Modules with functions
Threads (limited) Millions of processes
Rails.cache ETS tables
ActionCable (bolted on) Channels (built-in, first-class)
Implementation Hints:
Plug (Elixir’s Rack):
defmodule MyPlug do
def init(opts), do: opts
def call(conn, _opts) do
conn
|> put_resp_content_type("text/html")
|> send_resp(200, "<h1>Hello!</h1>")
end
end
Ecto changeset (validation):
defmodule User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :email])
|> validate_required([:name, :email])
|> validate_format(:email, ~r/@/)
|> unique_constraint(:email)
end
end
# Usage: Repo.insert(User.changeset(%User{}, params))
Learning milestones:
- Plug pipeline works → You understand functional middleware
- Ecto queries work → You understand Repository pattern
- Channels work → You understand real-time in functional style
- You appreciate the differences → You can choose the right tool
Project 9: Real-Time Features (Action Cable Deep Dive)
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby (Rails)
- Alternative Programming Languages: JavaScript (client)
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 3: Advanced
- Knowledge Area: WebSockets / Real-Time Web
- Software or Tool: Rails, Redis, JavaScript
- Main Book: “The Rails 5 Way” by Obie Fernandez
What you’ll build: A real-time chat application diving deep into Action Cable internals—understanding WebSocket connections, channels, pub/sub, and broadcasting.
Why it teaches real-time web: WebSockets are essential for modern apps. Understanding Action Cable teaches you how Rails integrates real-time features.
Core challenges you’ll face:
- WebSocket lifecycle → maps to connection, subscription, messages
- Pub/Sub pattern → maps to Redis, broadcasting
- JavaScript client → maps to consumer, subscriptions
- Authentication → maps to identifying connections
Key Concepts:
- WebSocket Protocol: RFC 6455
- Action Cable: Rails Guides
- Redis Pub/Sub: Redis documentation
Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Rails basics, JavaScript
Real world outcome:
# Chat room with Action Cable:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
def receive(data)
message = Message.create!(
user: current_user,
room: params[:room],
content: data['message']
)
ActionCable.server.broadcast(
"chat_#{params[:room]}",
message: render_message(message)
)
end
private
def render_message(message)
ApplicationController.render(
partial: 'messages/message',
locals: { message: message }
)
end
end
# In browser console:
subscription = App.cable.subscriptions.create(
{ channel: "ChatChannel", room: "general" },
{
received(data) {
document.getElementById('messages').innerHTML += data.message
}
}
)
subscription.send({ message: "Hello, world!" })
Implementation Hints:
Action Cable architecture:
┌─────────────────────────────────────────────────────────────────┐
│ ACTION CABLE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Browser Rails Server │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ JavaScript Consumer│◄──────▶│ Connection (per ws)│ │
│ │ │ WS │ • identified_by │ │
│ │ • subscriptions │ │ • current_user │ │
│ │ • received(data) │ └─────────┬──────────┘ │
│ │ • send(data) │ │ │
│ └────────────────────┘ │ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Channel (per sub) │ │
│ │ • subscribed │ │
│ │ • unsubscribed │ │
│ │ • stream_from │ │
│ │ • receive │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Redis Pub/Sub │ │
│ │ • Broadcast messages│ │
│ │ • Multi-server sync │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Connection authentication:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
if (user = User.find_by(id: cookies.signed[:user_id]))
user
else
reject_unauthorized_connection
end
end
end
end
Learning milestones:
- Basic chat works → You understand channels
- Multi-room works → You understand streaming
- Works with multiple servers → You understand Redis pub/sub
- Authentication works → You understand connection identification
Project 10: Rails Performance & Internals
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby (Rails)
- Alternative Programming Languages: N/A
- Coolness Level: Level 4: Hardcore Tech Flex
- Business Potential: 3. The “Service & Support” Model
- Difficulty: Level 4: Expert
- Knowledge Area: Performance / Profiling / Internals
- Software or Tool: Rails, rack-mini-profiler, bullet
- Main Book: “The Complete Guide to Rails Performance” by Nate Berkopec
What you’ll build: A performance analysis toolkit—understanding Rails internals through profiling, identifying N+1 queries, memory bloat, and cache strategies.
Why it teaches Rails deeply: Performance problems force you to understand what Rails actually does. You’ll trace through Rails source code and understand every allocation.
Core challenges you’ll face:
- N+1 query detection → maps to understanding eager loading
- Memory profiling → maps to object allocation, GC
- Request profiling → maps to what takes time
- Caching strategies → maps to fragment, Russian doll, low-level
Key Concepts:
- Rails Performance: “The Complete Guide to Rails Performance” - Berkopec
- Ruby GC: “Ruby Performance Optimization” by Alexander Dymo
- Database Optimization: “High Performance MySQL” (concepts apply)
Difficulty: Expert Time estimate: 3 weeks Prerequisites: All previous projects, production Rails experience
Real world outcome:
# Your profiling output:
$ rails performance:analyze
Request: GET /users
Total time: 245ms
Database: 180ms (12 queries - N+1 DETECTED!)
View rendering: 45ms
Ruby: 20ms
N+1 Queries detected:
User.all → then user.posts for each user
Fix: User.includes(:posts).all
Memory allocations: 15,234 objects
String: 8,432
Array: 3,211
Hash: 2,101
User: 500
Top 5 slow methods:
1. ActiveRecord::Result#each (45ms)
2. ERB::Compiler#compile (23ms)
3. ActionView::Template#render (18ms)
4. JSON.parse (12ms)
5. User#full_name (8ms)
Recommendations:
1. Add .includes(:posts) to fix N+1
2. Cache rendered partials
3. Use find_each for large collections
Implementation Hints:
Understanding Rails internals with TracePoint:
# Trace all method calls in a request
trace = TracePoint.new(:call) do |tp|
if tp.defined_class.to_s.start_with?('ActiveRecord')
puts "#{tp.defined_class}##{tp.method_id}"
end
end
trace.enable do
User.find(1)
end
# Output shows every AR method called!
Query analysis:
# See what SQL is generated
ActiveRecord::Base.logger = Logger.new(STDOUT)
# Or analyze queries programmatically
queries = []
callback = ->(name, start, finish, id, payload) {
queries << payload[:sql] if payload[:sql]
}
ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
User.includes(:posts).where(active: true).to_a
end
puts "Queries executed: #{queries.count}"
queries.each { |q| puts q }
Memory profiling:
require 'memory_profiler'
report = MemoryProfiler.report do
1000.times { User.create(name: "Test") }
end
report.pretty_print
# Shows allocated objects by gem, location, class
Learning milestones:
- You can find N+1 queries → You understand eager loading
- You can profile memory → You understand Ruby allocations
- You can trace Rails internals → You understand the source code
- You can optimize real apps → You’re a Rails performance expert
Project Comparison Table
| Project | Difficulty | Time | Depth of Understanding | Fun Factor |
|---|---|---|---|---|
| 1. Rack Application | ⭐⭐ | 1 week | ⚡⚡⚡ | 🎮🎮🎮 |
| 2. Minimal MVC Framework | ⭐⭐⭐ | 3-4 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮 |
| 3. Build an ORM | ⭐⭐⭐⭐ | 3-4 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮 |
| 4. Metaprogramming Deep Dive | ⭐⭐⭐ | 2 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮🎮 |
| 5. Rails from Scratch | ⭐⭐⭐⭐ | 4-6 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮🎮 |
| 6. Django-style (Python) | ⭐⭐⭐ | 3 weeks | ⚡⚡⚡⚡ | 🎮🎮🎮 |
| 7. Laravel-style (PHP) | ⭐⭐⭐ | 3 weeks | ⚡⚡⚡⚡ | 🎮🎮🎮 |
| 8. Phoenix-style (Elixir) | ⭐⭐⭐⭐ | 4 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮🎮 |
| 9. Action Cable Deep Dive | ⭐⭐⭐ | 2 weeks | ⚡⚡⚡⚡ | 🎮🎮🎮🎮 |
| 10. Rails Performance | ⭐⭐⭐⭐ | 3 weeks | ⚡⚡⚡⚡⚡ | 🎮🎮🎮🎮 |
Recommended Learning Path
Your Starting Point
If you’re a Rails user wanting to understand the magic: Projects 1 → 2 → 3 → 4 → 5 (Build from scratch)
If you’re comparing frameworks for a project: Projects 1 → 6 → 7 → 8 (Build similar things in each)
If you’re performance-focused: Projects 1 → 2 → 3 → 10 (Understand then optimize)
Recommended Sequence
Phase 1: Foundation (1-2 weeks)
└── Project 1: Rack Application → Understand the HTTP interface
Phase 2: Rails Core (6-10 weeks)
├── Project 2: MVC Framework → Routing, controllers, views
├── Project 3: ORM → ActiveRecord's secrets
└── Project 4: Metaprogramming → How the magic works
Phase 3: Integration (4-6 weeks)
└── Project 5: Rails from Scratch → Put it all together
Phase 4: Comparison (9-11 weeks)
├── Project 6: Django-style → Python's approach
├── Project 7: Laravel-style → PHP's refinements
└── Project 8: Phoenix-style → Functional paradigm
Phase 5: Mastery (5 weeks)
├── Project 9: Action Cable → Real-time Rails
└── Project 10: Performance → Production optimization
Final Project: Production-Grade Web Application
- File: LEARN_RAILS_WEB_FRAMEWORKS.md
- Main Programming Language: Ruby (Rails)
- Alternative Programming Languages: JavaScript (frontend)
- Coolness Level: Level 5: Pure Magic
- Business Potential: 4. The “Open Core” Infrastructure
- Difficulty: Level 5: Master
- Knowledge Area: Full-Stack Web Development
- Software or Tool: Rails, PostgreSQL, Redis, Sidekiq
- Main Book: “Sustainable Web Development with Ruby on Rails” by David Copeland
What you’ll build: A complete, production-ready SaaS application demonstrating mastery of Rails—authentication, authorization, background jobs, caching, API, real-time features, and deployment.
Why this is the ultimate project: This combines everything: deep Rails knowledge, performance optimization, real-world architecture decisions, and production deployment.
Core features:
- Multi-tenant SaaS with organizations
- Authentication (Devise or custom)
- Authorization (Pundit or custom policies)
- Background jobs (Sidekiq)
- Real-time updates (Action Cable)
- API (RESTful + optionally GraphQL)
- Admin dashboard
- Caching (fragment, Russian doll)
- Full test suite (RSpec)
- CI/CD pipeline
- Production deployment
Difficulty: Master Time estimate: 2-3 months Prerequisites: All 10 projects completed
Real world outcome:
Your SaaS application:
┌─────────────────────────────────────────────────────────────────┐
│ PROJECT MANAGEMENT SAAS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Features: │
│ ✓ Multi-tenant (organization-based) │
│ ✓ User authentication with 2FA │
│ ✓ Role-based permissions │
│ ✓ Real-time project updates │
│ ✓ Background job processing │
│ ✓ RESTful API with documentation │
│ ✓ Comprehensive test coverage (>95%) │
│ ✓ Performance-optimized (<100ms p95) │
│ ✓ Deployed to production (Heroku/AWS) │
│ │
│ Architecture decisions documented: │
│ • Why Rails over Django/Laravel │
│ • Service objects vs fat models │
│ • Caching strategy │
│ • Real-time architecture │
│ │
└─────────────────────────────────────────────────────────────────┘
Essential Resources
Books (Primary)
| Book | Author | Best For |
|---|---|---|
| Rebuilding Rails | Noah Gibbs | Start here for understanding Rails |
| Metaprogramming Ruby 2 | Paolo Perrotta | Ruby’s magic techniques |
| The Rails 7 Way | Obie Fernandez | Comprehensive Rails reference |
| The Complete Guide to Rails Performance | Nate Berkopec | Performance optimization |
| Programming Phoenix | Chris McCord | Phoenix for comparison |
| Two Scoops of Django | Greenfeld & Roy | Django for comparison |
Online Resources
| Resource | URL | Description |
|---|---|---|
| Rails Guides | guides.rubyonrails.org | Official documentation |
| Rails Source | github.com/rails/rails | Read the actual code |
| RubyMonk | rubymonk.com | Metaprogramming tutorials |
| The Odin Project | theodinproject.com | Full Rails curriculum |
| GoRails | gorails.com | Video tutorials |
Comparison Articles
| Resource | URL |
|---|---|
| Matt Schultz Framework Comparison | matt.fyi |
| Rails vs Django vs Laravel | StackShare |
Summary
| # | Project | Main Language | Knowledge Area |
|---|---|---|---|
| 1 | Rack Application | Ruby | Web Servers / HTTP Protocol |
| 2 | Minimal MVC Framework | Ruby | Web Framework Design / MVC |
| 3 | Build an ORM | Ruby | ORM Design / Database Patterns |
| 4 | Metaprogramming Deep Dive | Ruby | Ruby Internals / Metaprogramming |
| 5 | Rails from Scratch | Ruby | Full Web Framework Implementation |
| 6 | Django-style Framework | Python | Framework Comparison / Python |
| 7 | Laravel-style Framework | PHP | Framework Comparison / PHP |
| 8 | Phoenix-style (Elixir) | Elixir | Functional Web Frameworks |
| 9 | Action Cable Deep Dive | Ruby | WebSockets / Real-Time Web |
| 10 | Rails Performance | Ruby | Performance / Profiling |
| Final | Production SaaS | Ruby | Full-Stack Web Development |
Getting Started Checklist
Before starting Project 1:
- Ruby installed (3.0+):
ruby -v - Bundler installed:
gem install bundler - Read Ruby basics if needed (blocks, procs, classes)
- Understand HTTP basics (methods, headers, status codes)
- Install a database (SQLite for learning, PostgreSQL for production)
- Read “Rack basics” section of Rack documentation
Welcome to the world of web framework internals! 🚀
Generated for deep understanding of Ruby on Rails and web framework design