skein 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +69 -0
  4. data/README.md +57 -0
  5. data/RELEASES.md +4 -0
  6. data/Rakefile +30 -0
  7. data/VERSION +1 -0
  8. data/bin/skein +186 -0
  9. data/config/.gitignore +3 -0
  10. data/config/skein.yml.example +11 -0
  11. data/lib/skein.rb +24 -0
  12. data/lib/skein/client.rb +51 -0
  13. data/lib/skein/client/publisher.rb +14 -0
  14. data/lib/skein/client/rpc.rb +96 -0
  15. data/lib/skein/client/subscriber.rb +25 -0
  16. data/lib/skein/client/worker.rb +51 -0
  17. data/lib/skein/config.rb +87 -0
  18. data/lib/skein/connected.rb +52 -0
  19. data/lib/skein/context.rb +38 -0
  20. data/lib/skein/handler.rb +86 -0
  21. data/lib/skein/handler/async.rb +9 -0
  22. data/lib/skein/handler/threaded.rb +7 -0
  23. data/lib/skein/rabbitmq.rb +44 -0
  24. data/lib/skein/reporter.rb +11 -0
  25. data/lib/skein/rpc.rb +24 -0
  26. data/lib/skein/rpc/base.rb +23 -0
  27. data/lib/skein/rpc/error.rb +34 -0
  28. data/lib/skein/rpc/notification.rb +2 -0
  29. data/lib/skein/rpc/request.rb +62 -0
  30. data/lib/skein/rpc/response.rb +38 -0
  31. data/lib/skein/support.rb +67 -0
  32. data/skein.gemspec +95 -0
  33. data/test/data/sample_config.yml +13 -0
  34. data/test/helper.rb +42 -0
  35. data/test/script/em_example +28 -0
  36. data/test/unit/test_skein_client.rb +18 -0
  37. data/test/unit/test_skein_client_publisher.rb +10 -0
  38. data/test/unit/test_skein_client_subscriber.rb +41 -0
  39. data/test/unit/test_skein_client_worker.rb +61 -0
  40. data/test/unit/test_skein_config.rb +33 -0
  41. data/test/unit/test_skein_context.rb +44 -0
  42. data/test/unit/test_skein_rabbitmq.rb +14 -0
  43. data/test/unit/test_skein_reporter.rb +4 -0
  44. data/test/unit/test_skein_rpc_error.rb +10 -0
  45. data/test/unit/test_skein_rpc_request.rb +93 -0
  46. data/test/unit/test_skein_support.rb +95 -0
  47. metadata +148 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6896b1151e348573c5b58ee131b6f568373d7547
