Matcher という物を作った。パブリックドメインとするので自由に使ってください。
ruby/matcher/matcher.rb at master from sorah/sandbox - GitHub
使い方
Matcher.new(obj, default = DEFAULT_KEY) obj にハッシュを渡して、その後 Matcher#match?(*conds) にマッチ条件を渡すと true か false が返ってくる。
また、Matcher#match(*conds) でマッチした部分の配列等が返ってくる。
渡すハッシュでマッチ対象にできるのは {a: "b", b: "c"} といった1階層のHashだけで、複数の階層にわたるハッシュに対しては微妙。今回これを作った時にそれは想定してないのでどうでもいい。 誰かやってくれ。
引数 default
デフォルトだと Matcher::DEFAULT_KEY.
default は後述するマッチ条件でマッチ対象のキーが指定されてない時に参照するキーとなってる。
conds に使えるオブジェクト等
そもそも*conds引数は可変数引数になっている。
まず複数の引数を渡すと、渡した条件のうちいずれか一つにマッチすればマッチした扱いになり、全部にマッチしなければマッチしない扱いになる。
条件には、Regexp のオブジェクトを渡すとその正規表現にRegexp#matchでマッチするかどうかを確認、それ以外は==メソッドで確認される。
m = Matcher.new({foo: "bar", bar: "foo"}, :foo)
p m.match?("bar") #=> true
p m.match?("foo", "bar") #=> true
p m.match?(/fo/) #=> true
p m.match?("bar") #=> false
p m.match?(/b/) #=> false
普通にArrayを渡すと、これも複数の引数を渡した時のように配列内のオブジェクトを条件とみなしどれかにマッチしたときにマッチした扱いにする。が、あまり意味はないよね。ここでは。
Hash も渡す事ができる。Hash を渡すと、:any, :all を除いたキー以外だと、デフォルトのキーではなくそのキーに対してマッチするかどうかになる。で、何も指定 (後述する:anyとか:allとか) しない限りは、そのHashのどれかのキー(もちろんany,all以外)がマッチすればマッチした事になる。
p m.match?(bar: "foo") #=> true
p m.match?(bar: /f/) #=> true
p m.match?(bar: /bar/, foo: "bar") #=> true
ただ :any と :all キーを除いては、そのキーにHashを入れてさらに条件を指定しようとしても意味がない。そのHashとの比較になります。Arrayは:allとかを使っていない限りは「いずれかにマッチ」になる。
p m.match?(foo: ["foo", "bar"]) #=> true
そして :any キー は、その名の通り :any キーの値に入れた条件どれかにマッチすればマッチしたことになる。
:all キーもその名前の通り :all キーの値に入れた条件すべてにマッチすればマッチしたことになる
両方の値にはさっき書いた条件をそのまま評価するので何を入れても良い。ただ、その条件の「いずれかにマッチ」等が :all だと「全てにマッチ」に変わるだけ。
:any キーは :all の中で一部を「このいずれか」にしたい時に便利。
普通の条件式等でいうと {all: {a: {any: [1,2,3]}, b: /c/} は [1,2,3].include?(a) && /c/.match(b) みたいな感じ。
p m.match?(all: {foo: "bar", bar: "foo"}) #=> true
p m.match?(all: {foo: "foo", bar: "foo"}) #=> false
p m.match?(all: {foo: [/^f/,/o$/], bar: "foo"}) #=> true
p m.match?(all: {foo: [/^o/,/o$/], bar: "foo"}) #=> false
:raw キーのみを含んだハッシュをマッチ条件として渡したりすると、そのArrayと==でtrueになるか等になる。
要するに条件のカスタマイズではなくそれと比較させたいとかいう場合。
また match? ではなく match を使うと正規表現のMatchData等がとれ、条件にマッチしないときはfalse。
p m.match(foo: /b/, bar: /ba/) #=> [{:foo=>#<MatchData "b">}]
p m.match(foo: ["foo", /b/]) #=> [{:foo=>#<MatchData "b">}]
p m.match(/fo/, /ba/) #=> [#<MatchData "ba">]
p m.match(/fo/) #=> false
p m.match("foo", "bar") #=> ["bar"]
その他
その他使い方・返り値が帰ってくるのかは matcher_spec.rb を読んでください………
あとがき
しかし日本語でうまく書けないな。おれはもうだめだ。