sync_service 0.0.8

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.
Files changed (51) hide show
  1. data/.autotest +5 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +5 -0
  6. data/README.md +66 -0
  7. data/Rakefile +29 -0
  8. data/examples/application.rb +17 -0
  9. data/examples/client.rb +29 -0
  10. data/examples/config.ru +7 -0
  11. data/examples/php/client.php +17 -0
  12. data/examples/php/jsonRPCClient.php +165 -0
  13. data/examples/python/README +5 -0
  14. data/examples/python/client.py +16 -0
  15. data/examples/server.rb +4 -0
  16. data/lib/mobme/infrastructure/rpc/adaptor.rb +31 -0
  17. data/lib/mobme/infrastructure/rpc/base.rb +15 -0
  18. data/lib/mobme/infrastructure/rpc/error.rb +3 -0
  19. data/lib/mobme/infrastructure/rpc/runner.rb +21 -0
  20. data/lib/mobme/infrastructure/rpc/version.rb +14 -0
  21. data/lib/rpc/.gitignore +2 -0
  22. data/lib/rpc/CHANGELOG +10 -0
  23. data/lib/rpc/Gemfile +19 -0
  24. data/lib/rpc/LICENSE +20 -0
  25. data/lib/rpc/README.textile +7 -0
  26. data/lib/rpc/examples/em-http-request-json/client.rb +39 -0
  27. data/lib/rpc/examples/helpers.rb +15 -0
  28. data/lib/rpc/examples/net-http-json/client.rb +34 -0
  29. data/lib/rpc/examples/net-http-json/console.rb +13 -0
  30. data/lib/rpc/examples/server.ru +42 -0
  31. data/lib/rpc/examples/socket-json/client.rb +36 -0
  32. data/lib/rpc/examples/socket-json/server.rb +41 -0
  33. data/lib/rpc/lib/rpc.rb +166 -0
  34. data/lib/rpc/lib/rpc/clients/amqp/coolio.rb +0 -0
  35. data/lib/rpc/lib/rpc/clients/amqp/eventmachine.rb +0 -0
  36. data/lib/rpc/lib/rpc/clients/amqp/socket.rb +0 -0
  37. data/lib/rpc/lib/rpc/clients/em-http-request.rb +58 -0
  38. data/lib/rpc/lib/rpc/clients/net-http.rb +55 -0
  39. data/lib/rpc/lib/rpc/clients/redis.rb +0 -0
  40. data/lib/rpc/lib/rpc/clients/socket.rb +50 -0
  41. data/lib/rpc/lib/rpc/encoders/json.rb +142 -0
  42. data/lib/rpc/lib/rpc/encoders/xml.rb +0 -0
  43. data/lib/rpc/rpc.gemspec +34 -0
  44. data/lib/sync_service.rb +20 -0
  45. data/spec/adaptor_spec.rb +104 -0
  46. data/spec/base_spec.rb +61 -0
  47. data/spec/error_spec.rb +14 -0
  48. data/spec/runner_spec.rb +31 -0
  49. data/spec/spec_helper.rb +9 -0
  50. data/sync_service.gemspec +32 -0
  51. metadata +218 -0
