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.
Files changed (43) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +16 -0
  4. data/Gemfile.lock +36 -0
  5. data/LICENSE +20 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +19 -0
  8. data/Rakefile +52 -0
  9. data/VERSION +1 -0
  10. data/lib/gwt_rpc.rb +31 -0
  11. data/lib/gwt_rpc/base_extensions/array.rb +16 -0
  12. data/lib/gwt_rpc/base_extensions/boolean.rb +5 -0
  13. data/lib/gwt_rpc/base_extensions/date.rb +14 -0
  14. data/lib/gwt_rpc/base_extensions/fixnum.rb +20 -0
  15. data/lib/gwt_rpc/base_extensions/float.rb +22 -0
  16. data/lib/gwt_rpc/base_extensions/hash.rb +16 -0
  17. data/lib/gwt_rpc/base_extensions/multipart_string.rb +9 -0
  18. data/lib/gwt_rpc/base_extensions/string.rb +20 -0
  19. data/lib/gwt_rpc/client.rb +68 -0
  20. data/lib/gwt_rpc/gxt/hash.rb +24 -0
  21. data/lib/gwt_rpc/gxt/paginated_resultset.rb +16 -0
  22. data/lib/gwt_rpc/gxt/sort_dir.rb +15 -0
  23. data/lib/gwt_rpc/gxt/sort_info.rb +15 -0
  24. data/lib/gwt_rpc/procedure.rb +26 -0
  25. data/lib/gwt_rpc/request.rb +72 -0
  26. data/lib/gwt_rpc/response.rb +19 -0
  27. data/lib/gwt_rpc/response/reader.rb +75 -0
  28. data/spec/gwt_rpc/base_extensions/array_spec.rb +14 -0
  29. data/spec/gwt_rpc/base_extensions/boolean_spec.rb +16 -0
  30. data/spec/gwt_rpc/base_extensions/hash_spec.rb +14 -0
  31. data/spec/gwt_rpc/base_extensions/multipart_string_spec.rb +11 -0
  32. data/spec/gwt_rpc/base_extensions/string_spec.rb +18 -0
  33. data/spec/gwt_rpc/client_spec.rb +81 -0
  34. data/spec/gwt_rpc/gxt/hash_spec.rb +19 -0
  35. data/spec/gwt_rpc/gxt/paginated_resultset_spec.rb +14 -0
  36. data/spec/gwt_rpc/gxt/sort_dir_spec.rb +11 -0
  37. data/spec/gwt_rpc/gxt/sort_info_spec.rb +13 -0
  38. data/spec/gwt_rpc/procedure_spec.rb +30 -0
  39. data/spec/gwt_rpc/request_spec.rb +72 -0
  40. data/spec/gwt_rpc/response/reader_spec.rb +58 -0
  41. data/spec/gwt_rpc/response_spec.rb +39 -0
  42. data/spec/spec_helper.rb +12 -0
  43. 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::SortDir
2
+ attr_reader :direction
3
+
4
+ def initialize(direction)
5
+ @direction = direction
6
+ end
7
+
8
+ def self.gwt_deserialize(reader)
9
+ new(reader.read_int)
10
+ end
11
+
12
+ def eql?(o)
13
+ o.direction == direction
14
+ end
15
+ 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