4
+ data.tar.gz: 4ae4f3c7de9c5eaf0d17e9e0ec77ef02390824f1
5
+ SHA512:
6
+ metadata.gz: 32a30b3dc6c37ac747d372f9379f90463f0c36de086ef2eb464eedc0d5f93b8b88e8737c5d0cd3a42dcbed0ccdd0d12e43bf2ac6f4f7679527d7f2e8e8aa88cc
7
+ data.tar.gz: fb8169c2061892dbe1c2c05da18a672646e09abf773a020ccbe556cabdbc997b6c8d85548f8392a751d834dc7a13c1970e59147d7d73d58d778caa3be50cafde
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'birling'
4
+
5
+ group :development, :test do
6
+ gem 'rake'
7
+ gem 'jeweler'
8
+ gem 'test-unit'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,69 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.5.0)
5
+ public_suffix (~> 2.0, >= 2.0.2)
6
+ birling (0.1.3)
7
+ builder (3.2.2)
8
+ descendants_tracker (0.0.4)
9
+ thread_safe (~> 0.3, >= 0.3.1)
10
+ faraday (0.9.2)
11
+ multipart-post (>= 1.2, < 3)
12
+ git (1.3.0)
13
+ github_api (0.11.3)
14
+ addressable (~> 2.3)
15
+ descendants_tracker (~> 0.0.1)
16
+ faraday (~> 0.8, < 0.10)
17
+ hashie (>= 1.2)
18
+ multi_json (>= 1.7.5, < 2.0)
19
+ nokogiri (~> 1.6.0)
20
+ oauth2
21
+ hashie (3.4.6)
22
+ highline (1.7.8)
23
+ jeweler (2.1.2)
24
+ builder
25
+ bundler (>= 1.0)
26
+ git (>= 1.2.5)
27
+ github_api (~> 0.11.0)
28
+ highline (>= 1.6.15)
29
+ nokogiri (>= 1.5.10)
30
+ rake
31
+ rdoc
32
+ semver
33
+ jwt (1.5.6)
34
+ mini_portile2 (2.1.0)
35
+ multi_json (1.12.1)
36
+ multi_xml (0.5.5)
37
+ multipart-post (2.0.0)
38
+ nokogiri (1.6.8.1)
39
+ mini_portile2 (~> 2.1.0)
40
+ nokogiri (1.6.8.1-java)
41
+ oauth2 (1.2.0)
42
+ faraday (>= 0.8, < 0.10)
43
+ jwt (~> 1.0)
44
+ multi_json (~> 1.3)
45
+ multi_xml (~> 0.5)
46
+ rack (>= 1.2, < 3)
47
+ power_assert (0.3.1)
48
+ public_suffix (2.0.4)
49
+ rack (2.0.1)
50
+ rake (11.3.0)
51
+ rdoc (5.0.0)
52
+ semver (1.0.1)
53
+ test-unit (3.2.1)
54
+ power_assert
55
+ thread_safe (0.3.5)
56
+ thread_safe (0.3.5-java)
57
+
58
+ PLATFORMS
59
+ java
60
+ ruby
61
+
62
+ DEPENDENCIES
63
+ birling
64
+ jeweler
65
+ rake
66
+ test-unit
67
+
68
+ BUNDLED WITH
69
+ 1.13.6
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Skein
2
+
3
+ [Skein](https://en.wikipedia.org/wiki/V_formation) is a RabbitMQ-based standard
4
+ and implementation for Ruby that defines how to dispatch
5
+ [JSON-RPC](http://json-rpc.org) jobs over AMQP.
6
+
7
+ ## Dependencies
8
+
9
+ This library requires an active AMQP server like [RabbitMQ](http://rabbitmq.com)
10
+ and a Ruby driver for AMQP like [Bunny](http://rubybunny.info) or
11
+ [March Hare](http://rubymarchhare.info).
12
+
13
+ Both jRuby and MRI Ruby are supported.
14
+
15
+ ## Installation
16
+
17
+ The default [Bundler](http://bundler.io) configuration should be a good place
18
+ to start:
19
+
20
+ bundle install
21
+
22
+ ## Configuration
23
+
24
+ For testing, set up `config/rabbitmq.yml` with configuration parameters that
25
+ define how to connect to RabbitMQ.
26
+
27
+ ## Client Modes
28
+
29
+ ### RPC
30
+
31
+ An RPC client can make blocking or non-blocking calls. By default calls are
32
+ blocking, but they can be made non-blocking by adding `!` to the end of the
33
+ method name. For example:
34
+
35
+ client = Skein::Client.rpc('test_queue')
36
+
37
+ client.request!(test: 'data')
38
+
39
+ client.close
40
+
41
+ Note that non-blocking calls are fire-and-forget, there is no way of knowing
42
+ if that operation succeeded or failed.
43
+
44
+ ### Worker
45
+
46
+ The back-end that receives and processes RPC calls is instantiated as
47
+ a `Skein::Client::Worker` instance:
48
+
49
+ class Responder < Skein::Client::Worker
50
+ def request
51
+ {
52
+ result: true
53
+ }
54
+ end
55
+ end
56
+
57
+ Responder.new('test_queue')
data/RELEASES.md ADDED
@@ -0,0 +1,4 @@
1
+ * 0.3.0 - Adding EventMachine compatibility
2
+ * 0.2.1 - Cleaning up RPC interface with blocking vs. non-blocking
3
+ * 0.2.0 - First working version in JRuby/MRI Ruby
4
+ * 0.1.0 - Proof of concept prototype.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake/testtask'
4
+
5
+ # -- Jeweler ----------------------------------------------------------------
6
+
7
+ require 'jeweler'
8
+
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = 'skein'
11
+ gem.homepage = 'http://github.com/postageapp/skein'
12
+ gem.license = 'closed'
13
+ gem.summary = %Q{RabbitMQ RPC/PubSub Library}
14
+ gem.description = %Q{Wrapper for RabbitMQ that makes blocking RPC calls and handles pub-sub broadcasts.}
15
+ gem.email = 'tadman@postageapp.com'
16
+ gem.authors = [ 'Scott Tadman' ]
17
+ end
18
+
19
+ Jeweler::RubygemsDotOrgTasks.new
20
+
21
+ # -- test/unit --------------------------------------------------------------
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs << 'test'
25
+ t.test_files = FileList['test/**/test*.rb']
26
+ t.verbose = true
27
+ t.warning = !!ENV['RUBY_WARN']
28
+ end
29
+
30
+ task default: :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
data/bin/skein ADDED
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # == Imports ================================================================
4
+
5
+ require 'optparse'
6
+ require 'thread'
7
+
8
+ require_relative '../lib/skein'
9
+
10
+ # == Support Classes ========================================================
11
+
12
+ class EchoWorker < Skein::Client::Worker
13
+ def initialize(queue_name, options = nil)
14
+ super(queue_name, options || { })
15
+
16
+ @debug = options && options[:debug]
17
+ end
18
+
19
+ def echo(text)
20
+ if (@debug)
21
+ puts text
22
+ end
23
+
24
+ text
25
+ end
26
+ end
27
+
28
+ # == Support Methods ========================================================
29
+
30
+ def rescue_safely(options)
31
+ yield
32
+
33
+ rescue Object => e
34
+ $stderr.puts('[%s] %s' % [ e.class, e ])
35
+
36
+ if (options[:trace])
37
+ $stderr.puts(e.backtrace)
38
+ end
39
+
40
+ exit(-1)
41
+ end
42
+
43
+ def in_thread(options)
44
+ Thread.new do
45
+ begin
46
+ Thread.abort_on_exception = true
47
+
48
+ rescue_safely(options) do
49
+ yield
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # == Main ===================================================================
56
+
57
+ options = {
58
+ count: 1,
59
+ threads: 1
60
+ }
61
+
62
+ parser = OptionParser.new do |parser|
63
+ parser.on('-v', '--verbose') do
64
+ options[:verbose] = true
65
+ end
66
+
67
+ parser.on('-n', '--count=n') do |n|
68
+ options[:count] = n.to_i
69
+ end
70
+ parser.on('-c', '--threads=n') do |n|
71
+ options[:threads] = n.to_i
72
+ end
73
+ parser.on('-t', '--trace') do
74
+ options[:trace] = true
75
+ end
76
+ parser.on('-d', '--debug') do
77
+ options[:debug] = true
78
+ end
79
+
80
+ parser.on('-h', '--help') do
81
+ puts parser
82
+ exit(0)
83
+ end
84
+ end
85
+
86
+ Skein::RabbitMQ.force_require!
87
+
88
+ args = parser.parse(*ARGV)
89
+
90
+ case (command = args.shift)
91
+ when 'config'
92
+ Skein::Support.hash_format(Skein.config).each do |line|
93
+ puts line
94
+ end
95
+ when 'test'
96
+ rescue_safely(options) do
97
+ Skein::RabbitMQ.connect
98
+
99
+ puts '[OK] Connection succeeded.'
100
+ end
101
+ when 'publish'
102
+ rescue_safely(options) do
103
+ publisher = Skein::Client.publisher('test_pubsub')
104
+
105
+ loop do
106
+ publisher << { test: Time.now.to_f }
107
+
108
+ sleep(1)
109
+ end
110
+ end
111
+ when 'subscribe'
112
+ rescue_safely(options) do
113
+ subscriber = Skein::Client.subscriber('test_pubsub')
114
+
115
+ subscriber.listen do |message, metadata|
116
+ puts metadata.inspect
117
+ puts message.inspect
118
+ end
119
+ end
120
+ when 'echo'
121
+ rescue_safely(options) do
122
+ results = Queue.new
123
+
124
+ start = Time.now
125
+
126
+ count = Hash.new(0)
127
+
128
+ tabulator = Thread.new do
129
+ loop do
130
+ v = results.pop
131
+
132
+ break if (v.nil?)
133
+
134
+ count[v] += 1
135
+ end
136
+ end
137
+
138
+ options[:threads].times.map do
139
+ in_thread(options) do
140
+ client = Skein::Client.new
141
+ rpc = client.rpc('test_echo')
142
+
143
+ options[:count].times do |i|
144
+ test_data = SecureRandom.uuid
145
+
146
+ response = rpc.echo(test_data)
147
+
148
+ results << (response == test_data)
149
+
150
+ if (options[:verbose])
151
+ puts '[%s] %s (%d/%d)' % [
152
+ (response == test_data ? 'OK' : 'ERR'),
153
+ response.inspect,
154
+ i + 1,
155
+ options[:count]
156
+ ]
157
+ end
158
+ end
159
+
160
+ rpc.close
161
+ client.close
162
+ end
163
+ end.each(&:join)
164
+
165
+ results << nil
166
+ tabulator.join
167
+
168
+ elapsed = Time.now - start
169
+
170
+ puts 'Success: %d Failed: %d in %.1fms [%d mps]' % [
171
+ count[true],
172
+ count[false],
173
+ elapsed.to_f * 1000,
174
+ count[true] > 0 ? (count[true].to_f / elapsed.to_f) : 0
175
+ ]
176
+ end
177
+ when 'echo_server'
178
+ rescue_safely(options) do
179
+ options[:threads].times.map do
180
+ EchoWorker.new('test_echo')
181
+ end.each(&:join)
182
+ end
183
+ else
184
+ $stderr.puts('Unknown command: %s' % command)
185
+ exit(-1)
186
+ end
data/config/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.yml
2
+ *.json
3
+
@@ -0,0 +1,11 @@
1
+ defaults: &defaults
2
+ host: 127.0.0.1
3
+ port: 5672
4
+ username: guest
5
+ password: guest
6
+
7
+ development:
8
+ <<: *defaults
9
+
10
+ test:
11
+ <<: *defaults
data/lib/skein.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'json'
2
+
3
+ module Skein
4
+ VERSION = File.read(File.expand_path('../VERSION', File.dirname(__FILE__))).chomp.freeze
5
+
6
+ def self.version
7
+ VERSION
8
+ end
9
+
10
+ def self.config
11
+ @config ||= Skein::Config.new
12
+ end
13
+ end
14
+
15
+ require_relative './skein/connected'
16
+
17
+ require_relative './skein/client'
18
+ require_relative './skein/config'
19
+ require_relative './skein/context'
20
+ require_relative './skein/handler'
21
+ require_relative './skein/rabbitmq'
22
+ require_relative './skein/reporter'
23
+ require_relative './skein/rpc'
24
+ require_relative './skein/support'
@@ -0,0 +1,51 @@
1
+ require 'securerandom'
2
+ require 'fiber'
3
+
4
+ class Skein::Client < Skein::Connected
5
+ # == Properties ===========================================================
6
+
7
+ # == Class Methods ========================================================
8
+
9
+ def self.rpc(*args)
10
+ new.rpc(*args)
11
+ end
12
+
13
+ def self.receiver(*args)
14
+ new.receiver(*args)
15
+ end
16
+
17
+ def self.publisher(*args)
18
+ new.publisher(*args)
19
+ end
20
+
21
+ def self.subscriber(*args)
22
+ new.subscriber(*args)
23
+ end
24
+
25
+ # == Instance Methods =====================================================
26
+
27
+ def initialize(connection: nil, context: nil)
28
+ super(connection: connection, context: context)
29
+ end
30
+
31
+ def rpc(queue_name = nil)
32
+ Skein::Client::RPC.new(queue_name, connection: self.connection, context: self.context)
33
+ end
34
+
35
+ def receiver
36
+ Skein::Client::Receiver.new(connection: self.connection, context: self.context)
37
+ end
38
+
39
+ def publisher(queue_name)
40
+ Skein::Client::Publisher.new(queue_name, connection: self.connection, context: self.context)
41
+ end
42
+
43
+ def subscriber(queue_name, routing_key = nil)
44
+ Skein::Client::Subscriber.new(queue_name, routing_key, connection: self.connection, context: self.context)
45
+ end
46
+ end
47
+
48
+ require_relative './client/publisher'
49
+ require_relative './client/rpc'
50
+ require_relative './client/subscriber'
51
+ require_relative './client/worker'