契約っぽいプログラミング
ふとD言語の仕様を眺めていたら、契約プログラミングがサポートされているらしい(http://www.kmonos.net/alang/wnd/whats.ja.html#dbc)ので、かなり心惹かれた。
あと、コンパイルがとても楽そうなのも素晴らしい。良い意味で節操がなさげなのもまぁ良いのだけれど、ライブラリがまだ弱そうなのが二の足を踏ませる。
ライブラリの充実度合いでプログラミング言語を選ぶのは、へたれプログラマーな気がするけど、私はへたれプログラマーなので何の問題も無かったりする。どうせインフラの面倒を見たり、開発ガイドラインを作ったりして、仕事でプログラムをろくに書いていない俺なんて俺なんて俺なん…
で、Rubyで動的にごにょごにょすれば契約っぽいことができるのではないか、と思いやってみた。とりあえずinブロックだけ試してみたけれど、Rubyだとinが予約語なのでRailsっぽくbeforeで。あと、急に眠たくなってきたので結構適当。
module Assertion require 'runit/assert' include RUNIT::Assert def self.included(mod) def mod.before(&assert) define_method(:before_assert, assert) def self.method_added(name) return if (/(.+?)_with(?:out)?_assertion/ =~ name.to_s and method_defined?($1)) or (method_defined?("#{name}_with_assertion") and method_defined?("#{name}_without_assertion")) or name == :initialize define_method "#{name}_with_assertion" do |*args| before_assert send("#{name}_without_assertion".intern, *args) end alias_method("#{name}_without_assertion", name) alias_method(name, "#{name}_with_assertion") end end end end
とかModuleを作っておいて、適当なクラスにincludeしてbeforeブロックに検証内容を書いておくと…
class Hoge include Assertion before do assert(@age >= 18) assert(@name.length >= 8 && @name.length <20) end def initialize @age = 20 @name = 'july juillet' end attr_accessor :age, :name end h1 = Hoge.new puts h1.age h1.age = 17 puts h1.age # => `assert_block': <false> is not true. (Test::Unit::AssertionFailedError) h2 = Hoge.new puts h2.name h2.name = 'foobar' puts h2.name # => `assert_block': <false> is not true. (Test::Unit::AssertionFailedError)
という風にエラーチェックができた(本当はエラーメッセージの行番号がわかりづらいのだけど内緒)。
outブロック的なものは、飽きてしまったので省略。
それにしても、もうちょっとすっきり書けないものか… 特にメソッド名空間を汚しているのが嫌だなぁ。