( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

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 ""