sr-jimson 0.11.0

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