Slide 1

Slide 1 text

Ruby on Rails Hacking Guide Akira Matsuda

Slide 2

Slide 2 text

before

Slide 3

Slide 3 text

self.name Akira Matsuda != matz

Slide 4

Slide 4 text

pp self GitHub: amatsuda Twitter: @a_matsuda committer of: Ruby, Rails, Haml from: Tokyo, Japan founder of: Asakusa.rb an organizer of: RubyKaigi

Slide 5

Slide 5 text

amatsuda creator of: kaminari, active_decorator, action_args, html5_validators, hocus_pocus, erd, everywhere, i18n_generators, database_rewinder, rfd...

Slide 6

Slide 6 text

A Rails committer

Slide 7

Slide 7 text

Contributing to Rails 372 commits so far Since 6.years.ago

Slide 8

Slide 8 text

My motivation

Slide 9

Slide 9 text

Rails.has_many :bugs I often hit them in my production apps I need to x them I need to understand the code

Slide 10

Slide 10 text

How could I understand the code? I read it, and read it, and I read it I spent too much time reading and learning the code I don't want all other contributors to repeat that waste of time

Slide 11

Slide 11 text

I spent too much time reading and learning the code Because I learned it without a guide I wish I had a good guide like "Ruby Hacking Guide" by Minero Aoki So I'm speaking and writing

Slide 12

Slide 12 text

How could I hit Rails bug so often? Use Edge Rails In your hobby apps Don't trust Rails Rails should work as you expect Remove silencers!

Slide 13

Slide 13 text

Remove silencers! (Rails.root/con g/initializers/ backtrace_silencers.rb) # Rails.backtrace_cleaner.remove_silencers! => Rails.backtrace_cleaner.remove_silencers!

Slide 14

Slide 14 text

This talk How Rails works In MY understanding How I read the code

Slide 15

Slide 15 text

begin

Slide 16

Slide 16 text

Chapters Let's start reding! What is Railties? How Rails server boots Rails and Rack middleware Routes Controllers and actions ... and more

Slide 17

Slide 17 text

Let's start reading! Chapter 1

Slide 18

Slide 18 text

Ruby on Rails is_a Open-source On GitHub 100% Ruby No C extensions!

Slide 19

Slide 19 text

