gwt_rpc 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/gwt_rpc.rb +31 -0
- data/lib/gwt_rpc/base_extensions/array.rb +16 -0
- data/lib/gwt_rpc/base_extensions/boolean.rb +5 -0
- data/lib/gwt_rpc/base_extensions/date.rb +14 -0
- data/lib/gwt_rpc/base_extensions/fixnum.rb +20 -0
- data/lib/gwt_rpc/base_extensions/float.rb +22 -0
- data/lib/gwt_rpc/base_extensions/hash.rb +16 -0
- data/lib/gwt_rpc/base_extensions/multipart_string.rb +9 -0
- data/lib/gwt_rpc/base_extensions/string.rb +20 -0
- data/lib/gwt_rpc/client.rb +68 -0
- data/lib/gwt_rpc/gxt/hash.rb +24 -0
- data/lib/gwt_rpc/gxt/paginated_resultset.rb +16 -0
- data/lib/gwt_rpc/gxt/sort_dir.rb +15 -0
- data/lib/gwt_rpc/gxt/sort_info.rb +15 -0
- data/lib/gwt_rpc/procedure.rb +26 -0
- data/lib/gwt_rpc/request.rb +72 -0
- data/lib/gwt_rpc/response.rb +19 -0
- data/lib/gwt_rpc/response/reader.rb +75 -0
- data/spec/gwt_rpc/base_extensions/array_spec.rb +14 -0
- data/spec/gwt_rpc/base_extensions/boolean_spec.rb +16 -0
- data/spec/gwt_rpc/base_extensions/hash_spec.rb +14 -0
- data/spec/gwt_rpc/base_extensions/multipart_string_spec.rb +11 -0
- data/spec/gwt_rpc/base_extensions/string_spec.rb +18 -0
- data/spec/gwt_rpc/client_spec.rb +81 -0
- data/spec/gwt_rpc/gxt/hash_spec.rb +19 -0
- data/spec/gwt_rpc/gxt/paginated_resultset_spec.rb +14 -0
- data/spec/gwt_rpc/gxt/sort_dir_spec.rb +11 -0
- data/spec/gwt_rpc/gxt/sort_info_spec.rb +13 -0
- data/spec/gwt_rpc/procedure_spec.rb +30 -0
- data/spec/gwt_rpc/request_spec.rb +72 -0
- data/spec/gwt_rpc/response/reader_spec.rb +58 -0
- data/spec/gwt_rpc/response_spec.rb +39 -0
- data/spec/spec_helper.rb +12 -0
- metadata +285 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
class GwtRpc::Gxt::PaginatedResultset
|
2
|
+
attr_reader :offset, :total_count, :results
|
3
|
+
|
4
|
+
def initialize(offset, total_count, results)
|
5
|
+
@offset = offset
|
6
|
+
@total_count = total_count
|
7
|
+
@results = results
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.gwt_deserialize(reader)
|
11
|
+
offset = reader.read_int
|
12
|
+
total_count = reader.read_int
|
13
|
+
results = reader.read_object
|
14
|
+
new(offset, total_count, results)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class GwtRpc::Gxt::SortInfo
|
2
|
+
attr_reader :field, :direction
|
3
|
+
|
4
|
+
def initialize(field, direction)
|
5
|
+
@direction = direction
|
6
|
+
@field = field
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.gwt_deserialize(reader)
|
10
|
+
direction = reader.read_object
|
11
|
+
reader.read_int
|
12
|
+
reader.read_int
|
13
|
+
new(nil,direction)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class GwtRpc::Procedure
|
2
|
+
attr_reader :path, :namespace, :method, :identifier
|
3
|
+
def initialize(options, &block)
|
4
|
+
options.symbolize_keys!
|
5
|
+
@path = options[:path] or raise ":path must be provided"
|
6
|
+
if block
|
7
|
+
@block = block
|
8
|
+
else
|
9
|
+
@namespace = options[:namespace] or raise ":namespace must be provided"
|
10
|
+
@method = options[:method] or raise ":method must be provided"
|
11
|
+
@identifier = options[:identifier] or raise ":identifier must be provided"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(client, *parameters)
|
16
|
+
response = request(client, *parameters).call
|
17
|
+
end
|
18
|
+
|
19
|
+
def request(client, *parameters)
|
20
|
+
if @block
|
21
|
+
GwtRpc::Request.new(client, :procedure => self, :body => @block.call(*parameters))
|
22
|
+
else
|
23
|
+
GwtRpc::Request.new(client, :procedure => self, :parameters => parameters)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class GwtRpc::Request
|
2
|
+
def initialize(client, options = {})
|
3
|
+
@client = client
|
4
|
+
@procedure = options[:procedure]
|
5
|
+
@parameters = options[:parameters]
|
6
|
+
@body = options[:body]
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
10.times do
|
11
|
+
begin
|
12
|
+
response = Typhoeus::Request.post(url,
|
13
|
+
:body => body,
|
14
|
+
:headers => {'Content-Type' => "text/x-gwt-rpc; charset=utf-8"},
|
15
|
+
:timeout => 1000,
|
16
|
+
:cache_timeout => 60)
|
17
|
+
return GwtRpc::Response.new(@client, response).content
|
18
|
+
rescue GwtRpc::Error::NoResponse
|
19
|
+
# prevent random hiccups
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def url
|
25
|
+
'http://' + @client.domain + @procedure.path
|
26
|
+
end
|
27
|
+
|
28
|
+
def header
|
29
|
+
[5,0]
|
30
|
+
end
|
31
|
+
|
32
|
+
def body
|
33
|
+
if @body
|
34
|
+
body = @body
|
35
|
+
else
|
36
|
+
string_table, payload = stringtablize(data)
|
37
|
+
body = (header + [string_table.size] + string_table + payload).join("|") + "|"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def data
|
42
|
+
[
|
43
|
+
@client.js_url,
|
44
|
+
@procedure.identifier,
|
45
|
+
@procedure.namespace,
|
46
|
+
@procedure.method,
|
47
|
+
@parameters.size,
|
48
|
+
parameter_classes,
|
49
|
+
parameter_values.flatten
|
50
|
+
].flatten
|
51
|
+
end
|
52
|
+
|
53
|
+
def parameter_classes
|
54
|
+
@parameters.map{|p| @client.class.ruby_class_to_java(p.class)}
|
55
|
+
end
|
56
|
+
|
57
|
+
def parameter_values
|
58
|
+
@parameters.map{|p| p.gwt_serialize }.flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
def stringtablize(data)
|
62
|
+
string_table = data.select{|v| v.class == String }.uniq
|
63
|
+
vals = data.map do |v|
|
64
|
+
if v.class == String
|
65
|
+
string_table.index(v) + 1
|
66
|
+
else
|
67
|
+
v
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return string_table, vals
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class GwtRpc::Error < Exception; end
|
2
|
+
class GwtRpc::Error::ServerError < GwtRpc::Error; end
|
3
|
+
class GwtRpc::Error::NoResponse < GwtRpc::Error; end
|
4
|
+
|
5
|
+
class GwtRpc::Response
|
6
|
+
def initialize(client, raw_response)
|
7
|
+
@client = client
|
8
|
+
@raw_response = raw_response
|
9
|
+
|
10
|
+
raise GwtRpc::Error::NoResponse.new("Error: no response to request") if @raw_response.code == 0
|
11
|
+
raise GwtRpc::Error::ServerError.new("Error: status code #{@raw_response.code} not 200") if @raw_response.code != 200
|
12
|
+
raise GwtRpc::Error::ServerError.new("Error: #{@raw_response.body}") if @raw_response.body !~ /^\/\/OK/
|
13
|
+
end
|
14
|
+
|
15
|
+
def content
|
16
|
+
json_body = @raw_response.body.sub(/^\/\/OK/,'')
|
17
|
+
GwtRpc::Response::Reader.new(@client, json_body).read_object
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class GwtRpc::Response::Reader
|
2
|
+
DEBUG = false
|
3
|
+
|
4
|
+
attr_reader :version, :string_table, :data, :objects
|
5
|
+
def initialize(client, json_body)
|
6
|
+
@client = client
|
7
|
+
(@version, placeholder, @string_table, *@data) = JSON.parse(json_body).reverse
|
8
|
+
@max_prior_string_location = 0
|
9
|
+
@objects = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def read_int
|
13
|
+
int = @data.shift
|
14
|
+
puts "read int #{int}" if DEBUG
|
15
|
+
int
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_string(position=read_int)
|
19
|
+
if position > (@max_prior_string_location + 1)
|
20
|
+
raise "trying to read #{position}, which is too far ahead; max seen thus far is #{@max_prior_string_location}!"
|
21
|
+
end
|
22
|
+
|
23
|
+
if position > @max_prior_string_location
|
24
|
+
@max_prior_string_location += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
val = @string_table[position-1]
|
28
|
+
puts "read str #{val} (@#{position})" if DEBUG
|
29
|
+
|
30
|
+
val
|
31
|
+
end
|
32
|
+
|
33
|
+
def read_object
|
34
|
+
int = read_int
|
35
|
+
|
36
|
+
if int < 0
|
37
|
+
obj = @objects[-1 - int]
|
38
|
+
# if int == -11
|
39
|
+
# puts obj.inspect
|
40
|
+
# @objects.each_with_index do |doc, i|
|
41
|
+
# puts "#{i} => #{doc.inspect}"
|
42
|
+
# end
|
43
|
+
# exit
|
44
|
+
# end
|
45
|
+
elsif int > 0
|
46
|
+
java_class = read_string(int)
|
47
|
+
java_class.sub!(/\/\d+$/,'')
|
48
|
+
puts "reading obj #{java_class}" if DEBUG
|
49
|
+
ruby_class = @client.class.java_class_to_ruby(java_class)
|
50
|
+
|
51
|
+
if ruby_class
|
52
|
+
placeholder_position = @objects.count
|
53
|
+
@objects << "PLACEHOLDER of type #{java_class}"
|
54
|
+
|
55
|
+
obj = ruby_class.constantize.gwt_deserialize(self)
|
56
|
+
@objects[placeholder_position] = obj
|
57
|
+
else
|
58
|
+
raise "unknown java class '#{java_class}'"
|
59
|
+
end
|
60
|
+
elsif int == 0
|
61
|
+
obj = nil
|
62
|
+
end
|
63
|
+
obj
|
64
|
+
end
|
65
|
+
|
66
|
+
def debug
|
67
|
+
html = "<table>\n"
|
68
|
+
html << @data.map{|i| [i, @string_table[i-1]].map{|val| "<td>#{val}</td>"}}.map{|row| "<tr>#{row}</tr>"}.join("\n")
|
69
|
+
html << "</table><table>\n"
|
70
|
+
@string_table.each_with_index{|str,i| html << "<tr><td>#{i+1}</td><td>#{str}</td></tr>\n"}
|
71
|
+
html << "</table>"
|
72
|
+
|
73
|
+
html
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::BaseExtensions::Array do
|
4
|
+
describe ".gwt_deserialize" do
|
5
|
+
before :each do
|
6
|
+
@client = GwtRpc::Client.new
|
7
|
+
@reader = GwtRpc::Response::Reader.new(@client, '[3,1,2,1,2,["java.lang.String/2004016611","Hello","World"],0,5]')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should determine it size and call read_object that many times" do
|
11
|
+
Array.gwt_deserialize(@reader).should == ['Hello', 'World']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::BaseExtensions::Boolean do
|
4
|
+
describe ".gwt_deserialize" do
|
5
|
+
it "0 should be false" do
|
6
|
+
client = GwtRpc::Client.new
|
7
|
+
reader = GwtRpc::Response::Reader.new(client, '[0,[],0,5]')
|
8
|
+
GwtRpc::BaseExtensions::Boolean.gwt_deserialize(reader).should == false
|
9
|
+
end
|
10
|
+
it "1 should be true" do
|
11
|
+
client = GwtRpc::Client.new
|
12
|
+
reader = GwtRpc::Response::Reader.new(client, '[0,[],0,5]')
|
13
|
+
GwtRpc::BaseExtensions::Boolean.gwt_deserialize(reader).should == false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::BaseExtensions::Hash do
|
4
|
+
describe ".gwt_deserialize" do
|
5
|
+
before :each do
|
6
|
+
@client = GwtRpc::Client.new
|
7
|
+
@reader = GwtRpc::Response::Reader.new(@client, '[5,1,4,1,3,1,2,1,2,["java.lang.String/2004016611","Hello","Hola","Goodbye","Adios"],0,5]')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should determine it size and call read_object that many times" do
|
11
|
+
Hash.gwt_deserialize(@reader).should == {'Hello' => 'Hola', 'Goodbye' => 'Adios'}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::BaseExtensions::MultipartString do
|
4
|
+
describe ".gwt_deserialize" do
|
5
|
+
it "should combine multiple strings" do
|
6
|
+
client = GwtRpc::Client.new
|
7
|
+
reader = GwtRpc::Response::Reader.new(client, '[3,2,1,3,["a","b","c"],0,5]')
|
8
|
+
GwtRpc::BaseExtensions::MultipartString.gwt_deserialize(reader).should == ["a","b","c"]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::BaseExtensions::String do
|
4
|
+
describe ".gwt_serialize" do
|
5
|
+
it "should return itself" do
|
6
|
+
"foo".gwt_serialize.should == ["foo"]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".gwt_deserialize" do
|
11
|
+
it "should simply read a string from the string table" do
|
12
|
+
client = GwtRpc::Client.new
|
13
|
+
reader = GwtRpc::Response::Reader.new(client, '[1,["foo"],0,5]')
|
14
|
+
|
15
|
+
String.gwt_deserialize(reader).should == "foo"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::Client do
|
4
|
+
before :each do
|
5
|
+
@subclass = Class.new(GwtRpc::Client)
|
6
|
+
end
|
7
|
+
describe ".domain" do
|
8
|
+
it "should set the domain when passed a value" do
|
9
|
+
@subclass.domain "example.com"
|
10
|
+
@subclass.domain.should == "example.com"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".js_url" do
|
15
|
+
it "should set the js_url when passed a value" do
|
16
|
+
@subclass.js_url "http://www.example.com/foo/bar/"
|
17
|
+
@subclass.js_url.should == "http://www.example.com/foo/bar/"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "default class mapping" do
|
22
|
+
it "should define string" do
|
23
|
+
@subclass.java_class_to_ruby("java.lang.String").should == "String"
|
24
|
+
@subclass.java_class_to_ruby("java.util.ArrayList").should == "Array"
|
25
|
+
@subclass.java_class_to_ruby("java.util.HashMap").should == "Hash"
|
26
|
+
@subclass.java_class_to_ruby("java.lang.Boolean").should == "GwtRpc::BaseExtensions::Boolean"
|
27
|
+
@subclass.java_class_to_ruby("[Ljava.lang.String;").should == "GwtRpc::BaseExtensions::MultipartString"
|
28
|
+
@subclass.java_class_to_ruby("com.extjs.gxt.ui.client.data.RpcMap").should == "GwtRpc::Gxt::Hash"
|
29
|
+
@subclass.java_class_to_ruby("com.extjs.gxt.ui.client.Style$SortDir").should == "GwtRpc::Gxt::SortDir"
|
30
|
+
@subclass.java_class_to_ruby("com.extjs.gxt.ui.client.data.SortInfo").should == "GwtRpc::Gxt::SortInfo"
|
31
|
+
@subclass.java_class_to_ruby("com.extjs.gxt.ui.client.data.BasePagingLoadResult").should == "GwtRpc::Gxt::PaginatedResultset"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".map_classes" do
|
36
|
+
it "should store the class mapping" do
|
37
|
+
@subclass.map_classes "java.lang.Foo" => "Foo", "java.lang.Bar" => "Bar"
|
38
|
+
@subclass.java_class_to_ruby("java.lang.Foo").should == "Foo"
|
39
|
+
@subclass.java_class_to_ruby("java.lang.Bar").should == "Bar"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe ".ruby_class_to_java" do
|
44
|
+
it "should return the reverse mapping" do
|
45
|
+
@subclass.map_classes "java.lang.Foo" => "Foo", "java.lang.Bar" => "Bar"
|
46
|
+
@subclass.ruby_class_to_java("Foo").should == "java.lang.Foo"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe ".add_procedure" do
|
51
|
+
it "should add a procedure object to the set" do
|
52
|
+
@subclass.procedures.should == {}
|
53
|
+
@subclass.add_procedure :hello_world, :path => '/hello/world', :method => "foo", :namespace => "bar", :identifier => "abc"
|
54
|
+
@subclass.procedures[:hello_world].should be_a(GwtRpc::Procedure)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should define a method of the same name that calls that procedure" do
|
58
|
+
instance = @subclass.new
|
59
|
+
instance.should_not respond_to(:test)
|
60
|
+
@subclass.add_procedure :test, :path => '/test', :method => "foo", :namespace => "bar", :identifier => "abc"
|
61
|
+
|
62
|
+
instance.should respond_to(:test)
|
63
|
+
|
64
|
+
@subclass.procedures[:test].should_receive(:call).with do |client, *params|
|
65
|
+
client.should == instance
|
66
|
+
params.should == [1,2]
|
67
|
+
end
|
68
|
+
|
69
|
+
instance.test(1,2)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "instance" do
|
74
|
+
describe ".domain" do
|
75
|
+
it "should read from the class's .domain" do
|
76
|
+
@subclass.domain "example.com"
|
77
|
+
@subclass.new.domain.should == 'example.com'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::Gxt::Hash do
|
4
|
+
describe ".new" do
|
5
|
+
it "should be a subclass of Hash" do
|
6
|
+
GwtRpc::Gxt::Hash.new should be_a(Hash)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
describe ".gwt_deserialize" do
|
10
|
+
before :each do
|
11
|
+
@client = GwtRpc::Client.new
|
12
|
+
@reader = GwtRpc::Response::Reader.new(@client, '[5,2,4,3,2,1,2,["Hello","java.lang.String/2004016611","Hola","Goodbye","Adios"],0,5]')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should determine it size and call read_object that many times" do
|
16
|
+
GwtRpc::Gxt::Hash.gwt_deserialize(@reader).should == GwtRpc::Gxt::Hash['Hello' => 'Hola', 'Goodbye' => 'Adios']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe GwtRpc::Gxt::PaginatedResultset do
|
4
|
+
describe ".gwt_deserialize" do
|
5
|
+
it "should parse correctly" do
|
6
|
+
@client = GwtRpc::Client.new
|
7
|
+
@reader = GwtRpc::Response::Reader.new(@client, '[3,2,1,1,1,0,["java.util.ArrayList","java.lang.String","Hi"],0,5]')
|
8
|
+
results = GwtRpc::Gxt::PaginatedResultset.gwt_deserialize(@reader)
|
9
|
+
results.offset.should == 0
|
10
|
+
results.total_count.should == 1
|
11
|
+
results.results.should == ['Hi']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|