Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
rack-rewrite README in japanese

rack-rewrite

rewriteルールを適用するための Rack ミドルウェアです。 Apache mod_rewrite ルールを書く代わりに使えます。

References

Usage

Sample rackup file

gem 'rack-rewrite', '~> 1.0.0'
require 'rack/rewrite'
use Rack::Rewrite do
  rewrite '/wiki/John_Trupiano', '/john'
  r301 '/wiki/Yair_Flicker', '/yair'
  r302 '/wiki/Greg_Jastrab', '/greg'
  r301 %r{/wiki/(\w+)_\w+}, '/$1'
end

Railsでの使い方

config.gem 'rack-rewrite', '~> 1.0.0'
require 'rack/rewrite'
config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
  rewrite '/wiki/John_Trupiano', '/john'
  r301 '/wiki/Yair_Flicker', '/yair'
  r302 '/wiki/Greg_Jastrab', '/greg'
  r301 %r{/wiki/(\w+)_\w+}, '/$1'
end

ユースケース

現行のサイトを、新規サイトに置き換えたい

既存コンテンツへのリンク、いわゆる「Google Juice」の恩恵を受けつつ、新サイトのクールなURLにしたいです。 301でパッパと置き換えましょう。

r301 '/contact-us.php', '/contact-us'
r301 '/wiki/John_Trupiano', '/john'

古いルーティングをリタイアさせる

たとえば、モデル名を変えたら、規約にしたがってたくさんの routes が巻き添えを食って変わりました。 「ハード=コード」されたリンクの置き換えとかをして、死者が出るぐらいならリライトしましょう。

rewrite %r{/features(.*)}, '/facial_features$1'

CNAME の代わり

DNSをイジれない環境でも、 Rack::Rewrite であらゆるアクセスをカノニカルなドメインに 集約させられます。以下のルールでは、 $& を使ってRequest URIをまるごとキャプチャーしています。

r301 %r{.*}, 'http://mynewdomain.com$&', :if => Proc.new {|rack_env|
  rack_env['SERVER_NAME'] != 'mynewdomain.com'
}

サイト メンテナンス

多くの Capistrano ユーザーは、下記のような Apache のおまじないを仕込んでるかと思います。 何人の管理者がこのリライトルールを理解していることやら……

RewriteCond %{REQUEST_URI} !\.(css|jpg|png)$
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]

メンテナンスファイルが存在したらメンテナンスページにぶっ飛ばす感じですね。 Capistrano 的には一瞬でメンテナンスにできたりします。こんな感じですかね。

cap deploy:web:disable REASON=upgrade UNTIL=12:30PM

上のルール、わけわかんないし、 Rack::Rewrite で置き換えちゃいましょう。

maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
send_file /.*/, maintenance_file, :if => Proc.new { |rack_env|
  File.exists?(maintenance_file) && rack_env['PATH_INFO'] !~ /\.(css|jpg|png)/
}

Ruby 1.9 なら、(いわゆる「戻り読み」のおかげで)よりクールにできます。

maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
send_file /(.*)$(?<!css|png|jpg)/, maintenance_file, :if => Proc.new { |rack_env|
  File.exists?(maintenance_file)
}

oniguruma gem を使えば 1.8 でも同様のことが出来ます (マッチに Oniguruma::ORegexp も使えるっつーことです。クールでしょ?)

maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
send_file Oniguruma::ORegexp.new("(.*)$(?<!css|png|jpg)"), maintenance_file, :if => Proc.new { |rack_env|
  File.exists?(maintenance_file)
}

Rewrite Rules

:rewrite

#rewrite は、シンプルに PATH_INFO, QUERY_STRING, REQUEST_URI あたりの HTTP ヘッダーを上書きし、次の Rack スタックに渡します。エンドユーザのブラウザには何も出来なかった、 ではなく何も起こっていないように見えます。こんなんです:

rewrite '/wiki/John_Trupiano', '/john'   # [1]
rewrite %r{/wiki/(\w+)_\w+}, '/$1'       # [2]

ここで、 [1] では、エンドユーザのブラウザのアドレスバーは /wiki/John_Trupiano を指してる んですけれど、以降の Rack スタックとかアプリに渡っている PATH_INFO とか REQUEST_URI は /john になっているはずです。

で、 [2] は正規表現でマッチさせる一例です。 [1] を一般化してるイメージなんですけど、分かります? むかし、2009年の9月にサイト ( www.smartlogicsolutions.com ) を再構築した際の実際のルールだったりします。

:r301, :302

#r301 とか #r302 も、 #rewrite のように使えます。でも、なにせ 301 や 302 でリダイレクトさせちゃうんで、Rack スタックは中断されます。

r301 '/wiki/John_Trupiano', '/john'                # [1]
r301 '/wiki/(.*)', 'http://www.google.com/?q=$1'   # [2]

ルールは、上から順にマッチしていくんだと覚えといてください。なので、 「デフォルト」のルールを設定することも可能ですね。上記は、 John のことは知っていますが()、知らない人間については [2] で Google先生に聞きにいく感じです。

:send_file, :x_send_file

#send_file とか #x_send_file も、同じように使って構いません。 ただし、第二パラメーターである 'to' が、ヘッダで渡すPATHというよりは、 実際に表示させたいファイルへの path になります。

send_file /*/, 'public/spammers.htm', :if => Proc.new { |rack_env|
  rack_env['HTTP_REFERER'] =~ 'spammers.com'
}
x_send_file /^blog\/.*/, 'public/blog_offline.htm', :if => Proc.new { |rack_env|
  File.exists?('public/blog_offline.htm')
}

Tips

“QUERY_STRING” を保持する

いわゆる “QUERY_STRING” を保持したままリライトしたいのであれば、 正規表現のマッチ時のグループを使いましょう。

rewrite %r{/wiki/John_Trupiano(\?.*)?}, '/john$1'

こんな感じです。 (?.*) で ?hoge=fuga みたいなのにマッチし、 $1 に流し込めます。

Rule Guards

Procやlambdaを :if オプションでガードとして指定できます。 true か false を 返すようにしましょう。下記は、ファイルシステムを参照してメンテナンスにしたりしなかったりする サンプルです。

maintenance_file = File.join(RAILS_ROOT, 'public', 'system', 'maintenance.html')
x_send_file /.*/, maintenance_file, :if => Proc.new { |rack_env| 
  File.exists?(maintenance_file)
}

より複雑な Rewriting

どのルールも、第2引数に Proc を指定することが出来ます。 この仕組みを利用すれば、例えば以下のような感じで、午前0時から8時までの間の アクセスをすべて「ご利用できません」にすることもできます。

rewrite %r{(.*)}, lambda { |match, rack_env|
  Time.now.hour < 8 ? "/unavailable.html" : match[1]
}

それでもよくわかんない

ソース見ろや~

Copyright © 2009-2010 John Trupiano. See LICENSE for details.

そして、この邦訳は ブロッグ で利用するため、 Uchio Kondo が勝手に訳しています。ゴメンネ。なにかあれば twitter などにどうぞ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment