Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Rack is the foundation of all modern Ruby web frameworks

Slide 6

Slide 6 text

the future?

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

no worries

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

status quo

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

<<"show"me"the"code">>

Slide 16

Slide 16 text

! ->(env) { }

Slide 17

Slide 17 text

! ->(env) { } [ ]

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

! ->(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!']

Slide 22

Slide 22 text

! ->(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!']

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

HTML + ERB

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Hello World!

HTML

Slide 29

Slide 29 text

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

Current time: <%= Time.now %>

Slide 30

Slide 30 text

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

Current time: <%= Time.now %>

ERB.new(view).result) }

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Slide 33

Slide 33 text

MVC

Slide 34

Slide 34 text

froscon-rack-mvc

Slide 35

Slide 35 text

frack-mvc

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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'

Slide 38

Slide 38 text

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

application#layout

<%= yield %>

Slide 39

Slide 39 text

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

users#index

Slide 40

Slide 40 text

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.

Slide 41

Slide 41 text

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.

Slide 42

Slide 42 text

["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"] ["SERVER_NAME",."localhost"]["rack.input",. #>]["rack.version",.[1,. 0]]["rack.errors",.#>>] ["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",.#]["async.close",. #]

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

application#layout! ! users#index

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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'

Slide 54

Slide 54 text

$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

Slide 55

Slide 55 text

$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

Slide 56

Slide 56 text

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; }

Slide 57

Slide 57 text

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

application#layout

<%= yield %>

Slide 58

Slide 58 text

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

application#layout

<%= yield %>

Slide 59

Slide 59 text

$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

Slide 60

Slide 60 text

$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

Slide 61

Slide 61 text

application#layout ! users#index

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

["SERVER_SOFTWARE",."thin.1.6.2.codename.Doc.Brown"] ["SERVER_NAME",."localhost"]["rack.input",. #>]["rack.version",.[1,. 0]]["rack.errors",.#>>] ["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",.#]["async.close",. #] remember env?

Slide 65

Slide 65 text

$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

Slide 66

Slide 66 text

$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

Slide 67

Slide 67 text

$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

Slide 68

Slide 68 text

$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

Slide 69

Slide 69 text

$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

Slide 70

Slide 70 text

$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

Slide 71

Slide 71 text

$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

Slide 72

Slide 72 text

users#index

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

Slide 73

Slide 73 text

users#index

! ! ! ! !
    <% @users.each do |user| %>
  • <%= user %>
  • <% end %>
frack-mvc " #$ Gemfile #$ app " &$ views " #$ layouts " " &$ application.html.erb " &$ users " &$ index.html.erb #$ public " #$ css " " &$ style.css " #$ images " &$ js &$ config.ru

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

not sure if MVC…

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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)

Slide 79

Slide 79 text

$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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

PROVE IT!

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

$ 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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

! " &$ 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

Slide 92

Slide 92 text

" #$ 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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

MVC

Slide 108

Slide 108 text

better routing? MVC

Slide 109

Slide 109 text

better routing? magic? MVC

Slide 110

Slide 110 text

What if the router could be middleware?

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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.

Slide 114

Slide 114 text

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.

Slide 115

Slide 115 text

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.

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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'

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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)

Slide 122

Slide 122 text

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)

Slide 123

Slide 123 text

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' },

Slide 124

Slide 124 text

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' },

Slide 125

Slide 125 text

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' },

Slide 126

Slide 126 text

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' },

Slide 127

Slide 127 text

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'

Slide 128

Slide 128 text

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)

Slide 129

Slide 129 text

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)

Slide 130

Slide 130 text

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)

Slide 131

Slide 131 text

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)

Slide 132

Slide 132 text

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…

Slide 133

Slide 133 text

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('#')

Slide 134

Slide 134 text

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 }

Slide 135

Slide 135 text

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

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

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

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

What if the router could take a block?

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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)

Slide 150

Slide 150 text

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)

Slide 151

Slide 151 text

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)

Slide 152

Slide 152 text

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)

Slide 153

Slide 153 text

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)

Slide 154

Slide 154 text

what do we have now?

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

in ~ 50-100 LOC MVC

Slide 161

Slide 161 text

No content

Slide 162

Slide 162 text

I haven't shown you all of it

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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.

Slide 165

Slide 165 text

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…

Slide 166

Slide 166 text

2014.RailsCamp.de

Slide 167

Slide 167 text

Marco Schaden | @donschado | 23.08.2014 RedFrog Conf kthxbye

Slide 168

Slide 168 text

Marco Schaden | @donschado | 23.08.2014 RedFrog Conf Marco @DonSchado - 17 Std.
 rackup -r rack/lobster -b 'run Rack::Lobster.new'

Slide 169

Slide 169 text

Backup

Slide 170

Slide 170 text

Appendix: Rack in the wild

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

\m/ ActionController::Metal

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

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

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

Backup Backup

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

Spoiler Alert!

Slide 182

Slide 182 text

"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!

Slide 183

Slide 183 text

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:

Slide 184

Slide 184 text

*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

Slide 185

Slide 185 text

that’s all