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
data/test/helper.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'test/unit'
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
4
+
5
+ require 'skein'
6
+
7
+ # Simulate Rails 'test' environment
8
+ ENV['RAILS_ENV'] = 'test'
9
+
10
+ # Ensure tests are run from the root of the project so that configuration
11
+ # files can be found and loaded.
12
+ Dir.chdir(File.expand_path('../', File.dirname(__FILE__)))
13
+
14
+ class Test::Unit::TestCase
15
+ def assert_mapping(map)
16
+ result_map = map.each_with_object({ }) do |(k,v), h|
17
+ h[k] = yield(k)
18
+ end
19
+
20
+ assert_equal map, result_map do
21
+ result_map.each_with_object([ ]) do |(k,v), a|
22
+ unless (v == map[k])
23
+ a << k
24
+ end
25
+ end.map do |s|
26
+ "Input: #{s.inspect}\n Expected: #{map[s].inspect}\n Result: #{result_map[s].inspect}\n"
27
+ end.join
28
+ end
29
+ end
30
+
31
+ def assert_no_threads(message = nil)
32
+ threads = Thread.list.length
33
+
34
+ yield
35
+
36
+ assert_equal threads, Thread.list.length, message
37
+ end
38
+
39
+ def data_path(name)
40
+ File.expand_path(File.join('data', name), File.dirname(__FILE__))
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ gem 'eventmachine'
6
+ require 'eventmachine'
7
+
8
+ $LOAD_PATH << File.expand_path('../../lib', File.dirname(__FILE__))
9
+
10
+ require 'skein'
11
+
12
+ # == Support Classes ========================================================
13
+
14
+ class EchoWorker < Skein::Client::Worker
15
+ def echo(*args)
16
+ yield(*args)
17
+ end
18
+
19
+ def async?
20
+ true
21
+ end
22
+ end
23
+
24
+ # == Main ===================================================================
25
+
26
+ EventMachine.run do
27
+ EchoWorker.new('test_echo')
28
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinClient < Test::Unit::TestCase
4
+ def test_default
5
+ client = nil
6
+
7
+ assert_no_threads do
8
+ client = Skein::Client.new
9
+
10
+ assert client.context
11
+
12
+ client.close
13
+ end
14
+
15
+ # ensure
16
+ # client and client.close
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinPublisher < Test::Unit::TestCase
4
+ def test_defaults
5
+ publisher = Skein::Client::Publisher.new('test_name')
6
+
7
+ ensure
8
+ publisher and publisher.close
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinClientSubscriber < Test::Unit::TestCase
4
+ def test_cycle
5
+ client = Skein::Client.new
6
+
7
+ publisher = client.publisher('test')
8
+
9
+ received = nil
10
+
11
+ subscriber = client.subscriber('test', '*.*')
12
+ subscribing = false
13
+
14
+ thread = Thread.new do
15
+ Thread.abort_on_exception = true
16
+
17
+ subscribing = true
18
+
19
+ subscriber.listen do |payload|
20
+ received = payload
21
+
22
+ Thread.exit
23
+ end
24
+ end
25
+
26
+ while (!subscribing)
27
+ # Spin-lock to wait for the subscriber to come online
28
+ end
29
+
30
+ publisher.publish!({ data: true }, 'test.notification')
31
+
32
+ thread.join
33
+
34
+ assert_equal({ "data" => true }, received)
35
+
36
+ ensure
37
+ publisher and publisher.close
38
+ subscriber and subscriber.close
39
+ client and client.close
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinClientWorker < Test::Unit::TestCase
4
+ class ErrorGenerator < Skein::Client::Worker
5
+ class CustomError < RuntimeError
6
+ end
7
+
8
+ def raises_error
9
+ raise CustomError, 'Example error!'
10
+ end
11
+ end
12
+
13
+ def test_example
14
+ worker = Skein::Client::Worker.new('test_rpc')
15
+ handler = Skein::Handler.for(worker)
16
+
17
+ message = {
18
+ method: 'ident',
19
+ params: [ ],
20
+ id: '43d8352c-4907-4c32-9c81-fc34e91a3884'
21
+ }
22
+
23
+ handler.handle(JSON.dump(message)) do |response_json, error|
24
+ response = JSON.load(response_json)
25
+
26
+ expected = {
27
+ 'result' => worker.ident,
28
+ 'error' => nil,
29
+ 'id' => message[:id]
30
+ }
31
+
32
+ assert_equal(expected, response)
33
+ end
34
+
35
+ ensure
36
+ worker and worker.close
37
+ end
38
+
39
+ def test_throws_exception
40
+ worker = ErrorGenerator.new('test_error')
41
+ handler = Skein::Handler.for(worker)
42
+
43
+ message = {
44
+ method: 'raises_error',
45
+ id: '29fe8a40-fccf-43c6-ba48-818598c66e6f'
46
+ }
47
+
48
+ handler.handle(JSON.dump(message)) do |response_json, error|
49
+ expected = {
50
+ 'result' => nil,
51
+ 'error' => '[TestSkeinClientWorker::ErrorGenerator::CustomError] Example error!',
52
+ 'id' => message[:id]
53
+ }
54
+
55
+ assert_equal(expected.to_json, response_json)
56
+ end
57
+
58
+ ensure
59
+ worker and worker.close
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinConfig < Test::Unit::TestCase
4
+ def test_env
5
+ assert_equal('test', Skein::Config.env)
6
+ end
7
+
8
+ def test_default_state
9
+ config = Skein::Config.new(false)
10
+
11
+ assert config
12
+
13
+ assert_equal('127.0.0.1', config.host)
14
+ assert_equal(5672, config.port)
15
+ assert_equal('guest', config.username)
16
+ assert_equal('guest', config.password)
17
+ assert_equal(nil, config.namespace)
18
+ end
19
+
20
+ def test_with_config_path
21
+ config = Skein::Config.new(data_path('sample_config.yml'))
22
+
23
+ assert_equal('test.host', config.host)
24
+ assert_equal(5670, config.port)
25
+ assert_equal('test_user', config.username)
26
+ assert_equal('test_password', config.password)
27
+ assert_equal('test', config.namespace)
28
+ end
29
+
30
+ def test_config_exists
31
+ assert_equal(true, Skein::Config.exist?)
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinContext < Test::Unit::TestCase
4
+ def test_default
5
+ context = Skein::Context.default
6
+
7
+ assert(context)
8
+
9
+ assert_equal(Skein::Support.hostname, context.hostname)
10
+ assert_equal(Skein::Support.process_name, context.process_name)
11
+ assert_equal(Skein::Support.process_id, context.process_id)
12
+ end
13
+
14
+ def test_defaults
15
+ context = Skein::Context.new
16
+
17
+ assert_equal(Skein::Support.hostname, context.hostname)
18
+ assert_equal(Skein::Support.process_name, context.process_name)
19
+ assert_equal(Skein::Support.process_id, context.process_id)
20
+ end
21
+
22
+ def test_override
23
+ context = Skein::Context.new(
24
+ hostname: 'sample.host',
25
+ process_name: 'test_process',
26
+ process_id: 20301
27
+ )
28
+
29
+ assert_equal('sample.host', context.hostname)
30
+ assert_equal('test_process', context.process_name)
31
+ assert_equal(20301, context.process_id)
32
+ end
33
+
34
+ def test_generate_ident
35
+ context = Skein::Context.new
36
+
37
+ ident = context.ident(self)
38
+
39
+ assert(ident)
40
+
41
+ assert_not_equal(ident, context.ident(context))
42
+ assert_equal(ident, context.ident(self))
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinRabbitMQ < Test::Unit::TestCase
4
+ def test_can_connect
5
+ rmq = Skein::RabbitMQ.connect
6
+
7
+ assert rmq
8
+
9
+ assert rmq.respond_to?(:create_channel)
10
+
11
+ ensure
12
+ rmq and rmq.close
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinReporter < Test::Unit::TestCase
4
+ end
@@ -0,0 +1,10 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinRPCError < Test::Unit::TestCase
4
+ def test_default
5
+ error = Skein::RPC::Error.new
6
+
7
+ assert_equal nil, error.id
8
+ assert_equal nil, error.error
9
+ end
10
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinRPCRequest < Test::Unit::TestCase
4
+ def test_default
5
+ request = Skein::RPC::Request.new
6
+
7
+ assert request.id
8
+ assert request.id.match(/\A\h{8}\-\h{4}\-\h{4}\-\h{4}\-\h{12}\z/)
9
+
10
+ assert_equal nil, request.method
11
+ assert_equal nil, request.params
12
+ end
13
+
14
+ def test_with_invalid_method_name
15
+ request = Skein::RPC::Request.new(
16
+ method: 'invalid name',
17
+ id: 'aa8304bd-5c4a-4b77-a1bf-87f90d59b3af'
18
+ )
19
+
20
+ rescue Skein::RPC::InvalidMethod => e
21
+ assert e
22
+
23
+ assert e.request.is_a?(Skein::RPC::Request)
24
+ assert_equal 'aa8304bd-5c4a-4b77-a1bf-87f90d59b3af', e.to_error.id
25
+ else
26
+ fail
27
+ end
28
+
29
+ def test_with_no_method_name
30
+ request = Skein::RPC::Request.new(
31
+ method: nil,
32
+ id: 'aa8304bd-5c4a-4b77-a1bf-87f90d59b3af'
33
+ )
34
+ end
35
+
36
+ def test_with_single_param
37
+ request = Skein::RPC::Request.new(
38
+ method: 'single_param',
39
+ params: 'single'
40
+ )
41
+
42
+ assert_equal %w[ single ], request.params
43
+ end
44
+
45
+ def test_from_json_string
46
+ raw = {
47
+ method: 'test_method',
48
+ params: nil,
49
+ id: 'e0b6cffa-8040-4c44-bc11-7fc3d8f4662c'
50
+ }
51
+
52
+ json = JSON.dump(raw)
53
+
54
+ request = Skein::RPC::Request.new(json)
55
+
56
+ assert_equal 'test_method', request.method
57
+ assert_equal nil, request.params
58
+ assert_equal 'e0b6cffa-8040-4c44-bc11-7fc3d8f4662c', request.id
59
+
60
+ assert_equal raw, request.to_h
61
+ end
62
+
63
+ def test_from_hash
64
+ raw = {
65
+ method: 'test_method',
66
+ params: nil,
67
+ id: 'e0b6cffa-8040-4c44-bc11-7fc3d8f4662c'
68
+ }
69
+
70
+ request = Skein::RPC::Request.new(raw)
71
+
72
+ assert_equal 'test_method', request.method
73
+ assert_equal nil, request.params
74
+ assert_equal 'e0b6cffa-8040-4c44-bc11-7fc3d8f4662c', request.id
75
+
76
+ assert_equal raw, request.to_h
77
+ end
78
+
79
+ def test_to_response_result
80
+ request = Skein::RPC::Request.new(
81
+ method: 'test_method',
82
+ params: 'test',
83
+ id: 'd8b625f1-5e0b-4bcf-bf6e-569e9edc634d'
84
+ )
85
+
86
+ response = request.response(
87
+ result: %w[ result ]
88
+ )
89
+
90
+ assert_equal request.id, response.id
91
+ assert_equal %w[ result ], response.result
92
+ end
93
+ end
@@ -0,0 +1,95 @@
1
+ require_relative '../helper'
2
+
3
+ class TestSkeinSupport < Test::Unit::TestCase
4
+ def test_symbolize_keys_simple_hash
5
+ hash = {
6
+ 'test' => 'test_value',
7
+ true => 'true_value',
8
+ 2 => 'two'
9
+ }
10
+
11
+ expected = {
12
+ test: 'test_value',
13
+ true: 'true_value',
14
+ '2': 'two'
15
+ }
16
+
17
+ assert_equal(expected, Skein::Support.symbolize_keys(hash))
18
+ end
19
+
20
+ def test_symbolize_keys_on_array
21
+ array = [
22
+ {
23
+ 'test' => :value,
24
+ 'nested' => {
25
+ 'hash' => true
26
+ }
27
+ },
28
+ {
29
+ 'second' => :hash
30
+ }
31
+ ]
32
+
33
+ expected = [
34
+ {
35
+ test: :value,
36
+ nested: {
37
+ hash: true
38
+ }
39
+ },
40
+ {
41
+ second: :hash
42
+ }
43
+ ]
44
+
45
+ assert_equal(expected, Skein::Support.symbolize_keys(array))
46
+ end
47
+
48
+ def test_symbolize_keys_on_non_hashes
49
+ assert_mapping(
50
+ 1 => 1,
51
+ true => true,
52
+ nil => nil,
53
+ 'test' => 'test',
54
+ :symbol => :symbol
55
+ ) do |value|
56
+ Skein::Support.symbolize_keys(value)
57
+ end
58
+ end
59
+
60
+ def test_hostname
61
+ hostname = Skein::Support.hostname
62
+
63
+ assert_equal(String, hostname.class)
64
+ assert(hostname.length > 0)
65
+ end
66
+
67
+ def test_process_name
68
+ process_name = Skein::Support.process_name
69
+
70
+ assert(process_name)
71
+
72
+ assert(%w[ test_skein_support rake_test_loader ].include?(process_name))
73
+ end
74
+
75
+ def test_pid
76
+ process_id = Skein::Support.process_id
77
+
78
+ assert(process_id)
79
+ assert(process_id.is_a?(Integer))
80
+ end
81
+
82
+ def test_arrayify
83
+ assert_mapping(
84
+ [ :test ] => [ :test ],
85
+ :test => [ :test ],
86
+ true => [ true ],
87
+ 0 => [ 0 ],
88
+ [ 0 ] => [ 0 ],
89
+ [ nil ] => [ nil ],
90
+ nil => nil
91
+ ) do |value|
92
+ Skein::Support.arrayify(value)
93
+ end
94
+ end
95
+ end