jimson-temp 0.9.2

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