これは codereading グループの活動の一貫の記事なので、broken English で書かれています。
おもな活動場所は Issues · codereading/rack なので、興味があったらチェックしてみてください。
This is part of codereading group activity, so written in broken English.
We are in Issues · codereading/rack.
Thank you for any discussion, notation, and pointing out errors.
In this series of article (if I can continue), I start from /lib/rack/lobster.rb, the easiest rack application, to see through all of the rack codes.
First, see the lobster
At first, clone the rack repository and run the following command. It runs WEBrick.
$ ruby lib/rack/lobster.rb [2012-05-08 08:15:56] INFO WEBrick 1.3.1 [2012-05-08 08:15:56] INFO ruby 1.9.2 (2011-02-18) [i686-linux] [2012-05-08 08:15:56] WARN TCPServer Error: Address already in use - bind(2) [2012-05-08 08:15:56] INFO WEBrick::HTTPServer#start: pid=3408 port=9292
By accessing to http://localhost:9292/, you see cute lobster like this.
,.---._
,,,, / `,
\\\\ / '\_ ;
|||| /\/``-.__\;'
::::/\/_
{{`-.__.-'(`(^^(^^^(^ 9 `.========='
{{{{{{ { ( ( ( ( (-----:=
{{.-'~~'-.(,(,,(,,,(__6_.'=========.
::::\/\
|||| \/\ ,-'/,
//// \ `` _/ ;
'''' \ ` .'
`---'
In order to quit, because this application blocks SIGINT, you should send SIGKILL from kill command.
$ ps -ef | grep "lobster" tomita 3408 3137 0 08:15 pts/4 00:00:03 ruby lib/rack/lobster.rb tomita 3669 2789 0 08:30 pts/3 00:00:00 grep --color=auto lobster $ kill -9 3408
Code of lobster.rb
In this file, we define Rack::Lobster class as an application class, and run it if $0 == __FILE__, that is, this file is executed directly.
If you require this file from irb, WEBrick never runs.
LobsterString is ascii art data of the lobster. Paste this code into irb to check it.
LambdaLobster is the lambda-version of basically the same application, but this is used only for testing in spec_lobster.rb. This proves that rack can take lambda, not an instance of some class.
In order to run LambdaLobster, rewrite the last code like this, then re-run.
if $0 == __FILE__
require 'rack'
require 'rack/showexceptions'
Rack::Handler::WEBrick.run \
Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster::LambdaLobster)),
:Port => 9292
end
Inside the call method is the logic of this application. Here uses Request and Response class, and not in Lambda version. Check these applications as samples.
At last, we runs this application with middlewares. About middleware, check #151 Rack Middleware - RailsCasts or something.
Middleware usually takes an app as the first constructor argument, and respond to call. In normal condition, a middleware calls the given app's call method.
Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new))
Rack::Lint.new(Rack::Lobster.new) returns a callable object, which calls Rack::Lobster.new.call in it.
Then, check these two middlewares.
Rack::ShowExceptions
This class is in /lib/rack/showexceptions.rb.
ShowExceptions rescues all errors raised in the given app, and report them for debugging. Try clicking the "crash" button of lobster.rb without this and with this.
From now on, I just point out something interesting.
env["rack.errors"]
This is specified in SPEC as "The Error Stream". Usually this is connected to $stderr by Rack::Handler('s subclass).
It should respond to write, puts, etc. likes a writable IO object.
In contrast, env["rack.input"] represents raw HTTP POST data. This is also specified as "The Input Stream". This should behave like a readable IO object.
Rack::Lint
Rack::Lint is for checking that your app is satisfying rack SPEC. At first, assert method is defined.
module Assertion
def assert(message, &block)
unless block.call
raise LintError, message
end
end
end
This method is used not only in this top class, but in InputWrapper and ErrorWrapper. Therefore it is defined in a module, and included.
Lint checks the following points.
- env, contents of env
- env['rack.input'] (using wrapper)
- env['rack.error'] (using wrapper)
- status
- header
- body (in
each)
def call(env=nil)
dup._call(env)
end
is maybe for protecting the internal state of a Lint instance.
There is an idiom in _call.
def _call(env)
# by return self as the body, self.each will be called on presentation
# Lint#each validates @app.body and a caller of self.each
[status, headers, self]
end
Other parts describe rack SPEC in ruby. Check them to get detail view of the specification.
The point I noticed is hash key of headers should be string, symbol is not allowed.
assert("header key must be a string, was #{key.class}") {
key.kind_of? String
}
That's all for the first article.
I will see through WEBrick.run tomorrow.
