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

Ruby MVC from scratch with Rack

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: Ruby MVC from scratch with Rack

Ruby MVC from scratch with Rack

I

Marco Schaden | @donschado | 23.08.2014RedFrog Conf

Page 2: Ruby MVC from scratch with Rack

there was a rack applicationhandling 10.000+ req/sec

once upon a time

http://www.madebymarket.com/blog/dev/ruby-web-benchmark-report.html

Page 3: Ruby MVC from scratch with Rack

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

Page 4: Ruby MVC from scratch with Rack
Page 5: Ruby MVC from scratch with Rack

Rack is the foundation of all modern Ruby web frameworks

Page 6: Ruby MVC from scratch with Rack

http://chneukirchen.org/talks/euruko-2007/neukirchen07introducingrack.pdf

Page 7: Ruby MVC from scratch with Rack

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

Page 8: Ruby MVC from scratch with Rack

Write once, run "everywhere"

WEBrick!Thin!Puma!

Unicorn!Passenger!

Torqbox / Torquebox!Trinidad!Mongrel!

Pow!CGI!SCGI!

FastCGI!Ebb!

Fuzed!Litespeed!

Page 9: Ruby MVC from scratch with Rack

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

Page 10: Ruby MVC from scratch with Rack

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

Page 11: Ruby MVC from scratch with Rack

! ->(env) { }

Page 12: Ruby MVC from scratch with Rack

! ->(env) { }[ ]

Page 13: Ruby MVC from scratch with Rack

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

Page 14: Ruby MVC from scratch with Rack

! ->(env) { }

*run means "call that object for each request"

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

Page 15: Ruby MVC from scratch with Rack

! ->(env) { }

*run means "call that object for each request"

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

Page 16: Ruby MVC from scratch with Rack

