rhinoで言語の境界をとっぱらう
以前jrubyでjavascriptを動かすみたいなことを試したんだけど、現状のプロジェクトを進めるうちにどんどんその夢が広がりんぐなので、またその方向を押し進める試みをば。
最近だと大体どの言語環境でもJSONを解釈するlibはあるけど、JSONにfunctionを忍ばせてあるのまでは解釈ができないわけですよ。というわけでJSONに隠れて潜むfunctionもメソッドとして使いましょうというネタ。
・・たぶんわたすのグルに「なんでjruby!!??」といわれそうなんだけど、いまgroovyのExpandoを細かく見ているので練習がてらに近々groovyに書き直す予定。がんばれオレ!!
以下ソース。
#!/opt/jruby/bin/jruby require 'java' include_class "org.mozilla.javascript.Context" include_class "org.mozilla.javascript.ScriptableObject" class Javascript def initialize(script) @ctx = Context::enter @parent_scope = @ctx.initStandardObjects @script = "var jsObj="+script+";" ##TODO:無名jsonをムリヤリにグローバルスコープに追加。ださい。 @ctx.evaluateString(@parent_scope,@script,nil,0,nil) @current_scope = @parent_scope.get("jsObj",@parent_scope) begin yield self if block_given? rescue puts "error" ensure Context::exit end end def method_missing(name,*arg) js_find(name.to_s,arg) end private def js_find(name,args=nil) evl = @current_scope.get(name,@current_scope) args = args.to_java unless args.nil? ##callがdefinedならば実行できるやつ curryは・・・ムリっぽい if evl.respond_to?("call") ret = evl.call(@ctx, @parent_scope, @current_scope, args) ret else ##Array if evl.instance_of? Java::OrgMozillaJavascript::NativeArray ret = [] evl.length.times{|i|ret << evl.get(i,evl.getParentScope)} ret ##Object Hash?? elsif evl.instance_of? Java::OrgMozillaJavascript::NativeObject ##TODO ##IntegerもFloatで!!?? else evl end end end end
こんなクラスをjruby側で定義してやって、こんなふうに使う。
data = File.open(__FILE__).read.gsub(/.*__END__/m,"") class Person < Javascript require 'date' def name self.lastName.upcase+", "+self.firstNam end def address self.city+", "+self.state+", "+self.country end def age monthnames = {} Date::MONTHNAMES.each_with_index{|v,i| monthnames[v] = i+1} today = DateTime.now birth_date = DateTime.new(self.birthYear.to_i,monthnames[self.birthMonth],self.birthDay.gsub(/[^0-9]/,"").to_i) today.year - birth_date.year end end who = Person.new(data) ##from js puts who.firstName puts who.lastName puts who.fullName puts who.birth ##from ruby puts who.name puts who.address puts who.age __END__ { firstName:'Leroy', lastName:'Hutson', country:'U.S.A.', state:'New Jersey', city:'Newark', birthYear:'1945', birthMonth:'June', birthDay:'4th', profile:'Replaced Curtis Mayfield as lead singer for The Impressions in 1970 when Curtis Mayfield wanted to concentrate even more on his Curtom label and solo career.', roomMate:'Donny Hathaway', fullName:function(){return this.firstName+" "+this.lastName}, birth:function(){return this.birthDay+", "+this.birthMonth+", "+this.birthYear} }
javascript側のオブジェクトはmethod_missing経由でrhino解釈で戻って、当たり前だけどselfで参照できるからクラスメソッドとして定義したメソッドでどうとでもいじくれるというもの。ある程度の処理ならばクライアントサイドとサーバサイドでおんなじ処理ができますよ的な。