Ruby on Rails Hacking Guide

Ruby on Rails Hacking Guide

Slides for RubyConf India 2014 talk "Ruby on Rails Hacking Guide" http://rubyconfindia.org/2014/

76a777ff80f30bd3b390e275cce625bc?s=128

Akira Matsuda

March 23, 2014
Tweet

Transcript

  1. Ruby on Rails Hacking Guide Akira Matsuda

  2. before

  3. self.name Akira Matsuda != matz

  4. pp self GitHub: amatsuda Twitter: @a_matsuda committer of: Ruby, Rails,

    Haml from: Tokyo, Japan founder of: Asakusa.rb an organizer of: RubyKaigi
  5. amatsuda creator of: kaminari, active_decorator, action_args, html5_validators, hocus_pocus, erd, everywhere,

    i18n_generators, database_rewinder, rfd...
  6. A Rails committer

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

  8. My motivation

  9. Rails.has_many :bugs I often hit them in my production apps

    I need to x them I need to understand the code
  10. 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
  11. 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
  12. 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!
  13. Remove silencers! (Rails.root/con g/initializers/ backtrace_silencers.rb) # Rails.backtrace_cleaner.remove_silencers! => Rails.backtrace_cleaner.remove_silencers!

  14. This talk How Rails works In MY understanding How I

    read the code
  15. begin

  16. Chapters Let's start reding! What is Railties? How Rails server

    boots Rails and Rack middleware Routes Controllers and actions ... and more
  17. Let's start reading! Chapter 1

  18. Ruby on Rails is_a Open-source On GitHub 100% Ruby No

    C extensions!
  19. Source code % git clone rails/rails (Of course you're using

    hub right?)
  20. 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
  21. 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
  22. 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!
  23. Source code % cd rails

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

  25. What's included in the rails gem? When you want to

    know what's included in the gem, take a look at the gemspec
  26. rails.gemspec s.files = ['README.md'] + Dir['guides/**/*']

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

  28. 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
  29. 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
  30. Directories in the rails project % lsd # aliased to

    ls -ld *(-/DN) actionmailer actionpack actionview activemodel activerecord activesupport railties
  31. 7 dependencies You can nd them at the top directory

    in the rails repo
  32. What are these 7 gems? The whole MVC + something

    more
  33. Rails is a full-stack MVC framework All the Rails MVC

    components are in this one repository
  34. MVC View Controller Model

  35. MVC components View ActionView Controller ActionPack Model ActiveModel ActiveRecord

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

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

  38. C - actionpack class ApplicationController < ActionController::Base end

  39. Back to the dependencies list You'll notice one gem that

    is not included in any of the MVC
  40. 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
  41. Back to the dependencies list You'll notice one gem that

    is not included in any of the MVC Which is called railties
  42. 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
  43. What is railties? Chapter 2

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

  45. railties.gemspec s.summary = 'Tools for creating, working with, and running

    Rails applications.' s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.'
  46. railties.gemspec s.files = Dir['CHANGELOG.md', 'README.rdoc', 'RDOC_MAIN.rdoc', 'bin/**/*', 'lib/**/{*,.[a-z]*}'] s.executables =

    ['rails']
  47. railties/lib % cd railties/lib

  48. ls % ls rails rails.rb

  49. rails.rb The le that will be required when you `require

    'rails'` Let's start reading it!
  50. rails.rb require 'rails/application' module Rails ... end

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

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

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

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

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

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

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

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

    that includes Rails::Initializable
  59. rails/initializable.rb module Rails module Initializable class Initializer ... ennnd

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

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

  62. What we learned Railties de nes these core classes MyApp::Application

    < Application < Engine < Railtie < Initializable
  63. Railties is the core of Rails View ActionView Controller ActionPack

    Model ActiveModel ActiveRecord Railties
  64. Plugin? Engine < Plugin (until 3.2) Gone since Rails 4

  65. Pro tip: Tools to read it through Vim + unite.vim

    (unite-outline) rdefs RubyMine
  66. What is railties? It's a library providing something underneath the

    Rails Application Another answer is...
  67. Railroad tie According to WIKIPEDIA... http://en.wikipedia.org/wiki/ Railroad_tie

  68. Railroad tie "a rectangular support for the rails in railroad

    tracks" "Generally laid perpendicular to the rails" "Railroad ties were traditionally made of wood"
  69. Railroad tie "railroad tie/railway tie/crosstie (North America), or railway sleeper

    (Europe, Australia & Asia)"
  70. Railroad tie Applications Rails Railties http://ja.wikipedia.org/wiki/৽װઢ

  71. How is Railties actually used?

  72. 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:
  73. Railties in Rails % vim **/*railtie.rb

  74. 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
  75. We see Railtie everywhere! View ActionView Controller ActionPack Model ActiveModel

    ActiveRecord Railties Railtie Railtie Railtie
  76. 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
  77. What's written in a Railtie? Mainly we see two kinds

    of class method calls, con g and initializer
  78. What is con g?

  79. What is con g? (railtie/railtie.rb) module Rails class Railtie def

    config @config ||= Railtie::Configuration.new ennnd
  80. What is con g? It's an instance of Railtie::Con guration

  81. 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
  82. Railtie::Con guration? It accepts any method call and stores the

    given method name & value in a class level Hash as {method_name: value}
  83. What is initializer?

  84. 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
  85. What is initializer? It just holds the given block as

    a Proc instance (with a name and options)
  86. What Railtie is doing Sets some values via con g.foobar

    = 'baz' Stores Procs given to initializer method calls
  87. One more thing about Rails::Railtie (rails/railtie.rb) module Rails class Railtie

    ... class << self private :new
  88. 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?
  89. Why is it a class despite you can't instantiate? In

    order to be able to be inherited
  90. 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
  91. What's gonna happen when Rails::Railtie is inherited? Rails::Railtie memorizes the

    children classes
  92. We see Railtie everywhere! View ActionView Controller ActionPack Model ActiveModel

    ActiveRecord Railties Railtie Railtie Railtie
  93. Summary: What is railties? Rails is Railties, all is Railties,

    so Railties is all
  94. How Rails server boots Chapter 3

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

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

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

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

  99. Rails.root/con g/boot.rb Sets up gems listed in the Gem le

    Tweaks the $LOAD_PATH (Bundler)
  100. rails/commands.rb require 'rails/commands/commands_tasks' Rails::CommandsTasks.new(ARGV).run_command!(command)

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

    given name
  102. 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
  103. CommandsTasks#server Requires "rails/commands/ server" Requires the application Starts the server

  104. rails/commands/server.rb module Rails class Server < ::Rack::Server def initialize(*) super

    ENV["RAILS_ENV"] ||= options[:environment] end def start super ennnd
  105. Rails::Server#start Calls ::Rack::Server#start

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

  107. 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
  108. Anyway, Rack nally executes something like this

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

    0
  110. Rack::Builder.new module Rack class Builder def initialize(default_app = nil,&block) ...

    instance_eval(&block) if block_given? sennnd
  111. Rails.root/con g.ru require ::File.expand_path('../config/environment', __FILE__) run Rails.application

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

  113. Pro tip: `puts caller` Find it difficult to follow the

    method calls? Add `puts caller` to con g.ru le and start the server.
  114. 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
  115. `puts caller` Then you'll get an output like this...

  116. `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 `<main>' #{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 `<top (required)>' ./bin/rails:8:in `require' ./bin/rails:8:in `<main>'
  117. Pro tip: `puts caller` Remember to use caller when you

    get lost in the method call hell.
  118. Rails.root/con g/ environment.rb require File.expand_path('../application', __FILE__) Rails.application.initialize!

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

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

    < Rails::Application end end
  121. Rails.root/con g/ application.rb Requires 'rails/all' Requires bundled gems De nes

    Rails.application
  122. 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
  123. 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'
  124. */railtie.rb De nes con gs and initializers, as we have

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

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

    ... run_initializers(group, self) ... ennnd
  127. Rails.application.initialize! Calls run_initializers

  128. 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
  129. How the initializers are run? Calls #run method for each

    Initializer object in the initializers collection
  130. Initializer#run (rails/initializable.rb) module Rails module Initializable class Initializer def run(*args)

    @context.instance_exec(*args, &block) ennnnd
  131. Initializer#run It simply instance_execs the stored Proc in some context

    stored in @context @context is actually the application instance
  132. 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
  133. 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
  134. 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
  135. Who calls Rails::Railtie.initialize? Railtie subclasses such as ActiveRecord::Railtie, ActionPack::Railtle, etc.

  136. 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
  137. Building middleware stack Chapter 4

  138. con g.ru run Rails.application

  139. run Rails.application With this de nition, the Rack server calls

    `Rails.application.call(env)`
  140. Rails.application responds_to :call Rails.application is_a Rack app

  141. 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
  142. Rails::Application#call Calls super The super class is Rails::Engine

  143. 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
  144. Rails::Engine is also a Rack app The call goes to

    app.call
  145. 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
  146. What's app? app is con g.middleware that contains default_middleware_stack and

    endpoint
  147. 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
  148. What is Rails::Con guration::MiddlewareStackProxy? (rails/con guration.rb) module Rails module Configuration

    class MiddlewareStackProxy def initialize @operations = [] ennnnd
  149. What is Rails::Con guration::MiddlewareStackProxy? Basically it's a stack that proxies

    the Rails middleware stack
  150. 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
  151. 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
  152. What's the endpoint? endpoint = routes (is_a ActionDispatch::Routing::Route Set) by

    default
  153. 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"
  154. Routes Chapter 5

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

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

    "foo#bar" end
  157. What is routes? Rails.application.routes.class => ActionDispatch::Routing::RouteSet

  158. 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
  159. 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
  160. What is @router? It's a Journey thing!

  161. 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
  162. De ning routes

  163. 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
  164. routes.draw instance_execs the whole given block with an instance of

    ActionDispatch::Routing::Mapp er
  165. This DSL calls AD::Routing::Mapper#get (Rails.root/con g/routes.rb) Rails.application.routes.draw do get "/hello"

    => "foo#bar" end
  166. 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
  167. AD::Routing::Mapper#get Mapper#get nally goes to Mapper#add_route

  168. 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
  169. 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
  170. Mapper#add_route Finally adds a new Journey::Route to routes Makes a

    Mepper#to_route call
  171. 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
  172. 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
  173. 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
  174. to.respond_to?(:call) ?? Possibly An endpoint can be a Rack app

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

    }
  176. "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
  177. 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
  178. controller.action(action) is a Rack app > FooController.action('bar') => #<Proc:0x007fa63e990408@.../ action_controller/metal.rb:230>

  179. get "/hello" => "foo#bar" Maps "/hello" to a Proc returned

    by FooController.action('bar')
  180. Resolving routes

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

  182. 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
  183. RouteSet#call => Journey::Router#call module ActionDispatch module Routing class RouteSet ...

    def call(env) @router.call(env) ennnnd
  184. Journey::Router#call It's another long journey... Anyway it resolves the route

  185. 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
  186. Controllers and actions Chapter 6

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

    v)
  188. 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
  189. 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
  190. 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
  191. 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
  192. AC::Metal#dispatch Calls AC::Metal#process

  193. 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
  194. Controller#process_action process_action(action_name, *args) is equivalent to send action_name

  195. 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
  196. ... and more Chapter 7

  197. Rails Engines Chapter XX

  198. Composing response body Chapter XX

  199. Rendering views Chapter XX

  200. Helpers Chapter XX

  201. ActiveSupport::Depen dencies and autoload Chapter XX

  202. Callbacks Chapter XX

  203. Querying databases Chapter XX

  204. O / R mapping in ActiveRecord Chapter XX

  205. Maybe next time!

  206. end