data/.autotest ADDED
@@ -0,0 +1,5 @@
1
+ require 'autotest/bundler'
2
+
3
+ Autotest.add_hook(:initialize) do |at|
4
+ at.add_exception %r{^./(.git|coverage)}
5
+ end
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ vendor/
2
+ .bundle/
3
+ coverage/
4
+ spec/reports
5
+ *.log
6
+ .yardoc
7
+ *.gem
8
+
9
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --profile
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :osx do
6
+ gem "growl"
7
+ gem 'rb-fsevent'
8
+ end
9
+
10
+ group :linux do
11
+ gem "rb-inotify"
12
+ gem "libnotify"
13
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ ## Introduction
2
+
3
+ sync_service is a library to create synchronous SOA daemons. It's built on top of the JSON/RPC protocol and can be deployed anywhere you deploy a Rack application.
4
+
5
+ ## Install
6
+
7
+ Requires ruby-1.9.3p0.
8
+
9
+ Then do:
10
+
11
+ $ gem install sync_service
12
+
13
+ ## Hosting a Service
14
+
15
+ A service is just any Ruby object that descends from <code>SyncService::Base</code>. Any public method in the object is automatically exposed via SOA.
16
+
17
+
18
+ require "sync_service"
19
+
20
+ class Application < SyncService::Base
21
+ @service_name = "mobme.infrastructure.rpc.test"
22
+
23
+ def server_timestamp
24
+ Time.now.to_i
25
+ end
26
+
27
+ def buggy_method
28
+ raise MobME::Infrastructure::RPC::Error, "This exception is expected."
29
+ end
30
+
31
+ def method_missing(name, *args)
32
+ logger.err "[SERVER] received method #{name} with #{args.inspect}"
33
+ end
34
+ end
35
+
36
+ To expose the Application, you can either create a standalone server and run it via <code>SyncService::Runner</code>:
37
+
38
+ require 'sync_service'
39
+ require_relative 'application'
40
+
41
+ SyncService::Runner.start Application.new, '0.0.0.0', 8080, '/test_application'
42
+
43
+ or make a simple config.ru via SyncService::Adaptor:
44
+
45
+ require 'sync_service'
46
+ require Pathname.new(File.dirname(__FILE__)).join('application')
47
+
48
+ map("/test_application") do
49
+ run SyncService::Adaptor.new(Application.new)
50
+ end
51
+
52
+ ## Consuming a Service
53
+
54
+ To consume services, use <code>SyncService::Client</code>:
55
+
56
+ require "sync_service"
57
+ RPC.logging = true
58
+
59
+ client = SyncService::Client.setup("http://127.0.0.1:8080/test_application")
60
+
61
+ # Get result of an existing method.
62
+ puts "Server timestamp is #{client.server_timestamp}"
63
+
64
+
65
+ You can see the complete example in the examples/ folder.
66
+
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rspec/core/rake_task'
2
+ require "rake/tasklib"
3
+ require "flog"
4
+ require 'ci/reporter/rake/rspec'
5
+
6
+ RSpec::Core::RakeTask.new(:spec => ["ci:setup:rspec"]) do |t|
7
+ t.pattern = 'spec/**/*_spec.rb'
8
+ end
9
+
10
+ task :default => :spec
11
+
12
+ desc "Analyze for code complexity"
13
+ task :flog do
14
+ flog = Flog.new
15
+ flog.flog [ "lib" ]
16
+ threshold = 10
17
+
18
+ bad_methods = flog.totals.select do | name, score |
19
+ name != "main#none" && score > threshold
20
+ end
21
+ bad_methods.sort do | a, b |
22
+ a[ 1 ] <=> b[ 1 ]
23
+ end.reverse.each do | name, score |
24
+ puts "%8.1f: %s" % [ score, name ]
25
+ end
26
+ unless bad_methods.empty?
27
+ raise "#{ bad_methods.size } methods have a flog complexity > #{ threshold }"
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ require "sync_service"
2
+
3
+ class Application < SyncService::Base
4
+ @service_name = "mobme.infrastructure.rpc.test"
5
+
6
+ def server_timestamp
7
+ Time.now.to_i
8
+ end
9
+
10
+ def buggy_method
11
+ raise MobME::Infrastructure::RPC::Error, "This exception is expected."
12
+ end
13
+
14
+ def method_missing(name, *args)
15
+ logger.err "[SERVER] received method #{name} with #{args.inspect}"
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require "sync_service"
2
+
3
+ RPC.logging = true
4
+
5
+ client = SyncService::Client.setup("http://127.0.0.1:8080/test_application")
6
+
7
+ # Get result of an existing method.
8
+ puts "Server timestamp is #{client.server_timestamp}"
9
+
10
+ # Get result of a non-existing method via method_missing.
11
+ puts "Method missing works: #{client + 1}"
12
+
13
+ # Synchronous error handling.
14
+ begin
15
+ client.buggy_method
16
+ rescue MobME::Infrastructure::RPC::Error => exception
17
+ STDERR.puts "EXCEPTION CAUGHT: #{exception.inspect}"
18
+ end
19
+
20
+ # Notification isn't supported, because HTTP works in
21
+ # request/response mode, so it does behave in the same
22
+ # manner as rpc via method_missing. Sense of this is
23
+ # only to check, that it won't blow up.
24
+ puts "Sending a notification ..."
25
+ client.notification(:log, "Some shit.")
26
+
27
+ # Batch.
28
+ result = client.batch([[:log, ["Message"], nil], [:a_method, []]])
29
+ puts "Batch result: #{result}"
@@ -0,0 +1,7 @@
1
+ require 'sync_service'
2
+
3
+ require Pathname.new(File.dirname(__FILE__)).join('application')
4
+
5
+ map("/test_application") do
6
+ run SyncService::Adaptor.new(Application.new)
7
+ end
@@ -0,0 +1,17 @@
1
+ <?php
2
+ require_once 'jsonRPCClient.php';
3
+ $client = new jsonRPCClient('http://127.0.0.1:8080/test_application');
4
+
5
+ // Get result of an existing method.
6
+ echo 'Server timestamp is '. $client->server_timestamp() . "\n";
7
+
8
+ // Get result of a non-existing method via method_missing.
9
+ echo 'Method missing works: ' . $client->plus(1) . "\n";
10
+
11
+ // Synchronous error handling.
12
+ try {
13
+ $client->buggy_method();
14
+ } catch (Exception $e) {
15
+ echo 'EXCEPTION CAUGHT: ' . nl2br($e->getMessage()) . "\n";
16
+ }
17
+ ?>
@@ -0,0 +1,165 @@
1
+ <?php
2
+ /*
3
+ COPYRIGHT
4
+
5
+ Copyright 2007 Sergio Vaccaro <sergio@inservibile.org>
6
+
7
+ This file is part of JSON-RPC PHP.
8
+
9
+ JSON-RPC PHP is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+
14
+ JSON-RPC PHP is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with JSON-RPC PHP; if not, write to the Free Software
21
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
+ */
23
+
24
+ /**
25
+ * The object of this class are generic jsonRPC 1.0 clients
26
+ * http://json-rpc.org/wiki/specification
27
+ *
28
+ * @author sergio <jsonrpcphp@inservibile.org>
29
+ */
30
+ class jsonRPCClient {
31
+
32
+ /**
33
+ * Debug state
34
+ *
35
+ * @var boolean
36
+ */
37
+ private $debug;
38
+
39
+ /**
40
+ * The server URL
41
+ *
42
+ * @var string
43
+ */
44
+ private $url;
45
+ /**
46
+ * The request id
47
+ *
48
+ * @var integer
49
+ */
50
+ private $id;
51
+ /**
52
+ * If true, notifications are performed instead of requests
53
+ *
54
+ * @var boolean
55
+ */
56
+ private $notification = false;
57
+
58
+ /**
59
+ * Takes the connection parameters
60
+ *
61
+ * @param string $url
62
+ * @param boolean $debug
63
+ */
64
+ public function __construct($url,$debug = false) {
65
+ // server URL
66
+ $this->url = $url;
67
+ // proxy
68
+ empty($proxy) ? $this->proxy = '' : $this->proxy = $proxy;
69
+ // debug state
70
+ empty($debug) ? $this->debug = false : $this->debug = true;
71
+ // message id
72
+ $this->id = 1;
73
+ }
74
+
75
+ /**
76
+ * Sets the notification state of the object. In this state, notifications are performed, instead of requests.
77
+ *
78
+ * @param boolean $notification
79
+ */
80
+ public function setRPCNotification($notification) {
81
+ empty($notification) ?
82
+ $this->notification = false
83
+ :
84
+ $this->notification = true;
85
+ }
86
+
87
+ /**
88
+ * Performs a jsonRCP request and gets the results as an array
89
+ *
90
+ * @param string $method
91
+ * @param array $params
92
+ * @return array
93
+ */
94
+ public function __call($method,$params) {
95
+
96
+ // check
97
+ if (!is_scalar($method)) {
98
+ throw new Exception('Method name has no scalar value');
99
+ }
100
+
101
+ // check
102
+ if (is_array($params)) {
103
+ // no keys
104
+ $params = array_values($params);
105
+ } else {
106
+ throw new Exception('Params must be given as array');
107
+ }
108
+
109
+ // sets notification or request task
110
+ if ($this->notification) {
111
+ $currentId = NULL;
112
+ } else {
113
+ $currentId = $this->id;
114
+ }
115
+
116
+ // prepares the request
117
+ $request = array(
118
+ 'method' => $method,
119
+ 'params' => $params,
120
+ 'id' => $currentId
121
+ );
122
+ $request = json_encode($request);
123
+ $this->debug && $this->debug.='***** Request *****'."\n".$request."\n".'***** End Of request *****'."\n\n";
124
+
125
+ // performs the HTTP POST
126
+ $opts = array ('http' => array (
127
+ 'method' => 'POST',
128
+ 'header' => 'Content-type: application/json',
129
+ 'content' => $request
130
+ ));
131
+ $context = stream_context_create($opts);
132
+ if ($fp = fopen($this->url, 'r', false, $context)) {
133
+ $response = '';
134
+ while($row = fgets($fp)) {
135
+ $response.= trim($row)."\n";
136
+ }
137
+ $this->debug && $this->debug.='***** Server response *****'."\n".$response.'***** End of server response *****'."\n";
138
+ $response = json_decode($response,true);
139
+ } else {
140
+ throw new Exception('Unable to connect to '.$this->url);
141
+ }
142
+
143
+ // debug output
144
+ if ($this->debug) {
145
+ echo nl2br($debug);
146
+ }
147
+
148
+ // final checks and return
149
+ if (!$this->notification) {
150
+ // check
151
+ if ($response['id'] != $currentId) {
152
+ throw new Exception('Incorrect response id (request id: '.$currentId.', response id: '.$response['id'].')');
153
+ }
154
+ if (!is_null($response['error'])) {
155
+ throw new Exception('Request error: '.$response['error']);
156
+ }
157
+
158
+ return $response['result'];
159
+
160
+ } else {
161
+ return true;
162
+ }
163
+ }
164
+ }
165
+ ?>
@@ -0,0 +1,5 @@
1
+ Python client requires the python-jsonrpc library.
2
+
3
+ $ svn checkout http://svn.json-rpc.org/trunk/python-jsonrpc
4
+ $ cd python-jsonrpc
5
+ $ sudo python setup.py install
@@ -0,0 +1,16 @@
1
+ from jsonrpc import ServiceProxy
2
+
3
+ client = ServiceProxy("http://127.0.0.1:8080/test_application")
4
+
5
+ # Get result of an existing method.
6
+ print 'Server timestamp is ' + str(client.server_timestamp())
7
+
8
+ # Get result of a non-existing method via method_missing.
9
+ print 'Method missing works: ' + client.plus(1)
10
+
11
+ # Synchronous error handling.
12
+ try:
13
+ client.buggy_method()
14
+ except Exception, e:
15
+ print 'EXCEPTION CAUGHT: ' + e.error['message']
16
+
@@ -0,0 +1,4 @@
1
+ require 'sync_service'
2
+ require_relative 'application'
3
+
4
+ SyncService::Runner.start Application.new, '0.0.0.0', 8080, '/test_application'
@@ -0,0 +1,31 @@
1
+ module MobME::Infrastructure::RPC
2
+ class Adaptor
3
+ def initialize(service_object)
4
+ @service_object = service_object
5
+ end
6
+
7
+ def server
8
+ @server ||= RPC::Server.new(@service_object)
9
+ end
10
+
11
+ def call(env)
12
+ request = Rack::Request.new(env)
13
+ command = request.body.read
14
+ binary = server.execute(command)
15
+
16
+ if binary.match(/NoMethodError/)
17
+ response(404, binary)
18
+ else
19
+ response(200, binary)
20
+ end
21
+ end
22
+
23
+ def response(status, body)
24
+ headers = {
25
+ "Content-Type" => "application/json-rpc",
26
+ "Content-Length" => body.bytesize.to_s
27
+ }
28
+ [status, headers, [body]]
29
+ end
30
+ end
31
+ end