sr-jimson 0.11.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8a05ed480e1d0fd2b07c91b468b844324b045825
4
+ data.tar.gz: 544159b0cf98683f4e33525723e0fc5c24c3d8a0
5
+ SHA512:
6
+ metadata.gz: cf2c8ad81c0bebca24ebda12b593b49cbd26e9e48dbdcb3bd92191b9baa3b36f61f5a32b3ba8fbd4a797ef77b5660133182b3f2926f1cb69d278b599afac016f
7
+ data.tar.gz: 4b7c7caaadcd51da3df4e92d431069d0971c45edb4579fdaaa6d5dc9e57681ad6ccc5d3e6b43321b23f37ab815022f4b38c3d009476408a30c8050eb4928e731
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ coverage
3
+ .rvmrc
4
+ *.swp
5
+ Gemfile.lock
6
+ *.rbc
7
+ .bundle
8
+ .config
9
+ .yardoc
10
+ InstalledFiles
11
+ _yardoc
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,92 @@
1
+ == 0.10.0 / 2013-06-28
2
+
3
+ * Minor enhancements
4
+
5
+ * Update dependency versions
6
+
7
+ == 0.9.1 / 2012-09-18
8
+
9
+ * Bug fixes
10
+
11
+ * Allow opts to be passed to Server.with_routes
12
+
13
+ == 0.9.0 / 2012-08-22
14
+
15
+ * Minor enhancements
16
+
17
+ * Add show_errors option to server, which will cause application errors to include the error name and the first line of the backtrace
18
+
19
+ == 0.8.0 / 2012-08-17
20
+
21
+ * Major enhancements
22
+
23
+ * Add namespaced method calls to client (e.g. 'client[:foo].sum(1,2,3) # calls foo.sum')
24
+ * Add Server.with_routes to quickly created a routed server
25
+
26
+ == 0.7.1 / 2012-08-16
27
+
28
+ * Bug fixes
29
+
30
+ * Fix handling of array params in client, which were erroneously being flattened
31
+
32
+ == 0.7.0 / 2012-04-13
33
+
34
+ * Major enhancements
35
+
36
+ * Add namespaced routing
37
+
38
+ * Bug fixes
39
+
40
+ * Fix deprecation warning about RDoc task in Rakefile
41
+
42
+ == 0.6.0 / 2012-03-14
43
+
44
+ * Minor enhancements
45
+
46
+ * Add ability to pass options to Rack and RestClient
47
+
48
+ == 0.5.0 / 2012-03-06
49
+
50
+ * Major enhancements
51
+
52
+ * Switch to MultiJson from json gem
53
+
54
+ * Bug fixes
55
+
56
+ * Allow BigNum in 'id' field of request and response
57
+
58
+ == 0.3.1 / 2011-08-11
59
+
60
+ * Minor enhancements
61
+
62
+ * Refactor the way the server is intantiated/started to work better with config.ru
63
+
64
+ == 0.3.0 / 2011-08-11
65
+
66
+ * Major enhancements
67
+
68
+ * Replace eventmachine-httpserver with rack for more cross-platform goodness
69
+
70
+ == 0.2.3 / 2011-08-01
71
+
72
+ * Bug fixes
73
+
74
+ * Fix argument error in client error handling
75
+
76
+ == 0.2.2 / 2011-07-28
77
+
78
+ * Bug fixes
79
+
80
+ * Fix invalid local variable error in client error handling
81
+
82
+ == 0.2.1 / 2011-07-27
83
+
84
+ * Bug fixes
85
+
86
+ * Fix error in client handling some errors caused by errant 'new' keyword
87
+
88
+ == 0.2.0 / 2011-07-20
89
+
90
+ * Major enhancements
91
+
92
+ * Replace patron with rest-client for JRuby compatibility in the client
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sr-jimson.gemspec
4
+ gemspec
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'
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'
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,21 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'rdoc/task'
4
+
5
+ RSpec::Core::RakeTask.new(:rspec)
6
+
7
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
8
+ ENV['COVERAGE'] = 'true'
9
+ Rake::Task["rspec"].execute
10
+ end
11
+
12
+ Rake::RDocTask.new do |rdoc|
13
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
14
+
15
+ rdoc.rdoc_dir = 'rdoc'
16
+ rdoc.title = "jimson #{version}"
17
+ rdoc.rdoc_files.include('README*')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
20
+
21
+ task :default => :rspec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.10.0
data/lib/sr/jimson.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Sr
2
+ module Jimson
3
+ VERSION = "0.11.0"
4
+
5
+ autoload :Handler, 'sr/jimson/handler'
6
+ autoload :Router, 'sr/jimson/router'
7
+ autoload :Server, 'sr/jimson/server'
8
+ autoload :Client, 'sr/jimson/client'
9
+ end
10
+ end
@@ -0,0 +1,132 @@
1
+ #--
2
+ # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
3
+ # All rights reserved.
4
+
5
+ # Permission is granted for use, copying, modification, distribution,
6
+ # and distribution of modified versions of this work as long as the
7
+ # above copyright notice is included.
8
+ #++
9
+
10
+ class String
11
+ if instance_methods.first.is_a?(Symbol)
12
+ def _blankslate_as_name
13
+ to_sym
14
+ end
15
+ else
16
+ def _blankslate_as_name
17
+ self
18
+ end
19
+ end
20
+ end
21
+
22
+ class Symbol
23
+ if instance_methods.first.is_a?(Symbol)
24
+ def _blankslate_as_name
25
+ self
26
+ end
27
+ else
28
+ def _blankslate_as_name
29
+ to_s
30
+ end
31
+ end
32
+ end
33
+
34
+ ######################################################################
35
+ # BlankSlate provides an abstract base class with no predefined
36
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
37
+ # BlankSlate is useful as a base class when writing classes that
38
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
39
+ #
40
+ class BlankSlate
41
+ class << self
42
+
43
+ # Hide the method named +name+ in the BlankSlate class. Don't
44
+ # hide +instance_eval+ or any method beginning with "__".
45
+ def hide(name)
46
+ if instance_methods.include?(name._blankslate_as_name) and
47
+ name !~ /^(__|(instance_eval|object_id)$)/
48
+ @hidden_methods ||= {}
49
+ @hidden_methods[name.to_sym] = instance_method(name)
50
+ undef_method name
51
+ end
52
+ end
53
+
54
+ def find_hidden_method(name)
55
+ @hidden_methods ||= {}
56
+ @hidden_methods[name] || superclass.find_hidden_method(name)
57
+ end
58
+
59
+ # Redefine a previously hidden method so that it may be called on a blank
60
+ # slate object.
61
+ def reveal(name)
62
+ hidden_method = find_hidden_method(name)
63
+ fail "Don't know how to reveal method '#{name}'" unless hidden_method
64
+ define_method(name, hidden_method)
65
+ end
66
+ end
67
+
68
+ instance_methods.each { |m| hide(m) }
69
+ end
70
+
71
+ ######################################################################
72
+ # Since Ruby is very dynamic, methods added to the ancestors of
73
+ # BlankSlate <em>after BlankSlate is defined</em> will show up in the
74
+ # list of available BlankSlate methods. We handle this by defining a
75
+ # hook in the Object and Kernel classes that will hide any method
76
+ # defined after BlankSlate has been loaded.
77
+ #
78
+ module Kernel
79
+ class << self
80
+ alias_method :blank_slate_method_added, :method_added
81
+
82
+ # Detect method additions to Kernel and remove them in the
83
+ # BlankSlate class.
84
+ def method_added(name)
85
+ result = blank_slate_method_added(name)
86
+ return result if self != Kernel
87
+ BlankSlate.hide(name)
88
+ result
89
+ end
90
+ end
91
+ end
92
+
93
+ ######################################################################
94
+ # Same as above, except in Object.
95
+ #
96
+ class Object
97
+ class << self
98
+ alias_method :blank_slate_method_added, :method_added
99
+
100
+ # Detect method additions to Object and remove them in the
101
+ # BlankSlate class.
102
+ def method_added(name)
103
+ result = blank_slate_method_added(name)
104
+ return result if self != Object
105
+ BlankSlate.hide(name)
106
+ result
107
+ end
108
+
109
+ def find_hidden_method(name)
110
+ nil
111
+ end
112
+ end
113
+ end
114
+
115
+ ######################################################################
116
+ # Also, modules included into Object need to be scanned and have their
117
+ # instance methods removed from blank slate. In theory, modules
118
+ # included into Kernel would have to be removed as well, but a
119
+ # "feature" of Ruby prevents late includes into modules from being
120
+ # exposed in the first place.
121
+ #
122
+ class Module
123
+ alias blankslate_original_append_features append_features
124
+ def append_features(mod)
125
+ result = blankslate_original_append_features(mod)
126
+ return result if mod != Object
127
+ instance_methods.each do |name|
128
+ BlankSlate.hide(name)
129
+ end
130
+ result
131
+ end
132
+ end
@@ -0,0 +1,180 @@
1
+ require 'multi_json'
2
+ require 'rest-client'
3
+ require 'sr/jimson/blankslate'
4
+ require 'sr/jimson/request'
5
+ require 'sr/jimson/response'
6
+
7
+ module Sr::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 = Sr::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 'sr/jimson/client/error'