! ->(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.rurun [ ]200, {'Content-Type' => 'text/html'}, ['Hello World!']

Page 17: Ruby MVC from scratch with Rack

! ->(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.rurun [ ]200, {'Content-Type' => 'text/html'}, ['Hello World!']

Page 18: Ruby MVC from scratch with Rack

, read it: https://github.com/rack/rack

How does it work?

seriously

Page 19: Ruby MVC from scratch with Rack

HTML + ERB

Page 20: Ruby MVC from scratch with Rack

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

Page 21: Ruby MVC from scratch with Rack

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

Page 22: Ruby MVC from scratch with Rack

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

Page 23: Ruby MVC from scratch with Rack

# 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

Page 24: Ruby MVC from scratch with Rack

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

Page 25: Ruby MVC from scratch with Rack

# 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) }

Page 26: Ruby MVC from scratch with Rack

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

Page 27: Ruby MVC from scratch with Rack

</introduction>

Page 28: Ruby MVC from scratch with Rack

MVC

Page 29: Ruby MVC from scratch with Rack

froscon-rack-mvc

Page 30: Ruby MVC from scratch with Rack

frack-mvc

Page 31: Ruby MVC from scratch with Rack

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

Page 32: Ruby MVC from scratch with Rack

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'

Page 33: Ruby MVC from scratch with Rack

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>

Page 34: Ruby MVC from scratch with Rack

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

<h2>users#index</h2>

Page 35: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

*use adds a middleware to the rack application stack created by Rack::Builder.

Page 36: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Rack::Response.new(env)

*use adds a middleware to the rack application stack created by Rack::Builder.

Page 37: Ruby MVC from scratch with Rack

["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>]

Page 38: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

env

Page 39: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new( ) end!!!!!!!!!! !!!!!

end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 40: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 41: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 42: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 43: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 44: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 45: Ruby MVC from scratch with Rack

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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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 endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 46: Ruby MVC from scratch with Rack

application#layout!!users#index

Page 47: Ruby MVC from scratch with Rack

application#layout!!users#index

but can I haz style?

Page 48: Ruby MVC from scratch with Rack

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new(render 'users/index') end end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new( ) end!!!!!!!!!! end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 49: Ruby MVC from scratch with Rack

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new(render 'users/index') end end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new( ) end!!!!!!!!!! end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 50: Ruby MVC from scratch with Rack

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

Page 51: Ruby MVC from scratch with Rack

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>

Page 52: Ruby MVC from scratch with Rack

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

Page 53: Ruby MVC from scratch with Rack

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new(render 'users/index') end end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new( ) end!!!!!!!!!! end endend!!use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 54: Ruby MVC from scratch with Rack

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new(render 'users/index') end end endend!use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

$LOAD_PATH << '.'require 'rack'require 'tilt'!module Frack class Application class << self def call(env) Rack::Response.new( ) end!!!!!!!!!! end endend!!use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 55: Ruby MVC from scratch with Rack

application#layout !users#index

Page 56: Ruby MVC from scratch with Rack

application#layout !users#index

what about simple routing?

and list some users?

Page 57: Ruby MVC from scratch with Rack

["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?

Page 58: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

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

Page 59: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 60: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

if env['PATH_INFO'] == '/'

elseRack::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

Page 61: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 62: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 63: Ruby MVC from scratch with Rack

$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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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

Page 64: Ruby MVC from scratch with Rack

<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

Page 65: Ruby MVC from scratch with Rack

<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

Page 66: Ruby MVC from scratch with Rack
Page 67: Ruby MVC from scratch with Rack
Page 68: Ruby MVC from scratch with Rack

not sure if MVC…

Page 69: Ruby MVC from scratch with Rack

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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

render 'users/index')@users = ['Anthony Stark', 'Peter Parker', 'Bruce Wayne']

Rack::Response.new( else Rack::Response.new('Not found', 404) end end

Page 70: Ruby MVC from scratch with Rack

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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Rack::Response.new( else Rack::Response.new('Not found', 404) end end

Page 71: Ruby MVC from scratch with Rack

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 endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Rack::Response.new( else Rack::Response.new('Not found', 404) end end

UsersController.new.index)

Page 72: Ruby MVC from scratch with Rack

$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!!!!!!!!!!!! endend!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun Frack::Application

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

endend

class BaseControllerdef render(view) render_template('layouts/application') do render_template(view) endend!def render_template(path, &block) Tilt.new("app/views/#{path}.html.erb").render(self, &block)end

Page 73: Ruby MVC from scratch with Rack

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 endend!!!!!!!!!!!!!!!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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::BaseControllerdef index

@users = User.allrender 'users/index'

endend

Page 74: Ruby MVC from scratch with Rack

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 endend!!!!!!!!!!!!!!!use Rack::Static, root: 'public', urls: ['/images', '/js', '/css']use Rack::CommonLoggeruse Rack::ContentLengthrun 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::BaseControllerdef index

@users = User.allrender 'users/index'

endend

class Userdef self.all

['Anthony Stark', 'Peter Parker', 'Bruce Wayne']end

end

Page 75: Ruby MVC from scratch with Rack
Page 76: Ruby MVC from scratch with Rack

PROVE IT!

Page 77: Ruby MVC from scratch with Rack

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

Page 78: Ruby MVC from scratch with Rack

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

Page 79: Ruby MVC from scratch with Rack

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' dobefore { visit '/' }

it 'renders layout' doexpect(page).to have_content('application#layout')

end

it 'renders index view' doexpect(page).to have_content('users#index')

end

it 'shows a list of users' doexpect(page).to have_selector('li',text: /Stark|Parker|Wayne/, count: 3)

endend

&$ config.ru

Page 80: Ruby MVC from scratch with Rack

$ 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

Page 81: Ruby MVC from scratch with Rack

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 endend!class UsersController < Frack::BaseController def index @users = User.all render 'users/index' endend!class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 82: Ruby MVC from scratch with Rack

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 endend!class UsersController < Frack::BaseController def index @users = User.all render 'users/index' endend!class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

REFACTOR ALL THE FRACK

Page 83: Ruby MVC from scratch with Rack

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

Page 84: Ruby MVC from scratch with Rack

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

Page 85: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 86: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 87: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 88: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 89: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 90: Ruby MVC from scratch with Rack

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

class User def self.all ['Anthony Stark', 'Peter Parker', 'Bruce Wayne'] endend

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

Page 91: Ruby MVC from scratch with Rack

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::CommonLoggeruse Rack::ContentLengthrun Frack::Application

Page 92: Ruby MVC from scratch with Rack

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

Page 93: Ruby MVC from scratch with Rack

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)

endend

erb"path

Page 94: Ruby MVC from scratch with Rack

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)

endend

*"path

Page 95: Ruby MVC from scratch with Rack

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)

endend

path

Page 96: Ruby MVC from scratch with Rack

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)

endend

file( )path

Page 97: Ruby MVC from scratch with Rack

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)

endend

def file(path)Dir[ ]

end

file( )path

Page 98: Ruby MVC from scratch with Rack

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)

endend

def file(path)Dir[ ]

endFile.join('app', 'views', "#{path}.html.*")

file( )path

Page 99: Ruby MVC from scratch with Rack

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)

endend

def file(path)Dir[ ]

endFile.join('app', 'views', "#{path}.html.*") .first

file( )path

Page 100: Ruby MVC from scratch with Rack

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)

endend

def file(path)Dir[ ]

endFile.join('app', 'views', "#{path}.html.*") .first

file( )path

AWESOME!support all the template engines

Page 101: Ruby MVC from scratch with Rack

better routing?magic?

MVC

Page 102: Ruby MVC from scratch with Rack

What if the router could be middleware?

Page 103: Ruby MVC from scratch with Rack

Middleware Stack

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

Page 104: Ruby MVC from scratch with Rack

Middleware Stack

HT

TP

requ

est

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

Page 105: Ruby MVC from scratch with Rack

Middleware Stack

use Rack::Static

use Rack::CommonLogger

use Rack::ContentLength

run Frack::Application

HT

TP

requ

est

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

Page 106: Ruby MVC from scratch with Rack

Middleware Stack

use Rack::Static

use Rack::CommonLogger

use Rack::ContentLength

run Frack::Application

HT

TP

requ

est

HT

TP

resp

onse

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

Page 107: Ruby MVC from scratch with Rack

Middleware Stack

use Rack::Static

use Rack::CommonLogger

use Rack::ContentLength

run Frack::Application

HT

TP

requ

est

HT

TP

resp

onse

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 endend

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

Page 108: Ruby MVC from scratch with Rack

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::CommonLoggeruse Rack::ContentLength!run Frack::Application

Page 109: Ruby MVC from scratch with Rack

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::CommonLoggeruse Rack::ContentLength!run Frack::Applicationuse Frack::Router

Page 110: Ruby MVC from scratch with Rack

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

Page 111: Ruby MVC from scratch with Rack

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'

Page 112: Ruby MVC from scratch with Rack

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 endend

Page 113: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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)

Page 114: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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)

Page 115: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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

Page 116: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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

Page 117: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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

Page 118: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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)

elseRack::Response.new('Not found', 404)

end

ROUTES = {'/' =>

}

app.call(env)

{ 'controller' => 'users', 'action' => 'index' },

Page 119: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end endend

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)

elseRack::Response.new('Not found', 404)

end

ROUTES = {'/' =>

}

app.call(env)

'users#index'

Page 120: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end!!!! endend

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)

Page 121: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end!!!! endend

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)

Page 122: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end!!!! endend

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)

Page 123: Ruby MVC from scratch with Rack

module Frack class Router attr_reader :app!!!!! def initialize(app) @app = app end! def call(env) !!!!! end!!!! endend

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)

Page 124: Ruby MVC from scratch with Rack

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'] == '/'

elseRack::Response.new('Not found', 404)

end

UsersController.new.index)Rack::Response.new(

end endend

def call(env)

end

Page 125: Ruby MVC from scratch with Rack

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 endend

def call(env)

end

Page 126: Ruby MVC from scratch with Rack

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 endend

def call(env)

end

Page 127: Ruby MVC from scratch with Rack

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 endend

def call(env)

end

Page 128: Ruby MVC from scratch with Rack

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 endend

def call(env)

attr_accessor :env

self.env = env

end

Page 129: Ruby MVC from scratch with Rack

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 endend

def call(env)

attr_accessor :env

self.env = env

end

def dispatchcontroller.new.public_send(env['action'])

end

Page 130: Ruby MVC from scratch with Rack

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 endend

def call(env)

attr_accessor :env

self.env = env

end

def dispatchcontroller.new.public_send(env['action'])

end

def controller

Page 131: Ruby MVC from scratch with Rack

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 endend

def call(env)

attr_accessor :env

self.env = env

end

def dispatchcontroller.new.public_send(env['action'])

end

def controllerObject.const_get(env['controller'].capitalize + 'Controller')

end

Page 132: Ruby MVC from scratch with Rack

What if the router could take a block?

Page 133: Ruby MVC from scratch with Rack

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::CommonLoggeruse Rack::ContentLength!!!!

use Frack::Routerrun Frack::Application

Page 134: Ruby MVC from scratch with Rack

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::CommonLoggeruse Rack::ContentLength!!!!

use Frack::Router domatch '/' => 'users#index'

end

run Frack::Application

Page 135: Ruby MVC from scratch with Rack

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

endend

ROUTES

Page 136: Ruby MVC from scratch with Rack

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

endend

ROUTES

Page 137: Ruby MVC from scratch with Rack

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

endend

Page 138: Ruby MVC from scratch with Rack

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

endend

, &block)

