<%= image_tag "/images/rack-logo.png", :class => "right", :alt => "rack logo" %> Zaczynamy od postawienia stałej konstrukcji nośnej: stojaka (ang. rack). Następnie wstawiamy do stojaka wymienialne moduły, które zestawiamy według potrzeb, na przykład:
Christian Neukirchen, Rack: a Ruby Webserver Interface
Dabbling in my own web framework experiments, I noticed that there is a lot of code duplication among frameworks since they essentially all do the same things. And still, every Ruby web framework developer is writing his own handlers for every webserver he wants to use. Hopefully, the framework users are satisfied with that choice.
Nieformalnie, modułem/aplikacją Rack jest obiekt odpowiadający na wywołanie #call z haszem jako jedynym argumentem i zwracającym tablicę trzech elementów: status, headers i body.
:::ruby
# app.rb
require 'rubygems'
require 'rack'
App = lambda do |env|
[
200, # status
{"Content-Type" => "text/html; charset=utf-8"}, # headers
[ "hello world" ] # body
]
end
Rack::Handler::Thin.run App, :Port => 9292
Tę aplikację uruchamiamy po prostu tak (bez wstawiania do stojaka):
ruby app.rb
Argument env jest haszem, wyglądającym mniej więcej tak:
:::ruby
{
"HTTP_ACCEPT" => "*/*",
"HTTP_HOST" => "inf.univ.gda.pl",
"HTTP_PRAGMA" => "no-cache",
"HTTP_USER_AGENT" => "curl/7.12.2 ..."
"PATH_INFO" => "/",
"QUERY_STRING" => "",
"REMOTE_ADDR" => "127.0.0.1",
"REMOTE_HOST" => "127.0.0.1",
"REQUEST_METHOD" => "GET"
"REQUEST_PATH" => "/",
"REQUEST_URI" => "http://inf.univ.gda.pl/",
"SCRIPT_NAME" => "",
"SERVER_PORT" => "80",
"SERVER_PROTOCOL" => "HTTP/1.1",
}
:::ruby
# helloworld.rb
class HelloWorld
def call(env)
[200, {"Content-Type" => "text/html"}, ["hello world"]]
end
end
Tę klasę wstawiamy do stojaka (jako jedyny moduł):
:::ruby
# app2.ru
require 'helloworld'
run HelloWorld.new
i uruchamiamy wpisując w wierszu poleceń:
rackup -s thin -p 9292 app2.ru
Between the server and the framework, Rack can be customized to your applications needs using middleware, for example:
Aplikacje składamy tak, jak składamy funkcje:
:::ruby
app = Rack::CommonLogger.new(
Rack::ShowExceptions.new(
Rack::Lint.new(MyRackApp.new)))
Albo tak:
:::ruby
app = MyRackApp.new
app = Rack::Lint.new(app)
app = Rack::ShowExceptions.new(app)
app = Rack::CommonLogger.new(app)
:::ruby
# rapp.ru
require 'helloworld'
Rapp = Rack::Builder.new do
use Rack::Lint
run HelloWorld.new
end
use Rack::CommonLogger
run Rapp
Aplikację rapp.ru uruchamiamy tak:
rackup rapp.ru
Polecenie rackup
doda za nas wiersz
z Rack::Handler oraz bibliotekę
rubygems i rack.
:::ruby
require 'rack/request' ; require 'rack/response'
require 'bigdecimal' ; require 'bigdecimal/math'
include BigMath
module Rack
class Pi
def call(env)
req = Request.new(env)
prec = req.GET["prec"].to_i
res = Response.new
res.write "<title>PI</title>"
res.write "<p>"
res.write PI(prec + 1).to_s("10F")
res.write "</p>"
res.finish
end
end
end
:::ruby
# app3.rb
require 'rack-math' # gem z aplikacjami Rack:Euler, Rack:Pi...
App3 = Rack::Builder.new do
map '/pi' do
run Rack::Pi.new
end
map '/euler' do
run Rack::Euler.new
end
end
Umieszczamy aplikację w stojaku, który uruchamiamy za pomocą
rackup
:
:::ruby
# app3.ru
require 'app3'
run App3
Włączamy jeszcze jedną aplikację z gemu Rack:
:::ruby
# app4.rb
require 'rack/lobster' ; require 'app3'
App4 = Rack::Builder.new do
map '/math' do
run App3
end
map '/lobster' do
run Rack::Lobster.new
end
end
Uruchamiamy aplikację app4.ru:
:::ruby
require 'app4'
use Rack::CommonLogger
run App4
Korzystamy z layoutu i szablonów. Kopiujemy zmienne do szablonów.
:::ruby
# sinatra-math/pi.rb
require 'bigdecimal'
require 'bigdecimal/math'
include BigMath
module Sinatra
class Pi < Sinatra::Base
get '/?' do
prec = params[:prec].to_i
@pi = PI(prec + 1).to_s("10F")
@cname = 'PI'
erb :pi
end
end
end
Poniższą aplikację urozmaicimy homarem:
:::ruby
# app5.rb
require 'sinatra/base' ; require 'sinatra-math' ; require 'rack/lobster'
SinatraApp5 = Rack::Builder.new do
map '/' do
run Sinatra::Pi.new
end
map '/lobster' do
run Rack::Lobster.new
end
end
i uruchomimy ją korzystając via prosty plik:
:::ruby
# app5.ru
require 'app5'
run SinatraApp5
Yehuda Katz, The Russian Doll Pattern: Mountable apps in Rails 3
One of the hottest new features in Rails 3 is the ability to embed a Rails application in another Rails application. This allows the development of components that range from user authentication to a fully featured forum. These components can then be distributed as gems and fully integrated with another application. In fact, user private messaging could be a stand alone app, which is then mounted into a forum app, and finally mounted into your own custom app.
Nic nie jest tak łatwe, jak wygląda.
Interestingly, I really didn’t care for many of the (very rough) ideas expressed in Yehuda’s mountable Rails apps (Rails 3) session — in particular I really had no clue why they kept comparing Rails (a framework) to Drupal (a CMS). But, that said, the talk did do a great job stimulating discussion about alternative approaches in „CabooseConf” — apparently just a small room with, uh, tables and stuff — between myself, Bryan, Josh, Ted and others. For this reason it definitely belongs in my favorite sessions list.
Każde rozwiązanie ujawnia nowe problemy.
— Edward A. Murphy
[mountable app slices] are a challenging problem, and there are a lot of issues in terms of sharing application state, resolving cross-app dependencies, and so on. I hope that we’ll have an elegant solution to this soon; but I suspect that the real answer may be in making component-sized micro-apps easier to mount and integrate rather than taking an „app slices” or engines approach (if the latter case prevails, the Radiant extensions system has some stuff we can learn from).