Rubyで、OGNLっぽい文字列表現でオブジェクトグラフを辿るアレを書いた
Java使いはおなじみのOGNL(Object Notation Graph Language)っぽい記法で、Rubyのオブジェクトグラフを辿るアレを書いてみた。evalすればってツッコミはなしで、な。
厳密にOGNLになってなくて、どっちかっていうとRubyっぽい表現になってるけど、違和感ないはず。
文字列をparseしてオブジェクトグラフを辿る処理を、ループでも再帰でもなくinject(fold)でさらっと書けるようになったところに、関数型言語を勉強した成果が現れている、と思いたい。
RubyでOGNLっぽく、文字列で表現されたプロパティにアクセスするための適当なナニか — Gist
#!/usr/bin/env ruby # RubyでOGNL(Object Notation Graph Language)っぽく、 # 文字列で表現されたプロパティにアクセスするための # 適当なナニか。 # # # Ognl.new('foo_string').apply(foo) # => object notation graph language!! # # Ognl.new('foo_array[1]').apply(foo) # => 2 # # Ognl.new('foo_hash[:a]').apply(foo) # => ooo # # Ognl.new('nested_obj.foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('nested_obj.foo_array[1]').apply(foo) # => 456 # # Ognl.new('nested_obj.foo_hash[:a]').apply(foo) # => oppai!! # # ognl.new('foo_array[3]').apply(foo) # => #<Foo:0x10e0366a0> # # Ognl.new('foo_array[3].foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('foo_array[3].foo_array[1]').apply(foo) # => 456 # # Ognl.new('foo_array[3].foo_hash[:a]').apply(foo) # => oppai!! # # Ognl.new('foo_hash[:foo2]').apply(foo) # => #<Foo:0x10e0366a0> # # Ognl.new('foo_hash[:foo2].foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo) # => 456 # # Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo) # => oppai!! # class Ognl attr_accessor :expression def initialize(expression) @expression = expression end def parse(e) e.split(/(\.|\[[^\]]+\])/).reject{|exp| exp == "." || exp.nil? || exp.empty?} end def apply(target) exps = parse(expression) exps.inject(target){|obj, exp| if exp =~ /\[([^\]]+)\]/ key = $1 key = case key when /^\d+$/ then key.to_i when /^:(.+)/ then $1.to_sym else key.to_s end obj.send("[]", key) if obj.respond_to? "[]" else obj.send(exp) if obj.respond_to? exp end } end end class Foo attr_accessor :foo_array, :foo_hash, :nested_obj, :foo_int, :foo_string def initialize @foo_array = [1, 2, 3] @foo_hash = {:a => "ooo", :b => "pai"} @foo_int = 123 @foo_string = "object notation graph language!!" end end foo = Foo.new puts "Ognl.new('foo_string').apply(foo)" puts "=> #{Ognl.new('foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:a]').apply(foo)}" puts "" foo2 = Foo.new foo2.foo_int = 999 foo2.foo_string = "( ゚∀゚)o彡°おっぱい!おっぱい!" foo2.foo_hash[:a] = "oppai!!" foo2.foo_array[1] = 456 foo.nested_obj = foo2 foo.foo_array << foo2 foo.foo_hash[:foo2] = foo2 puts "Ognl.new('nested_obj.foo_string').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_string').apply(foo)}" puts "" puts "Ognl.new('nested_obj.foo_array[1]').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('nested_obj.foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_hash[:a]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3]').apply(foo)" puts "=> #{Ognl.new('foo_array[3]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_string').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_hash[:a]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_string').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo)}" puts ""