gwt_rpc 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|