無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

JSON-RPCで転送するJavaオブジェクトをさくっと作成するユーティリティ の続き

昨日作成したJSON-RPCで転送するJavaオブジェクトをさくっと作成するユーティリティですが、RubyJSONへのシリアライズだけじゃなく、JSONRubyへのデシリアライズもサポートしてればさらに便利じゃね?と思い立って実装してみた。API呼び出しの結果として返されたハッシュが定義済みのJavaクラスであればそのインスタンスに変換して返します。

require 'rubygems'
require 'uri'
require 'httpclient'
require 'json/lexer'
require 'json/objects'

module JsonRpc

  # クライアント
  class Client
    def initialize( name, host="http://localhost:8080", proxy=ENV["http_proxy"] )
      @client = HTTPClient.new( proxy, "JsonClientLib")
      @client.set_cookie_store("cookie.dat")
      @name = name
      @host = host
      @id = 0
    end
    def method_missing( name, *args )
      body = "{ 'id':#{@id+=1},method:#{(@name+"."+name.to_s).to_json},params:#{args.to_json}}"
      result = @client.post(@host, body )
      json = JSON::Lexer.new(result.content).nextvalue
      if json["error"]
        raise json["error"]["msg"]
      else
        JsonRpc.to_java_class(json["result"])
      end
    end
  end
  
  #JSON-RPCで送付するJavaクラスを定義する。
  #java_class:: 対応するJavaクラスのFQCN 
  #attrs:: Javaクラスの属性一覧
  #return:: Javaクラス
  def self.define( java_class, *attrs )
    cl = Class.new(JsonRpc::JavaClass) {|klass|
      @java_class = java_class
      @attrs = attrs
      def self.java_class ; @java_class; end
      def self.attrs; @attrs; end
      def initialize( *args )
        attrs = self.class.attrs
        args.each_index {|i|
          break if i >= attrs.length
          instance_variable_set("@" + attrs[i].to_s, args[i])
        }
      end
      attrs.each {|a| attr a,  true}
    }
    @@registory[java_class] = cl
    return cl
  end
  # 定義したJavaClass
  @@registory = {}
  
  # ハッシュをJavaClassにデシリアライズする。
  def self.to_java_class( obj )
    if obj.kind_of?( Hash )
      hash = obj.inject({}){|r,i| r[i[0]] = to_java_class(i[1]); r }
      if ( obj["javaClass"] && @@registory.key?(obj["javaClass"]) )
        java_class = @@registory[obj["javaClass"]]
        args = java_class.attrs.map {|a| hash[to_jar_attr_name( a )] }
        return java_class.new(*args)
      else
        return hash
      end
    elsif obj.kind_of?( Array )
      return obj.map {|i| to_java_class( i ) }
    else
      return obj
    end
  end
  def self.to_jar_attr_name( name )
    tmp = name.to_s.split(/_/)
    return ([tmp[0]] + tmp[1..-1].map{|i| i.capitalize}).join
  end
  
  # Javaクラスの基底クラス
  class JavaClass
    def to_hash
      h = {"javaClass"=>self.class.java_class}
      attrs = self.class.attrs
      attrs.each {|attr|
        java_attr_name = JsonRpc.to_jar_attr_name( attr )
        h[java_attr_name] = instance_variable_get("@" + attr.to_s)
      }
      return h
    end
    def to_json
      to_hash.to_json
    end
  end
end

利用例は以下。

# Kittenオブジェクトを定義
Kitten = JsonRpc::define( 
  "test.Kitten",
  :id, :name, :age
)
# クライアント作成
client = JsonRpc::Client.new( "dao", 
  "http://localhost:8888/json" )
# API呼び出し。
p client.put( Kitten.new( nil, "mii", 1 ))

実行結果です。

#<Kitten:0x7fd308f4 @name="mii", @age=1, @id={"kind"=>"Kitten", "javaClass"=>"com.google.appengine.api.datastore.Key", "id"=>4}>