Ruby MVC from scratch with Rack

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

5b7ac75124cebf11ff0de894b365198e?s=128

DonSchado

August 23, 2014
Tweet

Transcript

  1. Ruby MVC from scratch with Rack I Marco Schaden |

    @donschado | 23.08.2014 RedFrog Conf
  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
  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
  4. None
  5. Rack is the foundation of all modern Ruby web frameworks

  6. the future?

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

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

  9. no worries

  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
  11. status quo

  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
  13. Write once, run "everywhere" WEBrick! Thin! Puma! Unicorn! Passenger! Torqbox

    / Torquebox! Trinidad! Mongrel! Pow! CGI! SCGI! FastCGI! Ebb! Fuzed! Litespeed! …
  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
  15. <<"show"me"the"code">>

  16. ! ->(env) { }

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

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

    ['Hello World!']
  19. ! ->(env) { } *run means "call that object for

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

    each request" # config.ru run [ ] 200, {'Content-Type' => 'text/html'}, ['Hello World!']
  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!']
  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!']
  23. , read it: https://github.com/rack/rack How does it work? seriously

  24. HTML + ERB

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

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

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

  28. # config.ru ! ! ! ! ! ! ! !

    ! ! ! ! ! ! ! ! run -> e { Rack::Response.new(view) } view = <<-HTML <!DOCTYPE html> <html> <head> <title>Rack with HTML</title> </head> <body> <div> <h1>Hello World!</h1> </div> </body> </html> HTML
  29. # config.ru ! ! ! ! ! ! ! !

    ! ! ! ! ! ! ! ! run -> e { Rack::Response.new(view) } require 'erb' view = <<-HTML <!DOCTYPE html> <html> <head> <title>Rack with </head> <body> <div> <h1>Hello World!</h1> </div> </body> </html> HTML ERB</title> <h1>Current time: <%= Time.now %></h1>
  30. # config.ru ! ! ! ! ! ! ! !

    ! ! ! ! ! ! ! ! run -> e { Rack::Response.new(view) } require 'erb' view = <<-HTML <!DOCTYPE html> <html> <head> <title>Rack with </head> <body> <div> <h1>Hello World!</h1> </div> </body> </html> HTML ERB</title> <h1>Current time: <%= Time.now %></h1> ERB.new(view).result) }
  31. Current time: 2014-08-23 10:54:29 +0200

  32. </introduction>

  33. MVC

  34. froscon-rack-mvc

  35. frack-mvc

  36. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb &$ config.ru
  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'
  38. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb &$ config.ru <html> <head> <title>Frack MVC</title> </head> <body> <h1>application#layout</h1> <%= yield %> </body> </html>
  39. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb &$ config.ru <h2>users#index</h2>
  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.
  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.
  42. ["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"] ["SERVER_NAME",."localhost"]["rack.input",. #<Rack::Lint::InputWrapper:0x007fc0421e1ea0. @input=#<StringIO:0x007fc0421fb940>>]["rack.version",.[1,. 0]]["rack.errors",.#<Rack::Lint::ErrorWrapper: 0x007fc0421e1cc0.@error=#<IO:<STDERR>>>] ["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",.#<Method:. Thin::Connection#post_process>]["async.close",. #<EventMachine::DefaultDeferrable:0x007fc0421fab08>]
  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
  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
  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
  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
  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
  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
  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
  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
  51. application#layout! ! users#index

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

  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'
  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
  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
  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; }
  57. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js &$ config.ru <html> <head> <title>Frack MVC</title> ! </head> <body> <h1>application#layout</h1> <%= yield %> </body> </html>
  58. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js &$ config.ru <html> <head> <title>Frack MVC</title> ! </head> <body> <h1>application#layout</h1> <%= yield %> </body> </html> <link rel="stylesheet" type="text/css" href="css/style.css">
  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
  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
  61. application#layout ! users#index

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

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

    users?
  64. ["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"] ["SERVER_NAME",."localhost"]["rack.input",. #<Rack::Lint::InputWrapper:0x007fc0421e1ea0. @input=#<StringIO:0x007fc0421fb940>>]["rack.version",.[1,. 0]]["rack.errors",.#<Rack::Lint::ErrorWrapper: 0x007fc0421e1cc0.@error=#<IO:<STDERR>>>] ["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",.#<Method:. Thin::Connection#post_process>]["async.close",. #<EventMachine::DefaultDeferrable:0x007fc0421fab08>] remember env?
  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
  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
  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
  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
  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
  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
  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
  72. <h2>users#index</h2> ! ! ! ! ! frack-mvc " #$ Gemfile

    #$ app " &$ views " #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js &$ config.ru
  73. <h2>users#index</h2> ! ! ! ! ! <ul> <% @users.each do

    |user| %> <li><%= user %></li> <% end %> </ul> frack-mvc " #$ Gemfile #$ app " &$ views " #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js &$ config.ru
  74. None
  75. not sure if MVC…

  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
  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
  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)
  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
  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
  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
  82. None
  83. PROVE IT!

  84. frack-mvc " #$ Gemfile #$ app " &$ views "

    #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js ! ! ! ! &$ config.ru
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  107. MVC

  108. better routing? MVC

  109. better routing? magic? MVC

  110. What if the router could be middleware?

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

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

    that wraps up an inner application.
  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.
  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.
  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.
  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
  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
  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
  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'
  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
  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)
  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)
  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' },
  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' },
  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' },
  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' },
  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'
  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)
  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)
  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)
  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)
  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…
  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('#')
  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 }
  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
  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
  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
  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
  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
  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
  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
  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
  143. What if the router could take a block?

  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
  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
  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
  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
  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
  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)
  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)
  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)
  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)
  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)
  154. what do we have now?

  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
  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
  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
  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
  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
  160. in ~ 50-100 LOC MVC

  161. None
  162. I haven't shown you all of it

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

    they work.
  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.
  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…
  166. 2014.RailsCamp.de

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

  168. Marco Schaden | @donschado | 23.08.2014 RedFrog Conf Marco @DonSchado

    - 17 Std.
 rackup -r rack/lobster -b 'run Rack::Lobster.new'
  169. Backup

  170. Appendix: Rack in the wild

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

  172. \m/ ActionController::Metal

  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
  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
  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
  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
  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
  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
  179. Backup Backup

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

    possible web framework"
  181. Spoiler Alert!

  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!
  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:
  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
  185. that’s all