And the Greatest of These Is ... Space

Preview:

DESCRIPTION

Presentation given at Rails Underground; revision of earlier version given at Railsconf 2009.

Citation preview

And the Greatest of These Is... Rack SupportBen Scofield – Viget Labs

Saturday, July 25, 2009

Saturday, July 25, 2009

Application TemplatesSaturday, July 25, 2009

Nested Attribute Assignmentflickr: angelrays

Saturday, July 25, 2009

ActiveRecord::Base#touchflickr: jjjohn

Saturday, July 25, 2009

DB Seedingflickr: richardthomas78

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Rack

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

def call(env) [ status, # 200 headers, # {"Content-Type" => "text/html"} body # ["<html>...</html>"] ]end

Saturday, July 25, 2009

Saturday, July 25, 2009

requestapplication

responseSaturday, July 25, 2009

requestapplication

response

middleware

middleware

Saturday, July 25, 2009

Saturday, July 25, 2009

Rack::Profilerflickr: oberazzi

Saturday, July 25, 2009

Rack::MailExceptionsflickr: warmnfuzzy

Saturday, July 25, 2009

Rack::Cacheflickr: timpatterson

Saturday, July 25, 2009

rack::cascade

Rack::Cascadeflickr: _at

Saturday, July 25, 2009

Rack in Rails

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

> rake middlewareuse Rack::Lockuse ActionDispatch::ShowExceptions, [true]use ActionDispatch::Callbacks, [true]use ActionDispatch::Rescue, [#<Method: ...>]use ActionDispatch::Session::CookieStore, [{...}]use ActionDispatch::ParamsParseruse Rack::MethodOverrideuse Rack::Headuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacherun ActionController::Dispatcher.new

Saturday, July 25, 2009

> rake middlewareuse Rack::Lockuse ActionDispatch::ShowExceptions, [true]use ActionDispatch::Callbacks, [true]use ActionDispatch::Rescue, [#<Method: ...>]use ActionDispatch::Session::CookieStore, [{...}]use ActionDispatch::ParamsParseruse Rack::MethodOverrideuse Rack::Headuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacherun ActionController::Dispatcher.new

> rake middlewareuse Rack::Lockuse ActionDispatch::ShowExceptions, [true]use ActionDispatch::Callbacks, [true]use ActionDispatch::Rescue, [#<Method: ...>]use ActionDispatch::Session::CookieStore, [{...}]use ActionDispatch::ParamsParseruse Rack::MethodOverrideuse Rack::Headuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacherun ActionController::Dispatcher.new

Saturday, July 25, 2009

app = Rack::Builder.new { use Rails::Rack::LogTailer unless options[:detach] use Rails::Rack::Debugger if options[:debugger]

map "/" do use Rails::Rack::Static run ActionController::Dispatcher.new end}.to_app

Saturday, July 25, 2009

Rails::Initializer.run do |config| config.middleware.insert_before Rack::Head, Ben::Asplode config.middleware.swap Rack::Lock, Ben::Lock config.middleware.use Rack::MailExceptionsend

Saturday, July 25, 2009

Rails::Initializer.run do |config| config.middleware.insert_before Rack::Head, Ben::Asplode config.middleware.swap Rack::Lock, Ben::Lock config.middleware.use Rack::MailExceptionsend

# in config/initializers/middlewares.rb

ActionController::Dispatcher.middleware.insert_before 'Rack::Head', AppenderActionController::Dispatcher.middleware.delete 'Rack::Lock'ActionController::Dispatcher.middleware.use Prepender

Saturday, July 25, 2009

Metalflickr: lrargerich

Saturday, July 25, 2009

http://www.kiwibean.com/

Saturday, July 25, 2009

Saturday, July 25, 2009

EXPAND: testing metalPLEASE STAND BYTHIS IS ONLY A TEST

Saturday, July 25, 2009

Saturday, July 25, 2009

METAL

Sinatra

Saturday, July 25, 2009

Cool Tricks

Saturday, July 25, 2009

Rack::Bugflickr: catdancing

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

requires </body>

Saturday, July 25, 2009

Progressive CachingSaturday, July 25, 2009

Progressive CachingSaturday, July 25, 2009

Progressive CachingSaturday, July 25, 2009

Saturday, July 25, 2009

class SessionsController < ApplicationController def create # ... session[:personalize] = { :logged_in => true, :quicklist => current_user.quicklist.episode_ids, :subscriptions => current_user.subscription_ids } endend

Saturday, July 25, 2009

class ChannelsController < ApplicationController caches_page :show def show @channel = Channel.find(params[:id]) endend

Saturday, July 25, 2009

$(document).ready(function() { $.getJSON('/personalize', function(data) {

// identify quicklisted episodes $.each(data['quicklist'], function() { $('#episode_'+this).addClass('listed'); }); // switch navigation if (data['logged_in']) { $('#logged_in_nav').show(); } // etc. });});

Saturday, July 25, 2009

class Personalizer def self.call(env) if env["PATH_INFO"] =~ /^/personalize/ [ 200, {"Content-Type" => "application/javascript"}, env['rack.session'][:personalize].to_json ] else [404, {"Content-Type" => "text/html"}, ["Not Found"]] end endend

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

standard

progressive 0.0960.039

0.617

class PullList def self.call(env) if env["PATH_INFO"] =~ /^\/pulls/ [ 200, {"Content-Type" => "application/javascript"}, [Pull.by_user(user).for_date(date).map {|i| i.item_id}.to_json]] else [404, {"Content-Type" => "text/html"}, ["Not Found"]] end endend

Saturday, July 25, 2009

class PullList def self.call(env) if env["PATH_INFO"] =~ /^\/pulls/ [ 200, {"Content-Type" => "application/javascript"}, [env['rack.session'][:pulls].to_json] ] else [404, {"Content-Type" => "text/html"}, ["Not Found"]] end endend

standard

progressive

progressive v2 0.023

0.096

0.043

0.039

0.617

Saturday, July 25, 2009

DIY

Saturday, July 25, 2009

requestapplication

response

middleware

middleware

Saturday, July 25, 2009

Rack::EmbiggenerSaturday, July 25, 2009

Saturday, July 25, 2009

class Embiggener < Test::Unit::TestCase def test_embiggener_should_embiggen_known_url body = ["Hello! I'm at http://bit.ly/31IqMl"] assert_equal ["Hello! I'm at http://cnn.com"], Rack:: Embiggener.embiggen_urls(body) end

def test_embiggener_should_embiggen_multiple_urls body = [ "Hello! I'm at http://bit.ly/31IqMl", "And I'm at http://bit.ly/31IqMl" ] assert_equal [ "Hello! I'm at http://cnn.com", "And I'm at http://cnn.com" ], Rack::Embiggener.embiggen_urls(body) endend

Saturday, July 25, 2009

module Rack class Embiggener def self.embiggen_urls(original_body, login, key) new_body = [] original_body.each { |line| bits = line.scan(/(http:\/\/bit\.ly\/(.{6}))/)

new_body << bits.uniq.inject(line) do |body, bit| original_bit, hash = *bit new_url = embiggened_url(hash, login, key) body.gsub(original_bit, new_url) if new_url end } new_body end

# ... endend

Saturday, July 25, 2009

module Rack class Embiggener # ...

def self.embiggened_url(hash, login, key) url = [ "http://api.bit.ly/expand?version=2.0.1", "shortUrl=http://bit.ly/#{hash}", "login=#{login}", "apiKey=#{key}" ].join('&') response = JSON.parse(Net::HTTP.get_response(URI.parse(url))) if response['statusCode'] = 'OK' embiggened_url = response['results'][hash]['longUrl'] end end

# ... endend

Saturday, July 25, 2009

module Rack class Embiggener # ... attr_accessor :api_login, :api_key def initialize(app, api_login, api_key) @app = app @api_login = api_login @api_key = api_key end

def call(env) status, headers, body = @app.call(env) headers.delete('Content-Length') response = Rack::Response.new( Rack::Embiggener.embiggen_urls(body, api_login, api_hash), status, headers ) response.finish return response.to_a end endend

Saturday, July 25, 2009

module Rack class Embiggener def self.embiggen_urls(original_body, login, key) new_body = [] original_body.each { |line| bits = line.scan(/bit\:(\w+?)/)

new_body << bits.uniq.inject(line) do |body, bit| hash = bit.first original_bit = "bit:#{hash}" new_url = embiggened_url(hash, login, key) body.gsub(original_bit, new_url) if new_url end } new_body

end # ... endend

Saturday, July 25, 2009

module Rack class Embiggener # ...

def self.embiggened_url(hash, login, key) url = [ "http://api.bit.ly/expand?version=2.0.1", "hash=#{hash}", "login=#{login}", "apiKey=#{key}" ].join('&') response = JSON.parse(Net::HTTP.get_response(URI.parse(url))) if response['statusCode'] = 'OK' embiggened_url = response['results'][hash]['longUrl'] end end

# ... endend

Saturday, July 25, 2009

WARNINGexception handling

Saturday, July 25, 2009

WARNINGexception handling

formerly:

Saturday, July 25, 2009

Rack::Hoptoad** unofficial; thoughtbot is not responsible for this middleware; do not taunt rack::hoptoad; pregnant women should consult their doctors before using rack::hoptoad

Saturday, July 25, 2009

Saturday, July 25, 2009

> rake middlewareuse Rack::Lockuse ActionDispatch::ShowExceptions, [true]use ActionDispatch::Callbacks, [true]use ActionDispatch::Rescue, [#<Method: ...>]use ActionDispatch::Session::CookieStore, [{...}]use ActionDispatch::ParamsParseruse Rack::MethodOverrideuse Rack::Headuse ActiveRecord::ConnectionAdapters::ConnectionManagementuse ActiveRecord::QueryCacherun ActionController::Dispatcher.new

Saturday, July 25, 2009

ActionController::Dispatcher.middleware.delete ActionDispatch::ShowExceptionsActionController::Dispatcher.middleware.delete ActionDispatch::Rescue ActionController::Dispatcher.middleware.use 'Rack::Hoptoad', 'my-api-key'ActionController::Dispatcher.middleware.use 'Rack::ShowExceptions'

Saturday, July 25, 2009

module Rack class Hoptoad def initialize(app, api_key) @app = app @api_key = api_key end

def call(env) @app.call(env) rescue => exception notify_hoptoad(exception, env) raise exception end

# ... endend

Saturday, July 25, 2009

module Rack class Hoptoad def notify_hoptoad(exception, env) headers = { 'Content-type' => 'application/x-yaml', 'Accept' => 'text/xml, application/xml' } url = URI.parse("http://hoptoadapp.com/notices")

http = Net::HTTP.new(url.host, url.port)

data = { :api_key => @api_key, :error_message => "#{exception.class}: #{exception}", :backtrace => exception.backtrace, :request => {}, :session => env['rack.session'], :environment => env }

response = http.post( url.path, stringify_keys(:notice => data).to_yaml, headers ) if response != Net::HTTPSuccess # Hoptoad post failed end end endend

Saturday, July 25, 2009

Saturday, July 25, 2009

Intangibles

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Saturday, July 25, 2009

Thank You

ben scofield - @bscofield - http://www.viget.com/extend - http://www.speakerrate.com/bscofield

Saturday, July 25, 2009

Recommended