sync_service 0.0.8

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