sham_rackとは
ShamRack plumbs HTTP requests into Rack.
(ShamRackはHTTPリクエストをRackの中へ送り込みます。)
HTTPリクエストをRackに放り込んで、登録した処理を行う。
Rackミドルウェアのように見える。
何ができるか
HTTPリクエストをリクエストホストに大して投げる前にフックして、あたかも処理されたようにレスポンスを返すことができる。
つまり、StubやMockのようなことができる。
注意点
2015/01/16追記。
あるホストをsham_rackに追加すると、そのホストに対する全てリクエストがsham_rackに食われてしまう(ように見える)。
sham_rackではNet::HTTPにShamRack::NetHTTPをextendしているため、
Net:HTTPを使ったライブラリのリクエストはsham_rackに流れてしまう。
これを回避するためには、TCPSocketやExconの様なsocketを使った通信を行う他ない。
(勉強中なので他のやり方があったら是非教えてください)
使い方
ShamRack.atでリクエストホストを第一引数に、期待する処理をブロックで渡す。
ブロックはRackミドルウェアのcallメソッド内の処理を書くのとほぼ同じ。
ブロック内ではリクエストの情報をRackのEnvとして受け取れる。
ブロックで渡した処理はアプリとしてShamRackに登録される。
HTTPリクエストのStub
例えば、http://www.google.co.jp/ にGETしたら"Hello, world!"が返ってくるようにStubする。
require 'sham_rack'
ShamRack.at("www.google.co.jp") do |env|
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
end
response = Net::HTTP.new("www.google.co.jp", 80).get("/")
response.code #=> "200"
response.body #=> "Hello, world!"
HTTPSリクエストのStub
HTTPSの場合は、ShamRack.atの第二引数に443番ポートを指定する。
require 'sham_rack'
ShamRack.at("www.google.co.jp", 443) do |env|
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
end
https = Net::HTTP.new("www.google.co.jp", 443)
https.use_ssl = true
response = https.get("/")
response.code #=> "200"
response.body #=> "Hello, world!"
複数のリクエスト先のStub
複数書くだけ。
ShamRack.at("www.google.co.jp") do |env|
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
end
ShamRack.at("www.google.co.jp", 443) do |env|
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
end
他のStubの書き方
2015/01/16追記。
sham_rackのREADME.mdにも書いてあるが、以下のようにstubすると、同一ホストで登録していないパスへのアクセスが404になる。
これはdefault_responseとしてsham_rackに定義されている。
@stub_app = ShamRack.at("www.google.co.jp", 443).stub
@stub_app.register_resource("/", "Hello, world!", "text/plain")
https = Net::HTTP.new("www.google.co.jp", 443)
https.use_ssl = true
response = https.get("/")
response.code #=> "200"
response.body #=> "Hello, world!"
response = https.get("/")
response.code #=> "404"
response.body #=> "Not found"
Stubを止める場合
アプリをアンマウントする。登録したアプリ全てがアンマウントされる。
ShamRack.at("www.google.co.jp") do |env|
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
end
puts "----- google (stub) -----"
uri = URI.parse("http://www.google.co.jp/")
res = http(uri).get(uri.path)
p res.code #=> "200"
p res.body #=> "Hello, world!"
ShamRack.unmount_all
puts "----- google -----"
uri = URI.parse("http://www.google.co.jp/")
res = http(uri).get(uri.path)
p res.code #=> "200"
p res.body #=> "<!doctype html><html itemscope=\"\" itemtype=..."
ShamRackにRackミドルウェアも追加する場合
2015/01/16追記。
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
if env["REQUEST_METHOD"] == "GET" && env["PATH_INFO"] == "/"
@app.call(env)
else
[404, { "Content-type" => "text/plain" }, ["Not Found"]]
end
end
end
rackapp = Proc.new do
["200 OK", { "Content-type" => "text/plain" }, ["Hello, world!"]]
end
app = MyMiddleware.new(rackapp)
ShamRack.at("www.google.co.jp") do |env|
app.call(env)
end
README.mdに書いてある通り、以下のような書き方もできる。
ShamRack.at("www.google.co.jp").rackup do |env|
use MyMiddleware
run rackapp
end
感想
- Rack層でstubができるのですごく便利!
- テストやMock開発に役立ちそう