Rack::Auth::IPをリリースしました
GitHubPostRecieverでGitHub以外からのpostは受け付けたくなかったのでRackでIPのアクセス制限ができるRack::Auth::IPというミドルウェアを作ってみました。
rackupのファイルの中で、
require 'rack/auth/ip' # ローカルネットワークにアクセスを制限する use Rack::Auth::IP, %w( 192.168.0.0/24 ) # ブロックでも使える # ipにはIPAddrのインスタンスが与えられる use Rack::Auth::IP do |ip| Your::Model::IP.count({ :ip => ip.to_s }) != 0 end
のように使えます。
$ gem install rack-auth-ip
でインストールできます。
機能的に増やすということはあまりないと思いますが、
http://github.com/walf443/rack-auth-ip/tree/master
でソースコードを公開してますので、何かバグなリがあれば一報ください。
Rackのミドルウェアとして作ることで他の様々なアプリでも同じように設定できるようになるので、Rackは素晴らしいなと思います。
classXを使うと何がうれしいか
http://d.hatena.ne.jp/walf443/20080520/1211239672
で触れた宣言的なアクセサをGitHubPostRecieverに試しに適用してみた
+ has :host, :is => :ro, :kind_of => String, :required => true + has :port, :is => :ro, :kind_of => Integer, :default => 6667 + has :nick, :is => :ro, :kind_of => String, :required => true + has :user, :is => :ro, :kind_of => String, :lazy => true, :default => proc {|mine| mine.nick } + has :real, :is => :ro, :kind_of => String, :lazy => true, :default => proc {|mine| mine.nick } + has :template, :is => :ro, :kind_of => String, :required => true + def run method, json - json['commits'].each do |sha, commit| - CommitPingBot.new(@config['host'], @config['port'], { - 'nick', @config['nick'], - 'user', @config['user'], - 'real', @config['real'], - }).run("##{method}", View.new(@config['template'], commit).result) + json['commits'].reverse.each do |sha, commit| + CommitPingBot.new(@host, @port, { + 'nick' => @nick, + 'user' => @user, + 'real' => @real, + }).run("##{method}", View.new(@template, commit).result) end end end
元々のクラスだと、@configの中にどんな設定ができるのかということがわかりづらかったのですが、アクセサにしてしまえば、どの設定が必須なのかとかどんな値を指定すれば良いかが一目瞭然ですね。
Commit Bot for GitHub
GitHubでは、プロジェクトのコミットをfollowして興味のあるプロジェクトのコミットのみをまとめてみれるのですが、毎日それらを全部見るのはなかなか大変だったりします。
興味のあるプロジェクトの場合、割とfreenodeにIRCのチャンネルがあって、そこにjoinしていたりするので、#codereposみたいにコミットを流したくなり、今まではplaggerでGitHubのフィードをチェックしてNotify::IRCでポストしていたのですが、3つ、4つと設定するたびに、cronやplagger-ircdがどんどん増えていき、管理が大変になりました。
そこでそれらをまとめられないかなと自分で書くことにしました。GitHubはプロジェクトにURLを登録しておくと、そこにコミット情報をJSONで送ってくれる機能があるので、それらを受けて動作するものにしました。その方が即時性が高いし、定期アクセスさせないですむので向こうのサーバーにも優しいからです。
使ってるGemは、json、net-irc、rack ( mongrel )と比較的インストールも楽なのでぜひお試しください。ひょっとすると、各worker ( pluginのようなもの )に設定できる項目を宣言的に書いてもっとわかりやすくするために、この前のclassXを今後使うようにするかもしれません。( まだ gemにもなってないですがw )
http://github.com/walf443/github-post-reciever/tree/master
この手のプロジェクトはGitHubに色々あるのですが、自分のやつの特徴としては、サーバーがrequestを受けてresponseはすぐに返してしまい、非同期に処理をさせるために、Drb、Threadを使っていることです。この方がblockしないので向こうのサーバーには優しいのではないかなと思っている次第です。
Mooseっぽい柔軟なアクセサ
最近perl界隈で話題のMooseをちょくちょく弄ってみたりしているのですが、宣言的なアクセサは見通しも良くなるし、なかなか良いなと思ったんでRubyでもそれっぽく動作するやつを書いてみました。
Mooseっぽくコンストラクタも勝手に用意するようにしてみたのですが、これは好みが分かれるところのような気もするので、アクセサの部分は分離しても良いかなと思いました。ただ、あらかじめコンストラクタで引数を固定しておくとあとから拡張したいときや、デフォルトでは必要ないのだけど、テストのときとか書き換えてやりたいときにめんどくさかったりするのでHashを渡せばいいという割り切り方はなかなか良いなと思います。
とりあえずネタで書いてみたという程度なので、エラーメッセージなどは例によって今のところてきとうです。あと黒魔術はもっと減らせる気がしますw
いろいろなDSLをつかってるものとバッティングしそうな感じですが(汗
class Point < ClassX has :x, :is => :ro, :kind_of => Fixnum, :default => 10 has :y, :required => true has :hoge, :default => proc { "Hoge" } end Point.new end
追記:
なんとなくGithubの方に入れてみました。
http://github.com/walf443/classx/tree/master
ブロックの解釈の仕方をカスタマイズする
DSLみたいなものを作ろうとしてると、どうしてもブロックを俺俺文法で解釈させたくなって調べてみた。
ParseTreeというライブラリを使うとRubyのコードをS式に変換することが出来る。
S式に変換できるのは、モジュール単位、モジュールのメソッド単位、文字列を渡すの3種類の方法があり、
自分はブロックの部分のみをいじりたかったので次のようにしてみた
require 'rubygems' require 'parse_tree' class Hoge def fuga &block mod = Module.new mod.module_eval do mod.define_method :block, &block end s_exp = ParseTree.translate(mod, :block) HogeProcesssor.new(s_exp) end end
無名モジュールを作ってやってメソッド名を決めうちで定義してやってParseTreeに渡している。Ambitionのコードを見てみると違うやり方をしていたっぽいのだけどよくわからなかったw
HogeProcessorはSexpProcessorを継承したクラスでS式はこのクラスでいじくり回して欲しい形に変換する。
parse_〜というメソッドを定義しておけば自動的に実行されることはわかったのだけど、それ以外はよくわからなかった。
とりあえず頑張ってやってみてもParseTreeを使ってる時点で1.9に対応できなくなってしまうので、いいやと思ってHashとArrayの組み合わせで頑張ることにしたのでした。
非常に中途半端なのですが、また使いたくなった際に忘れてそうなのでメモです
rspec-fixutre 0.0.1をリリースしました
先日言っていた機能はまだ足してはないのですが、一通り個人的に使いたい機能を動くようにして、テストも充実させたのでgemにしました。
gem install rspec-fixture
でインストールできます。
http://coderepos.org/share/changeset/6563
で、単純なテストがいっぱいある場合にrspec-fixtureを使うとこんなに見通しがよくなるというサンプルとして書き直してみました。元々のテストコードがわかりづらすぎるという意見もあるかとは思いますが、ご参考までにどうぞ。
RSpecをTest::Baseっぽく使う
RSpecでテストを書いていて単純なテストなんだけど、いろいろなデータで検証させたいといった場合に毎度毎度exampleを書くのがめんどくさいと思い、こんな感じでTest::Baseっぽく使えるやつを作ってみた。
describe Point, "detect_location" do with_fixtures :point => :location do filters({ :point => lambda { |val| Point.new(*val) }, :location => :to_s, }) it "should detect point :point to :location (:msg)" do |point, location| point.detect_location.should == location end set_fixtures([ [ [1, 0] => :right ], [{[-1, 0] => :left }, "border" ], [{[-0.5,0] => :left }, "inner" ], [ [0, 1] => :top ], [ [0, -1] => :bottom ], ]) end end
実行結果はこんな感じになる
Point detect_location - should detect point ( 1, 0 ) to right () (FAILED - 6) - should detect point ( -1, 0 ) to left (border) (FAILED - 7) - should detect point ( -0.5, 0 ) to left (inner) (FAILED - 8) - should detect point ( 0, 1 ) to top () (FAILED - 9) - should detect point ( 0, -1 ) to bottom () (FAILED - 10) 6) 'Point detect_location should detect point ( 1, 0 ) to right ()' FAILED expected: "right", got: "unknown" (using ==) ./examples/detect_location_spec.rb:47: 7) 'Point detect_location should detect point ( -1, 0 ) to left (border)' FAILED expected: "left", got: "unknown" (using ==) ./examples/detect_location_spec.rb:47: 8) 'Point detect_location should detect point ( -0.5, 0 ) to left (inner)' FAILED expected: "left", got: "unknown" (using ==) ./examples/detect_location_spec.rb:47: 9) 'Point detect_location should detect point ( 0, 1 ) to top ()' FAILED expected: "top", got: "unknown" (using ==) ./examples/detect_location_spec.rb:47: 10) 'Point detect_location should detect point ( 0, -1 ) to bottom ()' FAILED expected: "bottom", got: "unknown" (using ==) ./examples/detect_location_spec.rb:47:
with_fixturesの引数は省略すると{ :input => :expected }がデフォルトで使われる。
実はHashである必然性は全然ないのだけど、こう書けた方が視覚的にわかりやすいのでこう書くようにした。
filterは値がProcの場合はそれが実行され、Procじゃない場合は順番に__send__されて適用するようにしてみた。この辺は使ってみてまだ色々変わったりとかしそう。
set_fixturesで渡すデータをTest::Base互換で書けるようにするとか、exampleを複数書けるようにするとか、exampleがinput.should == expectedになるときは省略して書けるようにするとか、まだ色々やろうかなと思うことはあるのでgemにはしてないです。
いつものようにCodereposに入れてあるので色々アドバイスなり、いただけるとありがたいです。
http://coderepos.org/share/browser/lang/ruby/rspec-fixture/trunk