$30 off During Our Annual Pro Sale. View Details »

Ruby MVC from scratch with Rack

DonSchado
August 23, 2014

Ruby MVC from scratch with Rack

Imagine for a while that Rails wouldn't exist. How would we write a MVC app from scratch?

Rack provides a minimal interface for developing web applications in Ruby. In fact it's the solid foundation of all major Ruby powered web frameworks.
During this talk we will dive deep into Rack. We will see the smallest possible Rack Application and learn how it works, by studying Rack internals. We will grow the Application step by step till we implement it in simple MVC style.

Code: https://github.com/DonSchado/Frack-MVC

DonSchado

August 23, 2014
Tweet

More Decks by DonSchado

Other Decks in Programming

Transcript

  1. Ruby MVC from scratch with Rack
    I
    Marco Schaden | @donschado | 23.08.2014
    RedFrog Conf

    View Slide

  2. there was a rack application
    handling 10.000+ req/sec
    once upon a time
    http://www.madebymarket.com/blog/dev/ruby-web-benchmark-report.html

    View Slide

  3. DISCLAIMER
    no live coding: but we write and refactor a lot of code!
    simple codez: examples try to be more obvious than "SOLID"!
    • a newbie will learn some cool tricks!
    • a pro will find a lot to complain ;)!
    !seriously: this presentation contains all the memes, sorry

    View Slide

  4. View Slide

  5. Rack is the foundation of all modern Ruby web frameworks

    View Slide

  6. the future?

    View Slide

  7. (this was less than a week before my talk @froscon)

    View Slide

  8. https://groups.google.com/forum/#!topic/rack-devel/P8oOycVBaH0

    View Slide

  9. no worries

    View Slide

  10. Rack 2.0, or Rack for the Future
    https://gist.github.com/raggi/11c3491561802e573a47
    https://docs.google.com/document/d/

    1Ltwk-NJeJHIZy7yqLi5XBxAnclRqWwOQBOzb4EYJGO4/
    https://gist.github.com/rkh/d99f0bf820de79b8d59b
    https://github.com/tenderlove/the_metal
    http://tenderlovemaking.com/2011/03/03/rack-api-is-awkward.html

    View Slide

  11. status quo

    View Slide

  12. http://chneukirchen.org/talks/euruko-2007/neukirchen07introducingrack.pdf
    • specification!
    • implementation!
    • minimal abstract API for HTTP!
    • Request: CGI environment!
    • Response: status, headers, body
    common interface
    between
    server and application

    View Slide

  13. Write once, run "everywhere"
    WEBrick!
    Thin!
    Puma!
    Unicorn!
    Passenger!
    Torqbox / Torquebox!
    Trinidad!
    Mongrel!
    Pow!
    CGI!
    SCGI!
    FastCGI!
    Ebb!
    Fuzed!
    Litespeed!

    View Slide

  14. RackApp
    +call(env)
    A rack application is any Ruby object that responds to call. !
    !
    It takes the environment hash as argument!
    and returns an array of exactly three values: !
    the status, the headers and the body*!
    !
    *which must respond to each

    View Slide

  15. <<"show"me"the"code">>

    View Slide

  16. !
    ->(env) { }

    View Slide

  17. !
    ->(env) { }
    [ ]

    View Slide

  18. !
    ->(env) { }
    [ ]
    200, {'Content-Type' => 'text/html'}, ['Hello World!']

    View Slide

  19. !
    ->(env) { }
    *run means "call that object for each request"
    run [ ]
    200, {'Content-Type' => 'text/html'}, ['Hello World!']

    View Slide

  20. !
    ->(env) { }
    *run means "call that object for each request"
    # config.ru
    run [ ]
    200, {'Content-Type' => 'text/html'}, ['Hello World!']

    View Slide

  21. !
    ->(env) { }
    $ rackup
    127.0.0.1 - - [23/Aug/2014 10:50:07] "GET / HTTP/1.1" 200 - 0.0007
    [2014-08-23 10:50:00] INFO WEBrick 1.3.1
    [2014-08-23 10:50:00] INFO ruby 2.1.1 (2014-02-24) [x86_64-darwin13.0]
    [2014-08-23 10:50:00] INFO WEBrick::HTTPServer#start: pid=11802 port=9292
    *run means "call that object for each request"
    # config.ru
    run [ ]
    200, {'Content-Type' => 'text/html'}, ['Hello World!']

    View Slide

  22. !
    ->(env) { }
    $ rackup
    127.0.0.1 - - [23/Aug/2014 10:50:07] "GET / HTTP/1.1" 200 - 0.0007
    [2014-08-23 10:50:00] INFO WEBrick 1.3.1
    [2014-08-23 10:50:00] INFO ruby 2.1.1 (2014-02-24) [x86_64-darwin13.0]
    [2014-08-23 10:50:00] INFO WEBrick::HTTPServer#start: pid=11802 port=9292
    *run means "call that object for each request"
    SHIP IT
    # config.ru
    run [ ]
    200, {'Content-Type' => 'text/html'}, ['Hello World!']

    View Slide

  23. , read it: https://github.com/rack/rack
    How does it work?
    seriously

    View Slide

  24. HTML + ERB

    View Slide

  25. # config.ru
    run -> e { [200, {'Content-Type' => 'text/html'}, ['Hello World!']] }

    View Slide

  26. # config.ru
    run -> e { Rack::Response.new('Hello World!') }

    View Slide

  27. # config.ru
    run -> e { Rack::Response.new(view) }

    View Slide

  28. # config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    run -> e { Rack::Response.new(view) }
    view = <<-HTML



    Rack with HTML



    Hello World!



    HTML

    View Slide

  29. # config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    run -> e { Rack::Response.new(view) }
    require 'erb'
    view = <<-HTML



    Rack with



    Hello World!



    HTML
    ERB
    Current time: <%= Time.now %>

    View Slide

  30. # config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    run -> e { Rack::Response.new(view) }
    require 'erb'
    view = <<-HTML



    Rack with



    Hello World!



    HTML
    ERB
    Current time: <%= Time.now %>
    ERB.new(view).result) }

    View Slide

  31. Current time: 2014-08-23 10:54:29 +0200

    View Slide


  32. View Slide

  33. MVC

    View Slide

  34. froscon-rack-mvc

    View Slide

  35. frack-mvc

    View Slide

  36. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru

    View Slide

  37. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    source "https://rubygems.org"
    !
    gem 'rack'
    gem 'thin'
    gem 'tilt'

    View Slide

  38. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru


    Frack MVC


    application#layout
    <%= yield %>


    View Slide

  39. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    users#index

    View Slide

  40. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    # Your code goes here...
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    *use adds a middleware to the rack application stack created by Rack::Builder.

    View Slide

  41. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    Rack::Response.new(env)
    *use adds a middleware to the rack application stack created by Rack::Builder.

    View Slide

  42. ["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"]
    ["SERVER_NAME",."localhost"]["rack.input",.
    #@input=#>]["rack.version",.[1,.
    0]]["rack.errors",.#0x007fc0421e1cc0.@error=#>>]
    ["rack.multithread",.false]["rack.multiprocess",.false]
    ["rack.run_once",.false]["REQUEST_METHOD",."GET"]
    ["REQUEST_PATH",."/"]["PATH_INFO",."/"]["REQUEST_URI",."/"]
    ["HTTP_VERSION",."HTTP/1.1"]["HTTP_USER_AGENT",."curl/
    7.30.0"]["HTTP_HOST",."localhost:9292"]["HTTP_ACCEPT",."*/
    *"]["GATEWAY_INTERFACE",."CGI/1.2"]["SERVER_PORT",."9292"]
    ["QUERY_STRING",.""]["SERVER_PROTOCOL",."HTTP/1.1"]
    ["rack.url_scheme",."http"]["SCRIPT_NAME",.""]
    ["REMOTE_ADDR",."127.0.0.1"]["async.callback",.#Thin::Connection#post_process>]["async.close",.
    #]

    View Slide

  43. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    env

    View Slide

  44. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  45. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  46. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    def render(view)
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  47. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  48. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  49. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  50. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  51. application#layout!
    !
    users#index

    View Slide

  52. application#layout!
    !
    users#index
    but can I haz style?

    View Slide

  53. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'

    View Slide

  54. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    !
    !
    !
    !
    !
    &$ config.ru

    View Slide

  55. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    !
    !
    !
    !
    !
    &$ config.ru
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js

    View Slide

  56. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    html { font-family: sans-serif; }
    h1 { color: #008040; }
    h2 { color: #ff0080; }

    View Slide

  57. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru


    Frack MVC
    !


    application#layout
    <%= yield %>


    View Slide

  58. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru


    Frack MVC
    !


    application#layout
    <%= yield %>



    View Slide

  59. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  60. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    end
    end
    end
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new( )
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    end
    !
    !
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    render 'users/index'
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  61. application#layout
    !
    users#index

    View Slide

  62. application#layout
    !
    users#index
    what about simple routing?

    View Slide

  63. application#layout
    !
    users#index
    what about simple routing?
    and list some users?

    View Slide

  64. ["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"]
    ["SERVER_NAME",."localhost"]["rack.input",.
    #@input=#>]["rack.version",.[1,.
    0]]["rack.errors",.#0x007fc0421e1cc0.@error=#>>]
    ["rack.multithread",.false]["rack.multiprocess",.false]
    ["rack.run_once",.false]["REQUEST_METHOD",."GET"]
    ["REQUEST_PATH",""/"]["PATH_INFO",""/"]["REQUEST_URI",."/"]
    ["HTTP_VERSION",."HTTP/1.1"]["HTTP_USER_AGENT",."curl/
    7.30.0"]["HTTP_HOST",."localhost:9292"]["HTTP_ACCEPT",."*/
    *"]["GATEWAY_INTERFACE",."CGI/1.2"]["SERVER_PORT",."9292"]
    ["QUERY_STRING",.""]["SERVER_PROTOCOL",."HTTP/1.1"]
    ["rack.url_scheme",."http"]["SCRIPT_NAME",.""]
    ["REMOTE_ADDR",."127.0.0.1"]["async.callback",.#Thin::Connection#post_process>]["async.close",.
    #]
    remember env?

    View Slide

  65. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  66. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  67. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    if env['PATH_INFO'] == '/'
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  68. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(&block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    if env['PATH_INFO'] == '/'
    else
    Rack::Response.new('Not found', 404)
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  69. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    !
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    if env['PATH_INFO'] == '/'
    else
    Rack::Response.new('Not found', 404)
    end
    &block)
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  70. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    !
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    if env['PATH_INFO'] == '/'
    else
    Rack::Response.new('Not found', 404)
    end
    @users = ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    &block)
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  71. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    !
    Rack::Response.new(render 'users/index')
    !
    !
    !
    end
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    if env['PATH_INFO'] == '/'
    else
    Rack::Response.new('Not found', 404)
    end
    @users = ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    &block)
    self,
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  72. users#index
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  73. users#index
    !
    !
    !
    !
    !

    <% @users.each do |user| %>
    <%= user %>
    <% end %>

    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru

    View Slide

  74. View Slide

  75. not sure if MVC…

    View Slide

  76. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    render 'users/index')
    @users = ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    Rack::Response.new(
    else
    Rack::Response.new('Not found', 404)
    end
    end

    View Slide

  77. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    Rack::Response.new(
    else
    Rack::Response.new('Not found', 404)
    end
    end

    View Slide

  78. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    !
    !
    !
    !
    !
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    Rack::Response.new(
    else
    Rack::Response.new('Not found', 404)
    end
    end
    UsersController.new.index)

    View Slide

  79. $LOAD_PATH << '.'
    require 'rack'
    require 'tilt'
    !
    module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    end
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    end
    end
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end

    View Slide

  80. Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    !
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end

    View Slide

  81. Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    !
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    &$ config.ru
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end

    View Slide

  82. View Slide

  83. PROVE IT!

    View Slide

  84. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    !
    !
    !
    !
    &$ config.ru

    View Slide

  85. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    !
    !
    !
    !
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru

    View Slide

  86. require 'spec_helper'
    !
    Capybara.app = Rack::Builder.new do
    eval(File.read(File.expand_path('./config.ru')))
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    !
    !
    !
    !
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru

    View Slide

  87. require 'spec_helper'
    !
    Capybara.app = Rack::Builder.new do
    eval(File.read(File.expand_path('./config.ru')))
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    !
    !
    !
    !
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    describe 'users#index' do
    before { visit '/' }
    it 'renders layout' do
    expect(page).to have_content('application#layout')
    end
    it 'renders index view' do
    expect(page).to have_content('users#index')
    end
    it 'shows a list of users' do
    expect(page).to have_selector('li',
    text: /Stark|Parker|Wayne/, count: 3)
    end
    end
    &$ config.ru

    View Slide

  88. $ rspec
    !
    users#index
    renders layout
    renders index view
    shows a list of users
    !
    Finished in 0.02326 seconds
    3 examples, 0 failures
    !
    Randomized with seed 13626

    View Slide

  89. module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    !
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    !
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru

    View Slide

  90. frack-mvc
    "
    #$ Gemfile
    #$ app
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    module Frack
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    !
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end
    end
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    !
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    REFACTOR
    ALL THE FRACK

    View Slide

  91. !
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    frack-mvc
    "
    #$ Gemfile
    #$ app

    View Slide

  92. " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    !
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    frack-mvc
    "
    #$ Gemfile
    #$ app

    View Slide

  93. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    module Frack
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end

    View Slide

  94. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    require 'app/controllers/users_controller'
    module Frack
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end

    View Slide

  95. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    require 'app/models/user'
    require 'app/controllers/users_controller'
    module Frack
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end

    View Slide

  96. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    class UsersController < Frack::BaseController
    def index
    @users = User.all
    render 'users/index'
    end
    end
    class User
    def self.all
    ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']
    end
    end
    require 'app/models/user'
    require 'app/controllers/users_controller'
    require 'lib/frack'
    module Frack
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    end
    class Application
    class << self
    def call(env)
    if env['PATH_INFO'] == '/'
    Rack::Response.new(UsersController.new.index)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    end
    end
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new("app/views/#{path}.html.erb").render(self, &block)
    end
    end

    View Slide

  97. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application

    View Slide

  98. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
    !
    require 'rack'
    require 'tilt'
    !
    module Frack
    autoload :Application, 'frack/application'
    autoload :BaseController, 'frack/base_controller'
    end

    View Slide

  99. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    "app/views/#{ }.html. ).render(self, &block)
    end
    end
    erb"
    path

    View Slide

  100. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    "app/views/#{ }.html. ).render(self, &block)
    end
    end
    *"
    path

    View Slide

  101. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    path

    View Slide

  102. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    file( )
    path

    View Slide

  103. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    def file(path)
    Dir[ ]
    end
    file( )
    path

    View Slide

  104. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    def file(path)
    Dir[ ]
    end
    File.join('app', 'views', "#{path}.html.*")
    file( )
    path

    View Slide

  105. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    def file(path)
    Dir[ ]
    end
    File.join('app', 'views', "#{path}.html.*") .first
    file( )
    path

    View Slide

  106. module Frack
    class BaseController
    def render(view)
    render_template('layouts/application') do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(
    end
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ).render(self, &block)
    end
    end
    def file(path)
    Dir[ ]
    end
    File.join('app', 'views', "#{path}.html.*") .first
    file( )
    path
    AWESOME!

    support all the template engines

    View Slide

  107. MVC

    View Slide

  108. better routing?
    MVC

    View Slide

  109. better routing?
    magic?
    MVC

    View Slide

  110. What if the router could be middleware?

    View Slide

  111. Middleware Stack
    *a middleware is a rack application that wraps up an inner application.

    View Slide

  112. Middleware Stack
    HTTP request
    *a middleware is a rack application that wraps up an inner application.

    View Slide

  113. Middleware Stack
    use Rack::Static
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    HTTP request
    *a middleware is a rack application that wraps up an inner application.

    View Slide

  114. Middleware Stack
    use Rack::Static
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    HTTP request
    HTTP response
    *a middleware is a rack application that wraps up an inner application.

    View Slide

  115. Middleware Stack
    use Rack::Static
    use Rack::CommonLogger
    use Rack::ContentLength
    run Frack::Application
    HTTP request
    HTTP response
    module Rack
    class ContentLength
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    status, headers, body = @app.call(env)
    # [...]
    # headers['Content-Length'] = length.to_s
    # [...]
    [status, headers, body]
    end
    end
    end
    *a middleware is a rack application that wraps up an inner application.

    View Slide

  116. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    !
    run Frack::Application

    View Slide

  117. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    !
    run Frack::Application
    use Frack::Router

    View Slide

  118. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
    !
    require 'rack'
    require 'tilt'
    !
    module Frack
    !
    autoload :Application, 'frack/application'
    autoload :BaseController, 'frack/base_controller'
    end

    View Slide

  119. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
    !
    require 'rack'
    require 'tilt'
    !
    module Frack
    !
    autoload :Application, 'frack/application'
    autoload :BaseController, 'frack/base_controller'
    end
    autoload :Router, 'frack/router'

    View Slide

  120. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    module Frack
    class Router
    attr_reader :app
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    app.call(env)
    end
    end
    end

    View Slide

  121. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    app.call(env)

    View Slide

  122. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ROUTES = {
    '/' =>
    }
    app.call(env)

    View Slide

  123. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    ROUTES = {
    '/' =>
    }
    app.call(env)
    { 'controller' => 'users', 'action' => 'index' },

    View Slide

  124. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    ROUTES = {
    '/' =>
    }
    app.call(env)
    { 'controller' => 'users', 'action' => 'index' },

    View Slide

  125. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(mapping)
    ROUTES = {
    '/' =>
    }
    app.call(env)
    { 'controller' => 'users', 'action' => 'index' },

    View Slide

  126. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(mapping)
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    { 'controller' => 'users', 'action' => 'index' },

    View Slide

  127. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(mapping)
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'

    View Slide

  128. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    mapping)

    View Slide

  129. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    mapping)

    View Slide

  130. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    controller_action( )
    mapping)

    View Slide

  131. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)

    View Slide

  132. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    def controller_action(mapping)
    !
    !
    end
    or keep it simple…

    View Slide

  133. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    def controller_action(mapping)
    !
    !
    end
    or keep it simple…
    controller, action = mapping.split('#')

    View Slide

  134. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    def initialize(app)
    @app = app
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    end
    end
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = ROUTES[env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    def controller_action(mapping)
    !
    !
    end
    or keep it simple…
    controller, action = mapping.split('#')
    { 'controller' => controller, 'action' => action }

    View Slide

  135. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if env['PATH_INFO'] == '/'
    else
    Rack::Response.new('Not found', 404)
    end
    UsersController.new.index)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    end

    View Slide

  136. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    UsersController.new.index)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    end

    View Slide

  137. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    Rack::Response.new(
    end
    end
    end
    def call(env)
    end

    View Slide

  138. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    *dispatch)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    end

    View Slide

  139. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    *dispatch)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    attr_accessor :env
    self.env = env
    end

    View Slide

  140. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    *dispatch)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    attr_accessor :env
    self.env = env
    end
    def dispatch
    controller.new.public_send(env['action'])
    end

    View Slide

  141. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    *dispatch)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    attr_accessor :env
    self.env = env
    end
    def dispatch
    controller.new.public_send(env['action'])
    end
    def controller

    View Slide

  142. module Frack
    class Application
    class << self
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    *dispatch)
    Rack::Response.new(
    end
    end
    end
    def call(env)
    attr_accessor :env
    self.env = env
    end
    def dispatch
    controller.new.public_send(env['action'])
    end
    def controller
    Object.const_get(env['controller'].capitalize + 'Controller')
    end

    View Slide

  143. What if the router could take a block?

    View Slide

  144. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    !
    !
    !
    !
    use Frack::Router
    run Frack::Application

    View Slide

  145. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH << '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::CommonLogger
    use Rack::ContentLength
    !
    !
    !
    !
    use Frack::Router do
    match '/' => 'users#index'
    end
    run Frack::Application

    View Slide

  146. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    def initialize(app)
    @app = app
    end
    end
    ROUTES

    View Slide

  147. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    ROUTES = {
    '/' =>
    }
    app.call(env)
    'users#index'
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    def initialize(app)
    @app = app
    end
    end
    ROUTES

    View Slide

  148. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    routes
    def initialize(app)
    @app = app
    end
    end

    View Slide

  149. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    routes
    def initialize(app)
    @app = app
    end
    end
    , &block)

    View Slide

  150. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    @routes = {}
    routes
    def initialize(app)
    @app = app
    end
    end
    , &block)

    View Slide

  151. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    @routes = {}
    instance_eval(&block) if block_given?
    routes
    def initialize(app)
    @app = app
    end
    end
    , &block)

    View Slide

  152. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    @routes = {}
    instance_eval(&block) if block_given?
    routes
    def match(route)
    def initialize(app)
    @app = app
    end
    end
    , &block)

    View Slide

  153. module Frack
    class Router
    attr_reader :app
    !
    !
    !
    !
    !
    !
    !
    end
    !
    def call(env)
    !
    !
    !
    !
    !
    end
    !
    !
    !
    !
    !
    !
    !
    !
    !
    frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    if (mapping = [env['PATH_INFO']])
    env.merge!(
    else
    Rack::Response.new('Not found', 404)
    end
    app.call(env)
    def controller_action(mapping)
    Hash[ %w(controller action).zip mapping.split('#') ]
    end
    controller_action( )
    mapping)
    , :routes
    @routes = {}
    instance_eval(&block) if block_given?
    routes
    def match(route)
    self.routes.merge!(route)
    end
    def initialize(app)
    @app = app
    end
    end
    , &block)

    View Slide

  154. what do we have now?

    View Slide

  155. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH.unshift '.'
    !
    require 'lib/frack'
    require 'app/controllers/users_controller'
    require 'app/models/user'
    !
    # use Rack::CommonLogger
    use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']
    use Rack::ContentLength
    use Frack::Router do
    match '/' => 'users#index'
    end
    !
    run Frack::Application

    View Slide

  156. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
    !
    require 'rack'
    require 'erb'
    require 'tilt'
    !
    module Frack
    autoload :Router, 'frack/router'
    autoload :Application, 'frack/application'
    autoload :BaseController, 'frack/base_controller'
    end

    View Slide

  157. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    module Frack
    class Router
    attr_reader :app, :routes
    !
    def initialize(app, &block)
    @app = app
    @routes = {}
    instance_eval(&block) if block_given?
    end
    !
    def call(env)
    if (mapping = routes[env['PATH_INFO']])
    env.merge!(controller_action(mapping))
    app.call(env)
    else
    Rack::Response.new('Not found', 404)
    end
    end
    !
    def controller_action(mapping)
    controller, action = mapping.split('#')
    { 'controller' => controller, 'action' => action }
    end
    !
    def match(route)
    self.routes.merge!(route)
    end
    end
    end

    View Slide

  158. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    module Frack
    class Application
    class << self
    attr_accessor :env
    !
    def call(env)
    self.env = env
    Rack::Response.new(*dispatch)
    end
    !
    def dispatch
    controller.new(env).public_send(env['action'])
    end
    !
    def controller
    Object.const_get(env['controller'].capitalize + 'Controller')
    end
    end
    end
    end

    View Slide

  159. frack-mvc
    "
    #$ Gemfile
    #$ app
    " #$ controllers
    " " &$ users_controller.rb
    " #$ models
    " " &$ user.rb
    " &$ views
    " #$ layouts
    " " &$ application.html.erb
    " &$ users
    " &$ index.html.erb
    #$ lib
    " #$ frack
    " " #$ application.rb
    " " #$ base_controller.rb
    " " &$ router.rb
    " &$ frack.rb
    #$ public
    " #$ css
    " " &$ style.css
    " #$ images
    " &$ js
    #$ spec
    " #$ integration
    " " &$ user_spec.rb
    " &$ spec_helper.rb
    &$ config.ru
    module Frack
    class BaseController
    attr_reader :env
    !
    def initialize(env)
    @env = env
    end
    !
    def render(view)
    render_template(layout) do
    render_template(view)
    end
    end
    !
    def render_template(path, &block)
    Tilt.new(file(path)).render(self, &block)
    end
    !
    def file(path)
    Dir[File.join('app', 'views', "#{path}.html.*")].first
    end
    !
    def layout
    File.join('layouts', 'application')
    end
    end
    end

    View Slide

  160. in ~ 50-100 LOC
    MVC

    View Slide

  161. View Slide

  162. I haven't shown you all of it

    View Slide

  163. Rebuilding Rails features, is a great opportunity to learn how they work.

    View Slide

  164. Challenges for the curious ones:
    !
    • params!
    • redirect_to!
    • partial rendering!
    • implicit call to render!
    • routing with placeholders!
    • routing with specific http verbs!
    • error handling!
    • session management!
    • flash messages!
    • persistence!
    • security stuff and protection!
    • different mime types!
    • url helper!
    • better class loading / autoloading!
    • asset management
    Rebuilding Rails features, is a great opportunity to learn how they work.

    View Slide

  165. YES, you should definitely do this at home
    *but maybe not in production
    or just <3 the Rails core team
    definitely checkout: http://lotusrb.org/
    WHY?
    maybe you come up with the next big framework…

    View Slide

  166. 2014.RailsCamp.de

    View Slide

  167. Marco Schaden | @donschado | 23.08.2014
    RedFrog Conf
    kthxbye

    View Slide

  168. Marco Schaden | @donschado | 23.08.2014
    RedFrog Conf
    Marco @DonSchado - 17 Std.

    rackup -r rack/lobster -b 'run Rack::Lobster.new'

    View Slide

  169. Backup

    View Slide

  170. Appendix: Rack in the wild

    View Slide

  171. http://guides.rubyonrails.org/rails_on_rack.html

    View Slide

  172. \m/
    ActionController::Metal

    View Slide

  173. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    *returns a valid Rack application for the Rails router to dispatch to

    View Slide

  174. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    OMG WHY?
    *returns a valid Rack application for the Rails router to dispatch to

    View Slide

  175. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    • maybe to extract lightweight micro services
    OMG WHY?
    *returns a valid Rack application for the Rails router to dispatch to

    View Slide

  176. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    • maybe to extract lightweight micro services
    • to improve the performance of any API endpoint
    OMG WHY?
    *returns a valid Rack application for the Rails router to dispatch to

    View Slide

  177. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    • maybe to extract lightweight micro services
    • to improve the performance of any API endpoint
    • because all benefits of rails are "just a few" includes away
    OMG WHY?
    *returns a valid Rack application for the Rails router to dispatch to

    View Slide

  178. http://api.rubyonrails.org/classes/ActionController/Metal.html
    class HelloController < ActionController::Metal
    def index
    self.response_body = "Hello World!"
    end
    end
    get 'hello', to: HelloController.action(:index)
    • maybe to extract lightweight micro services
    • to improve the performance of any API endpoint
    • because all benefits of rails are "just a few" includes away
    OMG WHY?
    *returns a valid Rack application for the Rails router to dispatch to
    class HelloController < ActionController::Metal
    include AbstractController::Rendering
    include ActionView::Layouts
    append_view_path "#{Rails.root}/app/views"
    !
    def index
    render "hello/index"
    end
    !
    # Ensure ActiveSupport::Notifications events are fired
    include ActionController::Instrumentation
    end

    View Slide

  179. Backup Backup

    View Slide

  180. http://www.madebymarket.com/blog/dev/ruby-web-benchmark-report.html
    "every possible rack server on every possible Ruby, every possible web framework"

    View Slide

  181. Spoiler Alert!

    View Slide

  182. "The fastest framework is Rack. !
    Plain old Rack with no framework at all."
    Ruby 2.1 + Thin + Rack can hit 6301 req/sec
    => Sinatra only manages !! ! 2505 req/sec.
    => Rails only does ! ! ! ! 1455 req/sec
    "So, by simply using those frameworks on top of Rack, 

    you give up over 60% of your maximum possible throughput."
    Spoiler Alert!

    View Slide

  183. Spoiler spoiler Alert!
    On a plain old Rack app it did 10.159 req/sec.
    +
    "Standard Ruby + Thin/Unicorn + Rails !
    is about the slowest possible combination"
    the fastest:

    View Slide

  184. *I think these benchmarks are a little bit unfair, !
    when you compare a full rails stack vs. a rack one-liner.!
    But I think the main message is valid:!
    Do you need the full stack for every request?
    Logging
    Sessions
    Cookies
    Security
    Caching
    Error Handling
    Routing

    View Slide

  185. that’s all

    View Slide