東京Ruby会議01で発表しました
Akasaka.rbの活動の一環として東京Ruby会議01でClassXについて発表してきました。
スライドは http://github.com/walf443/classx/tree/master%2Fdoc%2FTokyoRubyKaigi01_classX.pdf?raw=true
からDLできます。
発表すると、 http://github.com/walf443/classx/tree/28f1694a54968cb56c674fe62a6f2b6ad35a46b9/lib/classx/declare.rb
みたいなフィードバックが受けられるので素晴しいですね。
追記
早くも動画がUpされているようです。cojiさん仕事早すぎwww
盛大にDISっていただけると光栄です。 http://www.nicovideo.jp/watch/sm4367766
ClassX::Declare
東京Ruby会議01で高井さんにどうよといわれたやつ。
http://github.com/walf443/classx/tree/28f1694a54968cb56c674fe62a6f2b6ad35a46b9/lib/classx/declare.rb
↑のやつには使うとどんな風にできるかの説明がなかったので書いておきます。
# with ClassX::Declare # cool!! thanks Naoto Takai require 'classx' require 'classx/declare' include ClassX::Declare classx "Hoge" do has :x end Hoge.new(:x => 0) #=> #<Hoge:0x111a7dc @__attribute_of={"x"=><#ClassX::Attribute {}:8966980 @data=0 >}>
個人的には普通に書いてもいいような気もしつつ、何回もclass書くのがたるいという意見もそうかもなぁと思ったので、defaultでは有効になりませんが、オプションで使いたい人はつかえばいいよという方針で入れてみました。
RubyCocoaでFluent Interfaceはどうか
http://subtech.g.hatena.ne.jp/secondlife/20080709/1215530958のFluent InterfaceはRubyCocoaの呼びだしで使えたらうれしいかもと思った。
今はobjc_sendをつかって、
NSTimer.objc_send(:scheduledTimerWithTimeInterval, 120,- :target, self, :selector, 'update', :userInfo, nil, :repeats, true )
なふうに書いているのだけど、
NSTimer.objc_send_fluent(:scheduledTimerWithTimeInterval, 120).target(self).selector('update').userInfo(nil).repeats(true).execute
とか書けるのはどうかなと思ったけど、Cocoaに投げるまでメソッドがないかどうかは結局わからないのであまりうれしくもない気がする。
間違ったメソッドを実行した時点でエラーになってくれるとtypoを見つけやすくて助かるのだけど。。。
ClassX::ValidateでHashをチェックする
http://d.hatena.ne.jp/rubikitch/20080710/1215641240
メソッドの引数にHashをとるのはいっぱいあるときでもきれいに書けるので個人的にはあまり嫌いではない。
classXを使うとHashのチェックが柔軟にできるよというおはなし。
require 'classx/validate' class YourClass include ClassX::Validate def run params validate params do has :x has :y, :default => proc { {} }, :kind_of => Hash end # do something with params end end
classXのアクセサの宣言の仕方でHashをチェックできる。メソッドに渡すべきオプションを宣言的に書くことでコードも読みやすくなるはず。
実は内部ではブロックで指定したアクセサを持つclassXベースの無名クラスを作りclassXのアクセサのValidateをさせているだけ。戻り値はその無名クラスのインスタンスなのであまりかっこよくはないけど使う方が堅実に書ける。
require 'classx/validate' class YourClass include ClassX::Validate def run params validated_params = validate params do has :x has :y, :default => proc { {} }, :kind_of => Hash end p validated_params.x validated_params.y = '' # => raise ClassX::InvalidAttrArgument # do something with params end end
classXはhttp://github.com/walf443/classx/tree/masterで公開してます
メソッドの渡すHashもきっちりチェックしたい
http://d.hatena.ne.jp/walf443/20080524/1211650071
前の記事でコンストラクタに渡せるHashのキーと値を宣言的に書くことで見通しが良くなると書きましたが、コンストラクタに限らず、メソッドにオプションとして渡すHashもきっちりチェックしたいはず。ということで書いてみました。
内部的には無名のClassXを継承したクラスを作ってやってインスタンス化することでHashをチェックしてるので、classXと同様に使えます。
エラーのときのメッセージは調整した方がデバックなどのしやすさのために良いですが、まだ未調整です。
diff --git a/lib/github_post_reciever/worker/base.rb b/lib/github_post_reciever/worker/base.rb index 4b1ac05..1a931be 100644 --- a/lib/github_post_reciever/worker/base.rb +++ b/lib/github_post_reciever/worker/base.rb @@ -1,9 +1,12 @@ require 'drb/drb' require 'classx' +require 'classx/validate' class GitHubPostReciever module Worker class Base < ClassX + include Validate + def run method, json raise NoImprementedError end diff --git a/lib/github_post_reciever/worker/irc.rb b/lib/github_post_reciever/worker/irc.rb index e3aa0ab..ec1d5b0 100644 --- a/lib/github_post_reciever/worker/irc.rb +++ b/lib/github_post_reciever/worker/irc.rb @@ -43,13 +43,20 @@ class GitHubPostReciever has :template, :is => :ro, :kind_of => String, :required => true def run method, json - json['commits'].reverse.each do |sha, commit| + validated_json = validate json do + has :commits, :kind_of => Hash, :required => true + end + + validated_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 + rescue ClassX::InvalidSetterArgumentError => e + rescue ClassX::AttrRequiredError => e + warn e end end end
実装の方に興味がある方は、http://github.com/walf443/classx/tree/master/lib/classx/validate.rb
をどうぞ。
デフォルトのオプションと:isおよび:requiredオプションの廃止
classXで、Mooseをなぞるのをやめてアクセサの宣言のデフォルトの設定を変更しました。
:writableオプションを指定しない限りは、アクセサはwriterをpublicにしないようになりました。これは、単にreaderを定義する時は、オプションを明示的に定義する必要はなく、writerをpublicから触れるようなときはより明示的に指定した方が良いと思ったからです。:isを:writableに変更したのは強調させる意図です。
また、:defaultオプションを指定するか、:optionalにtrueを指定したとき以外はアクセサは必要な状態になるようにし、ないと例外が起こるようにしました。これは、オプションなアクセサは明示的に示すべきだと考えたからです。
具体例はコミットログと追加されたテストを見るのがわかりやすいです。
メソッドの渡すHashもきっちりチェックしたい
http://d.hatena.ne.jp/walf443/20080524/1211650071
前の記事でコンストラクタに渡せるHashのキーと値を宣言的に書くことで見通しが良くなると書きましたが、コンストラクタに限らず、メソッドにオプションとして渡すHashもきっちりチェックしたいはず。ということで書いてみました。
内部的には無名のClassXを継承したクラスを作ってやってインスタンス化することでHashをチェックしてるので、classXと同様に使えます。
エラーのときのメッセージは調整した方がデバックなどのしやすさのために良いですが、まだ未調整です。
diff --git a/lib/github_post_reciever/worker/base.rb b/lib/github_post_reciever/worker/base.rb index 4b1ac05..1a931be 100644 --- a/lib/github_post_reciever/worker/base.rb +++ b/lib/github_post_reciever/worker/base.rb @@ -1,9 +1,12 @@ require 'drb/drb' require 'classx' +require 'classx/validate' class GitHubPostReciever module Worker class Base < ClassX + include Validate + def run method, json raise NoImprementedError end diff --git a/lib/github_post_reciever/worker/irc.rb b/lib/github_post_reciever/worker/irc.rb index e3aa0ab..ec1d5b0 100644 --- a/lib/github_post_reciever/worker/irc.rb +++ b/lib/github_post_reciever/worker/irc.rb @@ -43,13 +43,20 @@ class GitHubPostReciever has :template, :is => :ro, :kind_of => String, :required => true def run method, json - json['commits'].reverse.each do |sha, commit| + validated_json = validate json do + has :commits, :kind_of => Hash, :required => true + end + + validated_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 + rescue ClassX::InvalidSetterArgumentError => e + rescue ClassX::AttrRequiredError => e + warn e end end end
実装の方に興味がある方は、http://github.com/walf443/classx/tree/master/lib/classx/validate.rb
をどうぞ。