gwt_rpc 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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