Ruby MVC from scratch with Rack

1,770 views

Published on

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.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,770
On SlideShare
0
From Embeds
0
Number of Embeds
38
Actions
Shares
0
Downloads
21
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Ruby MVC from scratch with Rack

  1. 1. Ruby MVC from scratch with Rack I Marco Schaden | @donschado | 23.08.2014RedFrog Conf
  2. 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. 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. 4. Rack is the foundation of all modern Ruby web frameworks
  5. 5. http://chneukirchen.org/talks/euruko-2007/neukirchen07introducingrack.pdf
  6. 6. 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
  7. 7. Write once, run "everywhere" WEBrick! Thin! Puma! Unicorn! Passenger! Torqbox /Torquebox! Trinidad! Mongrel! Pow! CGI! SCGI! FastCGI! Ebb! Fuzed! Litespeed! …
  8. 8. 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
  9. 9. <<"show"me"the"code">>
  10. 10. ! ->(env) { }
  11. 11. ! ->(env) { }[ ]
  12. 12. ! ->(env) { }[ ]200, {'Content-Type' => 'text/html'}, ['Hello World!']
  13. 13. ! ->(env) { } *run means "call that object for each request" run [ ]200, {'Content-Type' => 'text/html'}, ['Hello World!']
  14. 14. ! ->(env) { } *run means "call that object for each request" # config.ru run [ ]200, {'Content-Type' => 'text/html'}, ['Hello World!']
  15. 15. ! ->(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!']
  16. 16. ! ->(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!']
  17. 17. , read it: https://github.com/rack/rack How does it work? seriously
  18. 18. HTML + ERB
  19. 19. # config.ru run -> e { [200, {'Content-Type' => 'text/html'}, ['Hello World!']] }
  20. 20. # config.ru run -> e { Rack::Response.new('Hello World!') }
  21. 21. # config.ru run -> e { Rack::Response.new(view) }
  22. 22. # 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
  23. 23. # 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>
  24. 24. # 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) }
  25. 25. Current time: 2014-08-23 10:54:29 +0200
  26. 26. </introduction>
  27. 27. MVC
  28. 28. froscon-rack-mvc
  29. 29. frack-mvc
  30. 30. frack-mvc " #$ Gemfile #$ app "  &$ views "   #$ layouts "   "  &$ application.html.erb "   &$ users "   &$ index.html.erb &$ config.ru
  31. 31. 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'
  32. 32. 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>
  33. 33. frack-mvc " #$ Gemfile #$ app "  &$ views "   #$ layouts "   "  &$ application.html.erb "   &$ users "   &$ index.html.erb &$ config.ru <h2>users#index</h2>
  34. 34. 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.
  35. 35. 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.
  36. 36. ["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>]
  37. 37. 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
  38. 38. 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
  39. 39. 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
  40. 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) 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
  41. 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) 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
  42. 42. 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
  43. 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(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
  44. 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 ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 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
  45. 45. application#layout! ! users#index
  46. 46. application#layout! ! users#index but can I haz style?
  47. 47. $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
  48. 48. $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
  49. 49. 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; }
  50. 50. 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>
  51. 51. 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">
  52. 52. $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
  53. 53. $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
  54. 54. application#layout ! users#index
  55. 55. application#layout ! users#index what about simple routing? and list some users?
  56. 56. ["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?
  57. 57. $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
  58. 58. $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
  59. 59. $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
  60. 60. $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
  61. 61. $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
  62. 62. $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
  63. 63. <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
  64. 64. <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
  65. 65. not sure if MVC…
  66. 66. 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
  67. 67. 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
  68. 68. 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)
  69. 69. $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
  70. 70. 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
  71. 71. 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
  72. 72. PROVE IT!
  73. 73. 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
  74. 74. 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
  75. 75. 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
  76. 76. $ 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
  77. 77. 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
  78. 78. 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
  79. 79. ! "  &$ 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
  80. 80. " #$ 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
  81. 81. 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
  82. 82. 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
  83. 83. 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
  84. 84. 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
  85. 85. 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
  86. 86. 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
  87. 87. 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
  88. 88. 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
  89. 89. 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
  90. 90. 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
  91. 91. 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
  92. 92. 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
  93. 93. 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
  94. 94. 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
  95. 95. 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
  96. 96. 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
  97. 97. better routing? magic? MVC
  98. 98. What if the router could be middleware?
  99. 99. Middleware Stack *a middleware is a rack application that wraps up an inner application.
  100. 100. Middleware Stack HTTPrequest *a middleware is a rack application that wraps up an inner application.
  101. 101. Middleware Stack use Rack::Static use Rack::CommonLogger use Rack::ContentLength run Frack::Application HTTPrequest *a middleware is a rack application that wraps up an inner application.
  102. 102. Middleware Stack use Rack::Static use Rack::CommonLogger use Rack::ContentLength run Frack::Application HTTPrequest HTTPresponse *a middleware is a rack application that wraps up an inner application.
  103. 103. Middleware Stack use Rack::Static use Rack::CommonLogger use Rack::ContentLength run Frack::Application HTTPrequest HTTPresponse 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.
  104. 104. 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
  105. 105. 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
  106. 106. 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
  107. 107. 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'
  108. 108. 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
  109. 109. 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)
  110. 110. 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)
  111. 111. 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' },
  112. 112. 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' },
  113. 113. 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' },
  114. 114. 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' },
  115. 115. 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'
  116. 116. 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)
  117. 117. 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)
  118. 118. 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)
  119. 119. 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)
  120. 120. 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
  121. 121. 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
  122. 122. 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
  123. 123. 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
  124. 124. 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
  125. 125. 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
  126. 126. 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
  127. 127. 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
  128. 128. What if the router could take a block?
  129. 129. 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
  130. 130. 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
  131. 131. 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
  132. 132. 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
  133. 133. 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
  134. 134. 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)
  135. 135. 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)
  136. 136. 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)
  137. 137. 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)
  138. 138. 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)
  139. 139. in ~50-100 LOCMVC
  140. 140. I haven't shown you all of it
  141. 141. 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.
  142. 142. yes, you should definitely do this at home but maybe not in production love the Rails core team for their amazing work
  143. 143. 2014.RailsCamp.de
  144. 144. Marco Schaden | @donschado | 23.08.2014RedFrog Conf kthxbye
  145. 145. Marco Schaden | @donschado | 23.08.2014RedFrog Conf Marco @DonSchado - 17 Std.
 rackup -r rack/lobster -b 'run Rack::Lobster.new'
  146. 146. Backup
  147. 147. Appendix: Rack in the wild
  148. 148. http://guides.rubyonrails.org/rails_on_rack.html
  149. 149. m/ActionController::Metal
  150. 150. 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
  151. 151. 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
  152. 152. 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

×