Source code % git clone rails/rails (Of course you're using hub right?)

Slide 20

Slide 20 text

Pro tip: Use gem-src How can you nd the GH repo for the gem you use? Don't want to manually git- clone every gem that you read or patch? Use gem-src

Slide 21

Slide 21 text

amatsuda/gem-src Installation (as an rbenv plugin) % git clone https://github.com/ amatsuda/gem-src.git ~/.rbenv/plugins/gem-src Con guration % echo "gemsrc_clone_root: ~/ src" >> ~/.gemrc

Slide 22

Slide 22 text

gem-src (a social coder's best friend) Usage % gem i rails #=> automatically git-clone You no more need to memorize the gem author name, or search in GitHub!

Slide 23

Slide 23 text

Source code % cd rails

Slide 24

Slide 24 text

Ingredients of Rails Let's see what's included in the gem

Slide 25

Slide 25 text

What's included in the rails gem? When you want to know what's included in the gem, take a look at the gemspec

Slide 26

Slide 26 text

rails.gemspec s.files = ['README.md'] + Dir['guides/**/*']

Slide 27

Slide 27 text

rails.gemspec This gem doesn't really include any program

Slide 28

Slide 28 text

rails.gemspec s.add_dependency 'activesupport', version s.add_dependency 'actionpack', version s.add_dependency 'actionview', version s.add_dependency 'activemodel', version s.add_dependency 'activerecord', version s.add_dependency 'actionmailer', version s.add_dependency 'railties', version

Slide 29

Slide 29 text

rails.gemspec It has no code, but instead it de nes several dependencies It means that the rails gem is a meta package to install these 7 gems

Slide 30

Slide 30 text

Directories in the rails project % lsd # aliased to ls -ld *(-/DN) actionmailer actionpack actionview activemodel activerecord activesupport railties

Slide 31

Slide 31 text

7 dependencies You can nd them at the top directory in the rails repo

Slide 32

Slide 32 text

What are these 7 gems? The whole MVC + something more

Slide 33

Slide 33 text

Rails is a full-stack MVC framework All the Rails MVC components are in this one repository

Slide 34

Slide 34 text

MVC View Controller Model

Slide 35

Slide 35 text

MVC components View ActionView Controller ActionPack Model ActiveModel ActiveRecord

Slide 36

Slide 36 text

M - activemodel, activerecord class Recipe < ActiveRecord::Base ... end

Slide 37

Slide 37 text

V - actionview app/views/**/*.html.haml

Slide 38

Slide 38 text

C - actionpack class ApplicationController < ActionController::Base end

Slide 39

Slide 39 text

Back to the dependencies list You'll notice one gem that is not included in any of the MVC

Slide 40

Slide 40 text

rails.gemspec s.add_dependency 'activesupport', version s.add_dependency 'actionpack', version s.add_dependency 'actionview', version s.add_dependency 'activemodel', version s.add_dependency 'activerecord', version s.add_dependency 'actionmailer', version s.add_dependency 'railties', version

Slide 41

Slide 41 text

Back to the dependencies list You'll notice one gem that is not included in any of the MVC Which is called railties

Slide 42

Slide 42 text

Summary: Let's start reding! rails gem is a meta package that depends on the whole MVC components There's a gem that is not included in any of the MVC components, which is called railties

Slide 43

Slide 43 text

What is railties? Chapter 2

Slide 44

Slide 44 text

Let's start reading railties Let's quickly walk through the code

Slide 45

Slide 45 text

railties.gemspec s.summary = 'Tools for creating, working with, and running Rails applications.' s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.'

Slide 46

Slide 46 text

railties.gemspec s.files = Dir['CHANGELOG.md', 'README.rdoc', 'RDOC_MAIN.rdoc', 'bin/**/*', 'lib/**/{*,.[a-z]*}'] s.executables = ['rails']

Slide 47

Slide 47 text

railties/lib % cd railties/lib

Slide 48

Slide 48 text

ls % ls rails rails.rb

Slide 49

Slide 49 text

rails.rb The le that will be required when you `require 'rails'` Let's start reading it!

Slide 50

Slide 50 text

rails.rb require 'rails/application' module Rails ... end

Slide 51

Slide 51 text

rails.rb Requires 'rails/application' De nes a top-level module named Rails

Slide 52

Slide 52 text

Requires 'rails/application' Apparently the next thing to read is rails/application.rb

Slide 53

Slide 53 text

rails/application.rb require 'rails/engine' module Rails class Application < Engine ... ennd

Slide 54

Slide 54 text

rails/application.rb Requires 'rails/engine' De nes Rails::Application class inheriting Rails::Engine

Slide 55

Slide 55 text

rails/engine.rb require 'rails/railtie' module Rails class Engine < Railtie ... ennd

Slide 56

Slide 56 text

rails/engine.rb Requires 'rails/railtie' De nes Rails::Engine class inheriting Rails::Railtie

Slide 57

Slide 57 text

rails/railtie.rb require 'rails/initializable' require 'rails/configuration' module Rails class Railtie include Initializable ... ennd

Slide 58

Slide 58 text

rails/railtie.rb Requires 'rails/initializable' Requires 'rails/con guration' De nes Rails::Railtie class that includes Rails::Initializable

Slide 59

Slide 59 text

rails/initializable.rb module Rails module Initializable class Initializer ... ennnd

Slide 60

Slide 60 text

rails/initializable.rb De nes Rails::Initializable De nes Rails::Initializable::Initializer

Slide 61

Slide 61 text

Files we have read rails.rb rails/application.rb rails/engine.rb rails/railtie.rb rails/initializable.rb

Slide 62

Slide 62 text

What we learned Railties de nes these core classes MyApp::Application < Application < Engine < Railtie < Initializable

Slide 63

Slide 63 text

Railties is the core of Rails View ActionView Controller ActionPack Model ActiveModel ActiveRecord Railties

Slide 64

Slide 64 text

Plugin? Engine < Plugin (until 3.2) Gone since Rails 4

Slide 65

Slide 65 text

Pro tip: Tools to read it through Vim + unite.vim (unite-outline) rdefs RubyMine

Slide 66

Slide 66 text

What is railties? It's a library providing something underneath the Rails Application Another answer is...

Slide 67

Slide 67 text

Railroad tie According to WIKIPEDIA... http://en.wikipedia.org/wiki/ Railroad_tie

Slide 68

Slide 68 text

Railroad tie "a rectangular support for the rails in railroad tracks" "Generally laid perpendicular to the rails" "Railroad ties were traditionally made of wood"

Slide 69

Slide 69 text

Railroad tie "railroad tie/railway tie/crosstie (North America), or railway sleeper (Europe, Australia & Asia)"

Slide 70

Slide 70 text

Railroad tie Applications Rails Railties http://ja.wikipedia.org/wiki/৽װઢ

Slide 71

Slide 71 text

How is Railties actually used?

Slide 72

Slide 72 text

Railties in Rails % git grep Rails::Railtie actionmailer/lib/action_mailer/railtie.rb: class Railtie < Rails::Railtie # :nodoc: actionpack/lib/action_controller/railtie.rb: class Railtie < Rails::Railtie #:nodoc: actionpack/lib/action_dispatch/railtie.rb: class Railtie < Rails::Railtie # :nodoc: actionview/lib/action_view/railtie.rb: class Railtie < Rails::Railtie # :nodoc: activemodel/lib/active_model/railtie.rb: class Railtie < Rails::Railtie # :nodoc: activerecord/lib/active_record/railtie.rb: class Railtie < Rails::Railtie # :nodoc: activesupport/lib/active_support/i18n_railtie.rb: class Railtie < Rails::Railtie activesupport/lib/active_support/railtie.rb: class Railtie < Rails::Railtie # :nodoc:

Slide 73

Slide 73 text

Railties in Rails % vim **/*railtie.rb

Slide 74

Slide 74 text

Each gems has their own Railtie inheriting Rails::Railtie module ActionMailer class Railtie < Rails::Railtie module ActionController class Railtie < Rails::Railtie module ActionDispatch class Railtie < Rails::Railtie module ActionView class Railtie < Rails::Railtie module ActiveModel class Railtie < Rails::Railtie module ActiveRecord class Railtie < Rails::Railtie

Slide 75

Slide 75 text

We see Railtie everywhere! View ActionView Controller ActionPack Model ActiveModel ActiveRecord Railties Railtie Railtie Railtie

Slide 76

Slide 76 text

What's written in a Railtie? (active_model/railtie.rb) require "active_model" require "rails" module ActiveModel class Railtie < Rails::Railtie # :nodoc: config.eager_load_namespaces << ActiveModel initializer "active_model.secure_password" do ActiveModel::SecurePassword.min_cost = Rails.env.test? end ennd

Slide 77

Slide 77 text

What's written in a Railtie? Mainly we see two kinds of class method calls, con g and initializer

Slide 78

Slide 78 text

What is con g?

Slide 79

Slide 79 text

What is con g? (railtie/railtie.rb) module Rails class Railtie def config @config ||= Railtie::Configuration.new ennnd

Slide 80

Slide 80 text

What is con g? It's an instance of Railtie::Con guration

Slide 81

Slide 81 text

Railtie::Con guration? (railtie/con guration.rb) module Rails class Railtie class Configuration def method_missing(name, *args, &blk) if name.to_s =~ /=$/ @@options[$`.to_sym] = args.first elsif @@options.key?(name) @@options[name] else super end ennnnd

Slide 82

Slide 82 text

Railtie::Con guration? It accepts any method call and stores the given method name & value in a class level Hash as {method_name: value}

Slide 83

Slide 83 text

What is initializer?

Slide 84

Slide 84 text

What is initializer? (rails/initializable.rb) module Rails module Initializable class Initializer attr_reader :name, :block def initialize(name, context, options, &block) ... @name, @context, @options, @block = name, context, options, block ennd module ClassMethods def initializer(name, opts = {}, &blk) ... initializers << Initializer.new(name, nil, opts, &blk) ennnnd

Slide 85

Slide 85 text

What is initializer? It just holds the given block as a Proc instance (with a name and options)

Slide 86

Slide 86 text

What Railtie is doing Sets some values via con g.foobar = 'baz' Stores Procs given to initializer method calls

Slide 87

Slide 87 text

One more thing about Rails::Railtie (rails/railtie.rb) module Rails class Railtie ... class << self private :new

Slide 88

Slide 88 text

One more thing about Rails::Railtie Prohibited to be instantiated from outside self.instance returns the singleton instance Why is it a class despite you can't instantiate?

Slide 89

Slide 89 text

Why is it a class despite you can't instantiate? In order to be able to be inherited

Slide 90

Slide 90 text

What's gonna happen when Rails::Railtie is inherited? (rails/railtle.rb) module Rails class Railtie class << self def inherited(base) unless base.abstract_railtie? subclasses << base ennnnnd

Slide 91

Slide 91 text

What's gonna happen when Rails::Railtie is inherited? Rails::Railtie memorizes the children classes

Slide 92

Slide 92 text

We see Railtie everywhere! View ActionView Controller ActionPack Model ActiveModel ActiveRecord Railties Railtie Railtie Railtie

Slide 93

Slide 93 text

Summary: What is railties? Rails is Railties, all is Railties, so Railties is all

Slide 94

Slide 94 text

How Rails server boots Chapter 3

Slide 95

Slide 95 text

The "rails" command % rails server Rails.root/bin/rails

Slide 96

Slide 96 text

Rails.root/bin/rails require_relative '../config/boot' require 'rails/commands'

Slide 97

Slide 97 text

Rails.root/bin/rails Requires 'con g/boot' inside the app Requires 'rails/commands'

Slide 98

Slide 98 text

Rails.root/con g/boot.rb require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

Slide 99

Slide 99 text

Rails.root/con g/boot.rb Sets up gems listed in the Gem le Tweaks the $LOAD_PATH (Bundler)

Slide 100

Slide 100 text

rails/commands.rb require 'rails/commands/commands_tasks' Rails::CommandsTasks.new(ARGV).run_command!(command)

Slide 101

Slide 101 text

rails/commands.rb Requires 'rails/commands/ commands_tasks' Invokes a command task with the given name

Slide 102

Slide 102 text

rails/commands/ commands_tasks.rb module Rails class CommandsTasks def server ... require "rails/commands/server" Rails::Server.new.tap do |server| require APP_PATH ... server.start ennnd

Slide 103

Slide 103 text

CommandsTasks#server Requires "rails/commands/ server" Requires the application Starts the server

Slide 104

Slide 104 text

rails/commands/server.rb module Rails class Server < ::Rack::Server def initialize(*) super ENV["RAILS_ENV"] ||= options[:environment] end def start super ennnd

Slide 105

Slide 105 text

Rails::Server#start Calls ::Rack::Server#start

Slide 106

Slide 106 text

rack-1.5.2/lib/rack/server.rb module Rack class Server def start &blk ... ennnd

Slide 107

Slide 107 text

I'm not gonna do rack code reading here But method calls go on like start => wrapped_app => app => build_app_and_options_from_ con g => Rack::Builder.parse_ le => Rack::Builder.new_from_string

Slide 108

Slide 108 text

Anyway, Rack nally executes something like this

Slide 109

Slide 109 text

rack/builder.rb eval "Rack::Builder.new {\n" + ::File.read('config.ru') + "\n}.to_app", TOPLEVEL_BINDING, '(rackup)', 0

Slide 110

Slide 110 text

Rack::Builder.new module Rack class Builder def initialize(default_app = nil,&block) ... instance_eval(&block) if block_given? sennnd

Slide 111

Slide 111 text

Rails.root/con g.ru require ::File.expand_path('../config/environment', __FILE__) run Rails.application

Slide 112

Slide 112 text

Rails.root/con g.ru Requires Rails.root/con g/ environment.rb Runs Rails.application

Slide 113

Slide 113 text

Pro tip: `puts caller` Find it difficult to follow the method calls? Add `puts caller` to con g.ru le and start the server.

Slide 114

Slide 114 text

Rails.root/con g.ru # This file is used by Rack-based servers to start the application. puts caller require ::File.expand_path('../config/environment', __FILE__) run Rails.application

Slide 115

Slide 115 text

`puts caller` Then you'll get an output like this...

Slide 116

Slide 116 text

`puts caller` from con g.ru #{GEM_HOME}/rack-1.5.2/lib/rack/builder.rb:55:in `instance_eval' #{GEM_HOME}/rack-1.5.2/lib/rack/builder.rb:55:in `initialize' #{Rails.root}/config.ru:in `new' #{Rails.root}/config.ru:in `' #{GEM_HOME}/rack-1.5.2/lib/rack/builder.rb:49:in `eval' #{GEM_HOME}/rack-1.5.2/lib/rack/builder.rb:49:in `new_from_string' #{GEM_HOME}/rack-1.5.2/lib/rack/builder.rb:40:in `parse_file' #{GEM_HOME}/rack-1.5.2/lib/rack/server.rb:126:in `build_app_and_options_from_config' #{GEM_HOME}/rack-1.5.2/lib/rack/server.rb:82:in `app' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/server.rb:50:in `app' #{GEM_HOME}/rack-1.5.2/lib/rack/server.rb:163:in `wrapped_app' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/server.rb:130:in `log_to_stdout' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/server.rb:67:in `start' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/commands_tasks.rb:82:in `block in server' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/commands_tasks.rb:77:in `tap' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/commands_tasks.rb:77:in `server' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands/commands_tasks.rb:41:in `run_command!' #{GEM_HOME}/railties-4.1.0.rc1/lib/rails/commands.rb:17:in `' ./bin/rails:8:in `require' ./bin/rails:8:in `'

Slide 117

Slide 117 text

Pro tip: `puts caller` Remember to use caller when you get lost in the method call hell.

Slide 118

Slide 118 text

Rails.root/con g/ environment.rb require File.expand_path('../application', __FILE__) Rails.application.initialize!

Slide 119

Slide 119 text

Rails.root/con g/ environment.rb Requires Rails.root/con g/ application.rb Calls Rails.application.initialize!

Slide 120

Slide 120 text

Rails.root/con g/ application.rb require 'rails/all' Bundler.require(*Rails.groups) module YourApp class Application < Rails::Application end end

Slide 121

Slide 121 text

Rails.root/con g/ application.rb Requires 'rails/all' Requires bundled gems De nes Rails.application

Slide 122

Slide 122 text

rails/all.rb require "rails" %w( active_record action_controller action_view action_mailer rails/test_unit sprockets ).each do |framework| begin require "#{framework}/railtie" rescue LoadError end end

Slide 123

Slide 123 text

rails/all.rb (edited) require 'active_record/railtie' require 'action_controller/railtie' require 'action_view/railtie' require 'action_mailer/railtie' require 'rails/test_unit/railtie' require 'sprockets/railtie'

Slide 124

Slide 124 text

*/railtie.rb De nes con gs and initializers, as we have seen already

Slide 125

Slide 125 text

And then, what happens next is... Rails.application.initialize!

Slide 126

Slide 126 text

Rails.application.initialize! (rails/application.rb) module Rails class Application < Engine def initialize!(group=:default) ... run_initializers(group, self) ... ennnd

Slide 127

Slide 127 text

Rails.application.initialize! Calls run_initializers

Slide 128

Slide 128 text

run_initializers (rails/initializable.rb) module Rails module Initializable def run_initializers(group=:default, *args) ... initializers.tsort_each do |initializer| initializer.run(*args) if initializer.belongs_to?(group) end ... ennd

Slide 129

Slide 129 text

How the initializers are run? Calls #run method for each Initializer object in the initializers collection

Slide 130

Slide 130 text

Initializer#run (rails/initializable.rb) module Rails module Initializable class Initializer def run(*args) @context.instance_exec(*args, &block) ennnnd

Slide 131

Slide 131 text

Initializer#run It simply instance_execs the stored Proc in some context stored in @context @context is actually the application instance

Slide 132

Slide 132 text

All the Initializers (rails/application.rb) module Rails class Application < Engine ... def initializers Bootstrap.initializers_for(self) + railties_initializers(super) + Finisher.initializers_for(self) ennnd

Slide 133

Slide 133 text

What's in the initializers collection? (rails/initializable.rb) module Rails module Initializable module ClassMethods def initializer(name, opts = {}, &blk) ... initializers << Initializer.new(name, nil, opts, &blk) ennnnd

Slide 134

Slide 134 text

What's in the initializers collection? Instances of Rails::Initializable::Initializer Each Initializer just holds a Proc instance (and a name and options) given to Rails::Railtie::Initializable.initial ize method call

Slide 135

Slide 135 text

Who calls Rails::Railtie.initialize? Railtie subclasses such as ActiveRecord::Railtie, ActionPack::Railtle, etc.

Slide 136

Slide 136 text

Summary: How Rails server boots Require all gems in the Gem le (bundler) Load all Railties and Engines (con g/ application.rb) De ne YourApp::Application inheriting Rails::Application (con g/application.rb) Run Railtie#initializer de ned in each Railtie, Engine, and Application Load each Engine's load_path and routes, then load con g/initializers/* Run Rails.application (con g.ru) => The next chapter

Slide 137

Slide 137 text

Building middleware stack Chapter 4

Slide 138

Slide 138 text

con g.ru run Rails.application

Slide 139

Slide 139 text

run Rails.application With this de nition, the Rack server calls `Rails.application.call(env)`

Slide 140

Slide 140 text

Rails.application responds_to :call Rails.application is_a Rack app

Slide 141

Slide 141 text

Rails.application as a Rack application (railties/lib/rails/applicaiton.rb) module Rails class Application ... def call(env) env["ORIGINAL_FULLPATH"] = build_original_fullpath(env) env["ORIGINAL_SCRIPT_NAME"] = env["SCRIPT_NAME"] super(env) ennnd

Slide 142

Slide 142 text

Rails::Application#call Calls super The super class is Rails::Engine

Slide 143

Slide 143 text

Rails::Engine is also a Rack app (railties/lib/rails/engine.rb) module Rails class Engine ... def call(env) env.merge!(env_config) if env['SCRIPT_NAME'] env.merge! "ROUTES_#{routes.object_id}_SCRIPT_NAME" => env['SCRIPT_NAME'].dup end app.call(env) ennnd

Slide 144

Slide 144 text

Rails::Engine is also a Rack app The call goes to app.call

Slide 145

Slide 145 text

What's app? (railties/lib/rails/engine.rb) module Rails class Engine ... def app @app ||= begin config.middleware = config.middleware.merge_into(default_middleware_stack) config.middleware.build(endpoint) end ennnd

Slide 146

Slide 146 text

What's app? app is con g.middleware that contains default_middleware_stack and endpoint

Slide 147

Slide 147 text

What is con g.middleware? (rails/engine/con guration.rb) module Rails class Engine class Configuration < ::Rails::Railtie::Configuration def middleware @middleware ||= Rails::Configuration::MiddlewareStackProxy.new ennnnd

Slide 148

Slide 148 text

What is Rails::Con guration::MiddlewareStackProxy? (rails/con guration.rb) module Rails module Configuration class MiddlewareStackProxy def initialize @operations = [] ennnnd

Slide 149

Slide 149 text

What is Rails::Con guration::MiddlewareStackProxy? Basically it's a stack that proxies the Rails middleware stack

Slide 150

Slide 150 text

app = default_middlewarestack + endpoint (railties/lib/rails/engine.rb) module Rails class Engine ... def app @app ||= begin config.middleware = config.middleware.merge_into(default_middleware_stack) config.middleware.build(endpoint) end ennnd

Slide 151

Slide 151 text

What's the endpoint? (rails/engine.rb) module Rails class Engine < Railtie def endpoint self.class.endpoint || routes end def routes @routes ||= ActionDispatch::Routing::RouteSet.new ... ennnd

Slide 152

Slide 152 text

What's the endpoint? endpoint = routes (is_a ActionDispatch::Routing::Route Set) by default

Slide 153

Slide 153 text

Summary: Building middleware stack The Rack server calls `Rails.application.call` It calls the Rails default middleware stack and the endpoint The endpoint is "routes"

Slide 154

Slide 154 text

Routes Chapter 5

Slide 155

Slide 155 text

Rails.application.routes Usually written in Rails.root/ con g/routes.rb

Slide 156

Slide 156 text

An example routes.rb (Rails.root/con g/routes.rb) Rails.application.routes.draw do get "/hello" => "foo#bar" end

Slide 157

Slide 157 text

What is routes? Rails.application.routes.class => ActionDispatch::Routing::RouteSet

Slide 158

Slide 158 text

Rails.application.routes is also a Rack app (action_dispatch/routing/route_set.rb) module ActionDispatch module Routing class RouteSet ... def call(env) @router.call(env) ennnnd

Slide 159

Slide 159 text

What is @router? (action_dispatch/routing/route_set.rb) module ActionDispatch module Routing class RouteSet ... def initialize(request_class = ActionDispatch::Request) ... @set = Journey::Routes.new @router = Journey::Router.new(@set, { :parameters_key => PARAMETERS_KEY, :request_class => request_class}) @formatter = Journey::Formatter.new @set ennnnd

Slide 160

Slide 160 text

What is @router? It's a Journey thing!

Slide 161

Slide 161 text

Rails.application.routes is also a Rack app This is how Rails.application's endpoint accepts the Rack request But for now, let's go back to routes.draw DSL

Slide 162

Slide 162 text

De ning routes

Slide 163

Slide 163 text

routes.draw (action_dispatch/routing/route_set.rb) module ActionDispatch module Routing class RouteSet def draw(&block) ... eval_block(block) ... end def eval_block(block) ... mapper = Mapper.new(self) mapper.instance_exec(&block) ennnnd

Slide 164

Slide 164 text

routes.draw instance_execs the whole given block with an instance of ActionDispatch::Routing::Mapp er

Slide 165

Slide 165 text

This DSL calls AD::Routing::Mapper#get (Rails.root/con g/routes.rb) Rails.application.routes.draw do get "/hello" => "foo#bar" end

Slide 166

Slide 166 text

AD::Routing::Mapper#get (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper def get(*args, &block) map_method(:get, args, &block) end def map_method(method, args, &block) ... match(*args, options, &block) ... end def match(path, *rest) ... decomposed_match(_path, route_options) end def decomposed_match(path, options) add_route(path, options) ennnnnd

Slide 167

Slide 167 text

AD::Routing::Mapper#get Mapper#get nally goes to Mapper#add_route

Slide 168

Slide 168 text

Mapper#add_route (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper ... def add_route(action, options) ... mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options) app, conditions, requirements, defaults, as, anchor = mapping.to_route @set.add_route(app, conditions, requirements, defaults, as, anchor) ennnnnd

Slide 169

Slide 169 text

add_routes returns a Journey::Route (action_dispatch/journey/routes.rb) module ActionDispatch module Journey class Routes def add_route(app, path, conditions, defaults, name = nil) route = Route.new(name, app, path, conditions, defaults) route.precedence = routes.length routes << route ennnnd

Slide 170

Slide 170 text

Mapper#add_route Finally adds a new Journey::Route to routes Makes a Mepper#to_route call

Slide 171

Slide 171 text

Mapper#to_route => app (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper ... def to_route [ app, conditions, requirements, defaults, options[:as], options[:anchor] ] end def app Constraints.new(endpoint, blocks, @set.request_class) ennnnnnd

Slide 172

Slide 172 text

Constraints.new (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper class Constraints def self.new(app, constraints, request = Rack::Request) if constraints.any? super(app, constraints, request) else app end ennnnnd

Slide 173

Slide 173 text

What is endpoint? (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper class Mapping ... def endpoint to.respond_to?(:call) ? to : dispatcher ennnnnnd

Slide 174

Slide 174 text

to.respond_to?(:call) ?? Possibly An endpoint can be a Rack app or a mounted Engine

Slide 175

Slide 175 text

to.respond_to?(:call) ?? get "/ok" => ->(env) { [200, {}, ["OK"]] }

Slide 176

Slide 176 text

"foo#bar" does not respond_to :call (action_dispatch/routing/mapper.rb) module ActionDispatch module Routing class Mapper class Mapping ... def dispatcher Routing::RouteSet::Dispatcher.new( :defaults => defaults) ennnnnnd

Slide 177

Slide 177 text

Dispatcher is a Rack app (action_dispatch/routing/route_set.rb) module ActionDispatch module Routing class RouteSet class Dispatcher def call(env) ... dispatch(controller, params[:action], env) end def dispatch(controller, action, env) controller.action(action).call(env) ennnnnd

Slide 178

Slide 178 text

controller.action(action) is a Rack app > FooController.action('bar') => #

Slide 179

Slide 179 text

get "/hello" => "foo#bar" Maps "/hello" to a Proc returned by FooController.action('bar')

Slide 180

Slide 180 text

Resolving routes

Slide 181

Slide 181 text

Calls to the router Rails.application.routes.call(env)

Slide 182

Slide 182 text

Rails.application.router module ActionDispatch module Routing class RouteSet def initialize(request_class = ActionDispatch::Request) ... @set = Journey::Routes.new @router = Journey::Router.new(@set, { :parameters_key => PARAMETERS_KEY, :request_class => request_class}) @formatter = Journey::Formatter.new @set ennnnd

Slide 183

Slide 183 text

RouteSet#call => Journey::Router#call module ActionDispatch module Routing class RouteSet ... def call(env) @router.call(env) ennnnd

Slide 184

Slide 184 text

Journey::Router#call It's another long journey... Anyway it resolves the route

Slide 185

Slide 185 text

Summary: Routes Rails.application.routes is a Rack app Each routes' endpoint is a Rack app Each controller's each action is a Rack app e.g. 'foo#bar' becomes a Rack app generated by FooController.action('bar') Everything is a Rack app

Slide 186

Slide 186 text

Controllers and actions Chapter 6

Slide 187

Slide 187 text

What's gonna happen when the server got a Request? controller.action(action).call(en v)

Slide 188

Slide 188 text

controller.action(action).call(env) (action_dispatch/routing/route_set.rb) module ActionDispatch module Routing class RouteSet class Dispatcher def call(env) ... dispatch(controller, params[:action], env) end def dispatch(controller, action, env) controller.action(action).call(env) ennnnnd

Slide 189

Slide 189 text

Creating a Request object, and adding a Proc to the stack (action_controller/metal.rb) module ActionController class Metal < AbstractController::Base def self.action(name, klass = ActionDispatch::Request) middleware_stack.build(name.to_s) do |env| new.dispatch(name, klass.new(env)) end ennnd

Slide 190

Slide 190 text

Creating a Response object (action_controller/metal/rack_delegation.rb) module ActionController module RackDelegation def dispatch(action, request) set_response!(request) super(action, request) end def set_response!(request) @_response = ActionDispatch::Response.new @_response.request = request ennnd

Slide 191

Slide 191 text

AC::Metal#dispatch (action_controller/metal.rb) module ActionController class Metal < AbstractController::Base def dispatch(name, request) @_request = request @_env = request.env @_env['action_controller.instance'] = self process(name) to_a ennnd

Slide 192

Slide 192 text

AC::Metal#dispatch Calls AC::Metal#process

Slide 193

Slide 193 text

Controller#process (abstract_controller/base.rb) module AbstractController class Base def process(action, *args) ... process_action(action_name, *args) end private def process_action(method_name, *args) send_action(method_name, *args) end alias send_action send ennnd

Slide 194

Slide 194 text

Controller#process_action process_action(action_name, *args) is equivalent to send action_name

Slide 195

Slide 195 text

Summary: Controllers and actions The request goes to FooController.action('bar') FooController.action('bar') sets the Rails Request and Response objects to the controller instance Then call goes to FooController#bar via `send` call

Slide 196

Slide 196 text

... and more Chapter 7

Slide 197

Slide 197 text

Rails Engines Chapter XX

Slide 198

Slide 198 text

Composing response body Chapter XX

Slide 199

Slide 199 text

Rendering views Chapter XX

Slide 200

Slide 200 text

Helpers Chapter XX

Slide 201

Slide 201 text

ActiveSupport::Depen dencies and autoload Chapter XX

Slide 202

Slide 202 text

Callbacks Chapter XX

Slide 203

Slide 203 text

Querying databases Chapter XX

Slide 204

Slide 204 text

O / R mapping in ActiveRecord Chapter XX

Slide 205

Slide 205 text

Maybe next time!

Slide 206

Slide 206 text

end