skein 0.3.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.
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'