Page 139: Ruby MVC from scratch with Rack

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

endend

, &block)

Page 140: Ruby MVC from scratch with Rack

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

endend

, &block)

Page 141: Ruby MVC from scratch with Rack

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

endend

, &block)

Page 142: Ruby MVC from scratch with Rack

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

endend

, &block)

Page 143: Ruby MVC from scratch with Rack

in ~ 50-100 LOCMVC

Page 144: Ruby MVC from scratch with Rack
Page 145: Ruby MVC from scratch with Rack

I haven't shown you all of it

Page 146: Ruby MVC from scratch with Rack

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.

Page 147: Ruby MVC from scratch with Rack

yes, you should definitely do this at home

but maybe not in production

love the Rails core team for their amazing work

Page 148: Ruby MVC from scratch with Rack

2014.RailsCamp.de

Page 149: Ruby MVC from scratch with Rack

Marco Schaden | @donschado | 23.08.2014RedFrog Conf

kthxbye

Page 150: Ruby MVC from scratch with Rack

Marco Schaden | @donschado | 23.08.2014RedFrog Conf

Marco @DonSchado - 17 Std.rackup -r rack/lobster -b 'run Rack::Lobster.new'

Page 151: Ruby MVC from scratch with Rack

Backup

Page 152: Ruby MVC from scratch with Rack

Appendix: Rack in the wild

Page 153: Ruby MVC from scratch with Rack

http

://gu

ides

.ruby

onra

ils.o

rg/ra

ils_o

n_ra

ck.ht

ml

Page 154: Ruby MVC from scratch with Rack

\m/ActionController ::Metal

Page 155: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

get 'hello', to: HelloController.action(:index)

*returns a valid Rack application for the Rails router to dispatch to

Page 156: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

get 'hello', to: HelloController.action(:index)

OMG WHY?

*returns a valid Rack application for the Rails router to dispatch to

Page 157: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

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

Page 158: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

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

Page 159: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

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

Page 160: Ruby MVC from scratch with Rack

http://api.rubyonrails.org/classes/ActionController/Metal.html

class HelloController < ActionController::Metal def index self.response_body = "Hello World!" endend

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::Instrumentationend

Page 161: Ruby MVC from scratch with Rack

Backup Backup

Page 162: Ruby MVC from scratch with Rack

http://www.madebymarket.com/blog/dev/ruby-web-benchmark-report.html

"every possible rack server on every possible Ruby, every possible web framework"

Page 163: Ruby MVC from scratch with Rack

Spoiler Alert!

Page 164: Ruby MVC from scratch with Rack

"The fastest framework is Rack. !Plain old Rack with no framework at all."

Spoiler Alert!

Page 165: Ruby MVC from scratch with Rack

"The fastest framework is Rack. !Plain old Rack with no framework at all."

Ruby 2.1 + Thin + Rack can hit 6301 req/sec

Spoiler Alert!

Page 166: Ruby MVC from scratch with Rack

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

Spoiler Alert!

Page 167: Ruby MVC from scratch with Rack

"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

Spoiler Alert!

Page 168: Ruby MVC from scratch with Rack

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

Page 169: Ruby MVC from scratch with Rack

Spoiler spoiler Alert!

"Standard Ruby + Thin/Unicorn + Rails !is about the slowest possible combination"

Page 170: Ruby MVC from scratch with Rack

Spoiler spoiler Alert!

"Standard Ruby + Thin/Unicorn + Rails !is about the slowest possible combination"

the fastest:

Page 171: Ruby MVC from scratch with Rack

Spoiler spoiler Alert!

+

"Standard Ruby + Thin/Unicorn + Rails !is about the slowest possible combination"

the fastest:

Page 172: Ruby MVC from scratch with Rack

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:

Page 173: Ruby MVC from scratch with Rack

*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

Page 174: Ruby MVC from scratch with Rack

# hello_world.rbrequire 'rack'!!!!!!!!

app = Rack::Builder.new do !!!end

$ ruby hello_world.rb

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

$ rackup

map '/' do! end

Rack::Handler::WEBrick.run app, :Port => 9292

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

use Rack::CommonLogger

$ ruby -r rack -e 'Rack::Handler::Thin.run lambda { |env| [200, {}, ["ok"]] }'

$ rackup -b 'run proc { [200, {}, [Time.now.to_s]] }'

for the curious:

Page 175: Ruby MVC from scratch with Rack

that’s all