We mentioned earlier that between the server and the framework, Rack can be customized to your applications needs using middleware.
The fundamental idea behind Rack middleware is
Si tenemos una app Rack rack_app
y dos middlewares con nombres
MiddleWare1
y
MiddleWare2
que queremos usar, podemos escribir esto:
Rack::Handler::Thin.run Middleware1.new(Middleware2.new(rack_app))Si necesitamos pasar opciones en el segundo argumento la llamada quedaría mas o menos como esto:
Rack::Handler::Thin.run( Middleware1.new( Middleware2.new(rack_app, options2), options1) )Si fueran mas de dos middlewares el correspondiente código se volverá aún mas ilegible y hace mas fácil que metamos la pata cuando queramos hacer algo como - por ejemplo -modificar el orden de los middleware.
La clase Rack::Builder implementa un pequeño DSL para facilitar la construcción de aplicaciones Rack.
Rack::Builder is the thing that glues various Rack middlewares and applications together and convert them into a single entity/rack application.
A good analogy is comparing Rack::Builder object with a stack, where at the very bottom is your actual rack application and all middlewares on top of it, and the whole stack itself is a rack application too.
Dada la aplicación:
infinity = Proc.new {|env| [200, {"Content-Type" => "text/html"}, env.[inspect]] } Rack::Handler::Mongrel.run infinity, :Port => 9292
Podemos reescribirla:
[~/sinatra/rack/rack-builder/map]$ cat app_builder.rb require 'rack' infinity = Proc.new {|env| [200, {"Content-Type" => "text/html"}, [env.inspect]]} builder = Rack::Builder.new builder.run infinity Rack::Handler::Thin.run builder, :Port => 9292o bien:
[~/sinatra/rack/rack-builder/map]$ cat app_builder2.rb require 'rack' infinity = Proc.new {|env| [200, {"Content-Type" => "text/html"}, [ env.inspect ]] } builder = Rack::Builder.new do run infinity end Rack::Handler::Thin.run builder, :Port => 9292
[~/local/src/ruby/sinatra/rack/rack-builder/simple1]$ cat app.rb require 'rack' require 'rack/server' app = Rack::Builder.new do use Rack::CommonLogger use Rack::ShowExceptions use Rack::Lint map "/chuchu" do run lambda { |env| [ 200, {}, ["hello"]] } end map "/chachi" do run lambda { |env| [ 200, {}, ["world"]] } end run lambda { |env| [ 200, {}, ["everything"]] } end Rack::Server.start :app => app
[~/rack/rack-from-the-beginning(master)]$ cat hello_world.rb # hello_world.rb require 'rack' require 'rack/server' class EnsureJsonResponse def initialize(app = nil) @app = app end # Set the 'Accept' header to 'application/json' no matter what. # Hopefully the next middleware respects the accept header :) def call(env) env['HTTP_ACCEPT'] = 'application/json' puts "env['HTTP_ACCEPT'] = #{env['HTTP_ACCEPT']}" @app.call(env) if @app end end class Timer def initialize(app = nil) @app = app end def call(env) before = Time.now status, headers, body = @app.call(env) if @app headers['X-Timing'] = (Time.now - before).to_i.to_s [status, headers, body] end end class HelloWorldApp def initialize(app = nil) @app = app end def self.call(env) [200, {}, ['hello world!']] end end # put the timer at the top so it captures everything below it app = Rack::Builder.new do use Timer # put the timer at the top so it captures everything below it use EnsureJsonResponse run HelloWorldApp end Rack::Server.start :app => app
~/rack/rack-from-the-beginning(master)]$ cat Rakefile desc "run the server" task :default do sh "rackup" end desc "run the server hello_world.rb" task :server do sh "ruby hello_world.rb" end desc "run the client" task :client do sh %q{curl -v 'http://localhost:9292'} end desc "run the client for hello_world" task :client2 do sh %q{curl -v 'http://localhost:8080'} end
[~/rack/rack-from-the-beginning(master)]$ rake server ruby hello_world.rb >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:8080, CTRL+C to stop
[~/rack/rack-from-the-beginning(master)]$ rake client2 curl -v 'http://localhost:8080' * About to connect() to localhost port 8080 (#0) * Trying ::1... Connection refused * Trying 127.0.0.1... connected * Connected to localhost (127.0.0.1) port 8080 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8y zlib/1.2.5 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 200 OK < X-Timing: 0 < Connection: close < Server: thin 1.5.1 codename Straight Razor < * Closing connection #0 hello world!
[~/rack/rack-from-the-beginning(master)]$ rake server ruby hello_world.rb >> Thin web server (v1.5.1 codename Straight Razor) >> Maximum connections set to 1024 >> Listening on 0.0.0.0:8080, CTRL+C to stop env['HTTP_ACCEPT'] = application/json