jimson-temp 0.9.2

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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,86 @@
1
+ == 0.9.1 / 2012-09-18
2
+
3
+ * Bug fixes
4
+
5
+ * Allow opts to be passed to Server.with_routes
6
+
7
+ == 0.9.0 / 2012-08-22
8
+
9
+ * Minor enhancements
10
+
11
+ * Add show_errors option to server, which will cause application errors to include the error name and the first line of the backtrace
12
+
13
+ == 0.8.0 / 2012-08-17
14
+
15
+ * Major enhancements
16
+
17
+ * Add namespaced method calls to client (e.g. 'client[:foo].sum(1,2,3) # calls foo.sum')
18
+ * Add Server.with_routes to quickly created a routed server
19
+
20
+ == 0.7.1 / 2012-08-16
21
+
22
+ * Bug fixes
23
+
24
+ * Fix handling of array params in client, which were erroneously being flattened
25
+
26
+ == 0.7.0 / 2012-04-13
27
+
28
+ * Major enhancements
29
+
30
+ * Add namespaced routing
31
+
32
+ * Bug fixes
33
+
34
+ * Fix deprecation warning about RDoc task in Rakefile
35
+
36
+ == 0.6.0 / 2012-03-14
37
+
38
+ * Minor enhancements
39
+
40
+ * Add ability to pass options to Rack and RestClient
41
+
42
+ == 0.5.0 / 2012-03-06
43
+
44
+ * Major enhancements
45
+
46
+ * Switch to MultiJson from json gem
47
+
48
+ * Bug fixes
49
+
50
+ * Allow BigNum in 'id' field of request and response
51
+
52
+ == 0.3.1 / 2011-08-11
53
+
54
+ * Minor enhancements
55
+
56
+ * Refactor the way the server is intantiated/started to work better with config.ru
57
+
58
+ == 0.3.0 / 2011-08-11
59
+
60
+ * Major enhancements
61
+
62
+ * Replace eventmachine-httpserver with rack for more cross-platform goodness
63
+
64
+ == 0.2.3 / 2011-08-01
65
+
66
+ * Bug fixes
67
+
68
+ * Fix argument error in client error handling
69
+
70
+ == 0.2.2 / 2011-07-28
71
+
72
+ * Bug fixes
73
+
74
+ * Fix invalid local variable error in client error handling
75
+
76
+ == 0.2.1 / 2011-07-27
77
+
78
+ * Bug fixes
79
+
80
+ * Fix error in client handling some errors caused by errant 'new' keyword
81
+
82
+ == 0.2.0 / 2011-07-20
83
+
84
+ * Major enhancements
85
+
86
+ * Replace patron with rest-client for JRuby compatibility in the client
data/LICENSE.txt ADDED
@@ -0,0 +1,17 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2
+ of this software and associated documentation files (the "Software"), to deal
3
+ in the Software without restriction, including without limitation the rights
4
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5
+ copies of the Software, and to permit persons to whom the Software is
6
+ furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in
9
+ all copies or substantial portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Jimson
2
+ ### JSON-RPC 2.0 Client and Server for Ruby
3
+ ![next build status](https://secure.travis-ci.org/chriskite/jimson.png?branch=next)
4
+
5
+ ## Client: Quick Start
6
+ require 'jimson-temp'
7
+ client = Jimson::Client.new("http://www.example.com:8999") # the URL for the JSON-RPC 2.0 server to connect to
8
+ result = client.sum(1,2) # call the 'sum' method on the RPC server and save the result '3'
9
+
10
+ ## Server: Quick Start
11
+ require 'jimson-temp'
12
+
13
+ class MyHandler
14
+ extend Jimson::Handler
15
+
16
+ def sum(a,b)
17
+ a + b
18
+ end
19
+ end
20
+
21
+ server = Jimson::Server.new(MyHandler.new)
22
+ server.start # serve with webrick on http://0.0.0.0:8999/
23
+
24
+ ## JSON Engine
25
+ Jimson uses multi\_json, so you can load the JSON library of your choice in your application and Jimson will use it automatically.
26
+
27
+ For example, require the 'json' gem in your application:
28
+ require 'json'
29
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ gem 'rubygems-tasks', '~> 0.2'
6
+ require 'rubygems/tasks'
7
+
8
+ Gem::Tasks.new
9
+
10
+ desc "Run all specs"
11
+ RSpec::Core::RakeTask.new(:rspec) do |spec|
12
+ spec.pattern = 'spec/**/*_spec.rb'
13
+ end
14
+
15
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
16
+ spec.pattern = 'spec/**/*_spec.rb'
17
+ spec.rcov = true
18
+ end
19
+
20
+ task :default => :rspec
21
+
22
+ require 'rdoc/task'
23
+
24
+ Rake::RDocTask.new do |rdoc|
25
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
26
+
27
+ rdoc.rdoc_dir = 'rdoc'
28
+ rdoc.title = "jimson #{version}"
29
+ rdoc.rdoc_files.include('README*')
30
+ rdoc.rdoc_files.include('lib/**/*.rb')
31
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.2
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'jimson/handler'
3
+ require 'jimson/router'
4
+ require 'jimson/server'
5
+ require 'jimson/client'
6
+
7
+ module Jimson
8
+ end
@@ -0,0 +1,180 @@
1
+ require 'blankslate'
2
+ require 'multi_json'
3
+ require 'rest-client'
4
+ require 'jimson/request'
5
+ require 'jimson/response'
6
+
7
+ module Jimson
8
+ class ClientHelper
9
+ JSON_RPC_VERSION = '2.0'
10
+
11
+ def self.make_id
12
+ rand(10**12)
13
+ end
14
+
15
+ def initialize(url, opts = {}, namespace = nil)
16
+ @url = url
17
+ URI.parse(@url) # for the sake of validating the url
18
+ @batch = []
19
+ @opts = opts
20
+ @namespace = namespace
21
+ @opts[:content_type] = 'application/json'
22
+ end
23
+
24
+ def process_call(sym, args)
25
+ resp = send_single_request(sym.to_s, args)
26
+
27
+ begin
28
+ data = MultiJson.decode(resp)
29
+ rescue
30
+ raise Client::Error::InvalidJSON.new(resp)
31
+ end
32
+
33
+ return process_single_response(data)
34
+
35
+ rescue Exception, StandardError => e
36
+ e.extend(Client::Error) unless e.is_a?(Client::Error)
37
+ raise e
38
+ end
39
+
40
+ def send_single_request(method, args)
41
+ namespaced_method = @namespace.nil? ? method : "#@namespace#{method}"
42
+ post_data = MultiJson.encode({
43
+ 'jsonrpc' => JSON_RPC_VERSION,
44
+ 'method' => namespaced_method,
45
+ 'params' => args,
46
+ 'id' => self.class.make_id
47
+ })
48
+ resp = RestClient.post(@url, post_data, @opts)
49
+ if resp.nil? || resp.body.nil? || resp.body.empty?
50
+ raise Client::Error::InvalidResponse.new
51
+ end
52
+
53
+ return resp.body
54
+ end
55
+
56
+ def send_batch_request(batch)
57
+ post_data = MultiJson.encode(batch)
58
+ resp = RestClient.post(@url, post_data, @opts)
59
+ if resp.nil? || resp.body.nil? || resp.body.empty?
60
+ raise Client::Error::InvalidResponse.new
61
+ end
62
+
63
+ return resp.body
64
+ end
65
+
66
+ def process_batch_response(responses)
67
+ responses.each do |resp|
68
+ saved_response = @batch.map { |r| r[1] }.select { |r| r.id == resp['id'] }.first
69
+ raise Client::Error::InvalidResponse.new if saved_response.nil?
70
+ saved_response.populate!(resp)
71
+ end
72
+ end
73
+
74
+ def process_single_response(data)
75
+ raise Client::Error::InvalidResponse.new if !valid_response?(data)
76
+
77
+ if !!data['error']
78
+ code = data['error']['code']
79
+ msg = data['error']['message']
80
+ raise Client::Error::ServerError.new(code, msg)
81
+ end
82
+
83
+ return data['result']
84
+ end
85
+
86
+ def valid_response?(data)
87
+ return false if !data.is_a?(Hash)
88
+
89
+ return false if data['jsonrpc'] != JSON_RPC_VERSION
90
+
91
+ return false if !data.has_key?('id')
92
+
93
+ return false if data.has_key?('error') && data.has_key?('result')
94
+
95
+ if data.has_key?('error')
96
+ if !data['error'].is_a?(Hash) || !data['error'].has_key?('code') || !data['error'].has_key?('message')
97
+ return false
98
+ end
99
+
100
+ if !data['error']['code'].is_a?(Fixnum) || !data['error']['message'].is_a?(String)
101
+ return false
102
+ end
103
+ end
104
+
105
+ return true
106
+
107
+ rescue
108
+ return false
109
+ end
110
+
111
+ def push_batch_request(request)
112
+ request.id = self.class.make_id
113
+ response = Response.new(request.id)
114
+ @batch << [request, response]
115
+ return response
116
+ end
117
+
118
+ def send_batch
119
+ batch = @batch.map(&:first) # get the requests
120
+ response = send_batch_request(batch)
121
+
122
+ begin
123
+ responses = MultiJson.decode(response)
124
+ rescue
125
+ raise Client::Error::InvalidJSON.new(json)
126
+ end
127
+
128
+ process_batch_response(responses)
129
+ @batch = []
130
+ end
131
+
132
+ end
133
+
134
+ class BatchClient < BlankSlate
135
+
136
+ def initialize(helper)
137
+ @helper = helper
138
+ end
139
+
140
+ def method_missing(sym, *args, &block)
141
+ request = Jimson::Request.new(sym.to_s, args)
142
+ @helper.push_batch_request(request)
143
+ end
144
+
145
+ end
146
+
147
+ class Client < BlankSlate
148
+ reveal :instance_variable_get
149
+ reveal :inspect
150
+ reveal :to_s
151
+
152
+ def self.batch(client)
153
+ helper = client.instance_variable_get(:@helper)
154
+ batch_client = BatchClient.new(helper)
155
+ yield batch_client
156
+ helper.send_batch
157
+ end
158
+
159
+ def initialize(url, opts = {}, namespace = nil)
160
+ @url, @opts, @namespace = url, opts, namespace
161
+ @helper = ClientHelper.new(url, opts, namespace)
162
+ end
163
+
164
+ def method_missing(sym, *args, &block)
165
+ @helper.process_call(sym, args)
166
+ end
167
+
168
+ def [](method, *args)
169
+ if method.is_a?(Symbol)
170
+ # namespace requested
171
+ new_ns = @namespace.nil? ? "#{method}." : "#@namespace#{method}."
172
+ return Client.new(@url, @opts, new_ns)
173
+ end
174
+ @helper.process_call(method, args)
175
+ end
176
+
177
+ end
178
+ end
179
+
180
+ require 'jimson/client/error'
@@ -0,0 +1,23 @@
1
+ module Jimson
2
+ class Client
3
+ module Error
4
+ class InvalidResponse < StandardError
5
+ def initialize()
6
+ super('Invalid or empty response from server.')
7
+ end
8
+ end
9
+
10
+ class InvalidJSON < StandardError
11
+ def initialize(json)
12
+ super("Couldn't parse JSON string received from server:\n#{json}")
13
+ end
14
+ end
15
+
16
+ class ServerError < StandardError
17
+ def initialize(code, message)
18
+ super("Server error #{code}: #{message}")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Jimson
2
+ module Handler
3
+
4
+ def jimson_default_methods
5
+ self.instance_methods.map(&:to_s) - Object.methods.map(&:to_s)
6
+ end
7
+
8
+ def jimson_expose(*methods)
9
+ @jimson_exposed_methods ||= []
10
+ @jimson_exposed_methods += methods.map(&:to_s)
11
+ end
12
+
13
+ def jimson_exclude(*methods)
14
+ @jimson_excluded_methods ||= []
15
+ @jimson_excluded_methods += methods.map(&:to_s)
16
+ end
17
+
18
+ def jimson_exposed_methods
19
+ @jimson_exposed_methods ||= []
20
+ @jimson_excluded_methods ||= []
21
+ (jimson_default_methods - @jimson_excluded_methods + @jimson_exposed_methods).sort
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Jimson
2
+ class Request
3
+
4
+ attr_accessor :method, :params, :id
5
+ def initialize(method, params, id = nil)
6
+ @method = method
7
+ @params = params
8
+ @id = id
9
+ end
10
+
11
+ def to_h
12
+ h = {
13
+ 'jsonrpc' => '2.0',
14
+ 'method' => @method
15
+ }
16
+ h.merge!('params' => @params) if !!@params && !params.empty?
17
+ h.merge!('id' => id)
18
+ end
19
+
20
+ def to_json(*a)
21
+ MultiJson.encode(self.to_h)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module Jimson
2
+ class Response
3
+ attr_accessor :result, :error, :id
4
+
5
+ def initialize(id)
6
+ @id = id
7
+ end
8
+
9
+ def to_h
10
+ h = {'jsonrpc' => '2.0'}
11
+ h.merge!('result' => @result) if !!@result
12
+ h.merge!('error' => @error) if !!@error
13
+ h.merge!('id' => @id)
14
+ end
15
+
16
+ def is_error?
17
+ !!@error
18
+ end
19
+
20
+ def succeeded?
21
+ !!@result
22
+ end
23
+
24
+ def populate!(data)
25
+ @error = data['error'] if !!data['error']
26
+ @result = data['result'] if !!data['result']
27
+ end
28
+
29
+ end
30
+ end