Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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/

Akira Matsuda

March 23, 2014
Tweet

More Decks by Akira Matsuda

Other Decks in Programming

Transcript

  1. Ruby on Rails
    Hacking Guide
    Akira Matsuda

    View Slide

  2. before

    View Slide

  3. self.name
    Akira Matsuda
    != matz

    View Slide

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

    View Slide

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

    View Slide

  6. A Rails committer

    View Slide

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

    View Slide

  8. My motivation

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

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

    View Slide

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

    View Slide

  15. begin

    View Slide

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

    View Slide

  17. Let's start reading!
    Chapter 1

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  23. Source code
    % cd rails

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. MVC
    View
    Controller
    Model

    View Slide

  35. MVC components
    View
    ActionView
    Controller
    ActionPack
    Model
    ActiveModel
    ActiveRecord

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  43. What is railties?
    Chapter 2

    View Slide

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

    View Slide

  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.'

    View Slide

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

    View Slide

  47. railties/lib
    % cd railties/lib

    View Slide

  48. ls
    % ls
    rails rails.rb

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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"

    View Slide

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

    View Slide

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

    View Slide

  71. How is Railties actually used?

    View Slide

  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:

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  78. What is con g?

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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}

    View Slide

  83. What is initializer?

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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?

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  94. How Rails server
    boots
    Chapter 3

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  105. Rails::Server#start
    Calls ::Rack::Server#start

    View Slide

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

    View Slide

  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

    View Slide

  108. Anyway,
    Rack nally executes
    something like this

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  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

    View Slide

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

    View Slide

  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 `'
    #{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 `'

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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'

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  127. Rails.application.initialize!
    Calls run_initializers

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  137. Building
    middleware stack
    Chapter 4

    View Slide

  138. con g.ru
    run Rails.application

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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"

    View Slide

  154. Routes
    Chapter 5

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  162. De ning routes

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  178. controller.action(action) is a
    Rack app
    > FooController.action('bar')
    => #action_controller/metal.rb:230>

    View Slide

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

    View Slide

  180. Resolving routes

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  186. Controllers and
    actions
    Chapter 6

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  192. AC::Metal#dispatch
    Calls AC::Metal#process

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  196. ... and more
    Chapter 7

    View Slide

  197. Rails Engines
    Chapter XX

    View Slide

  198. Composing
    response body
    Chapter XX

    View Slide

  199. Rendering views
    Chapter XX

    View Slide

  200. Helpers
    Chapter XX

    View Slide

  201. ActiveSupport::Depen
    dencies and autoload
    Chapter XX

    View Slide

  202. Callbacks
    Chapter XX

    View Slide

  203. Querying
    databases
    Chapter XX

    View Slide

  204. O / R mapping in
    ActiveRecord
    Chapter XX

    View Slide

  205. Maybe next time!

    View Slide

  206. end

    View Slide