batsir 0.1.0 → 0.3.7

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 (63) hide show
  1. data/.travis.yml +9 -1
  2. data/CHANGES.md +54 -0
  3. data/Gemfile +7 -10
  4. data/README.md +49 -5
  5. data/Rakefile +23 -16
  6. data/batsir.gemspec +43 -15
  7. data/lib/batsir/acceptors/acceptor.rb +31 -5
  8. data/lib/batsir/acceptors/amqp_acceptor.rb +36 -8
  9. data/lib/batsir/amqp.rb +35 -7
  10. data/lib/batsir/amqp_consumer.rb +8 -0
  11. data/lib/batsir/compiler/stage_worker_compiler.rb +86 -0
  12. data/lib/batsir/config.rb +208 -24
  13. data/lib/batsir/dsl/conditional_notifier_declaration.rb +31 -0
  14. data/lib/batsir/dsl/dsl_mappings.rb +27 -2
  15. data/lib/batsir/errors.rb +18 -0
  16. data/lib/batsir/filter.rb +5 -0
  17. data/lib/batsir/log.rb +7 -0
  18. data/lib/batsir/logger.rb +37 -0
  19. data/lib/batsir/logo.rb +3 -8
  20. data/lib/batsir/notifiers/amqp_notifier.rb +14 -4
  21. data/lib/batsir/notifiers/conditional_notifier.rb +29 -0
  22. data/lib/batsir/notifiers/notifier.rb +6 -5
  23. data/lib/batsir/registry.rb +6 -2
  24. data/lib/batsir/stage.rb +9 -4
  25. data/lib/batsir/stage_worker.rb +3 -56
  26. data/lib/batsir/strategies/retry_strategy.rb +35 -0
  27. data/lib/batsir/strategies/strategy.rb +20 -0
  28. data/lib/batsir/transformers/field_transformer.rb +2 -3
  29. data/lib/batsir/transformers/json_input_transformer.rb +6 -2
  30. data/lib/batsir/transformers/json_output_transformer.rb +6 -2
  31. data/lib/batsir/transformers/transformer.rb +5 -1
  32. data/lib/batsir/version.rb +10 -0
  33. data/lib/batsir.rb +31 -13
  34. data/spec/batsir/acceptors/acceptor_spec.rb +7 -78
  35. data/spec/batsir/acceptors/amqp_acceptor_spec.rb +55 -66
  36. data/spec/batsir/acceptors/shared_examples.rb +102 -0
  37. data/spec/batsir/amqp_spec.rb +58 -0
  38. data/spec/batsir/chain_spec.rb +4 -4
  39. data/spec/batsir/config_spec.rb +97 -0
  40. data/spec/batsir/dsl/chain_mapping_spec.rb +5 -6
  41. data/spec/batsir/dsl/conditional_notifier_mapping_spec.rb +80 -0
  42. data/spec/batsir/dsl/stage_mapping_spec.rb +38 -20
  43. data/spec/batsir/filter_queue_spec.rb +9 -15
  44. data/spec/batsir/filter_spec.rb +4 -5
  45. data/spec/batsir/log_spec.rb +10 -0
  46. data/spec/batsir/logger_spec.rb +46 -0
  47. data/spec/batsir/notifiers/amqp_notifier_spec.rb +43 -22
  48. data/spec/batsir/notifiers/conditional_notifier_spec.rb +62 -0
  49. data/spec/batsir/notifiers/notifier_spec.rb +4 -66
  50. data/spec/batsir/notifiers/shared_examples.rb +100 -0
  51. data/spec/batsir/registry_spec.rb +48 -0
  52. data/spec/batsir/stage_spec.rb +91 -85
  53. data/spec/batsir/stage_worker_spec.rb +13 -13
  54. data/spec/batsir/strategies/retry_strategy_spec.rb +58 -0
  55. data/spec/batsir/strategies/strategy_spec.rb +28 -0
  56. data/spec/batsir/support/bunny_mocks.rb +78 -5
  57. data/spec/batsir/transformers/field_transformer_spec.rb +22 -22
  58. data/spec/batsir/transformers/json_input_transformer_spec.rb +8 -3
  59. data/spec/batsir/transformers/json_output_transformer_spec.rb +2 -2
  60. data/spec/batsir/transformers/transformer_spec.rb +18 -4
  61. data/spec/spec_helper.rb +6 -2
  62. metadata +78 -23
  63. data/VERSION +0 -1
@@ -1,51 +1,58 @@
1
1
  require File.join( File.dirname(__FILE__), "..", "..", "spec_helper" )
2
+ require File.join( File.dirname(__FILE__), 'shared_examples')
2
3
 
3
4
  describe Batsir::Notifiers::AMQPNotifier do
4
5
  let(:notifier_class){
5
6
  Batsir::Notifiers::AMQPNotifier
6
- }
7
+ }
7
8
 
8
- context "With respect to setting options" do
9
- it "should be a Batsir::Notifiers::Notifier" do
9
+ def new_notifier(options = {})
10
+ notifier_class.new(options)
11
+ end
12
+
13
+ it_should_behave_like "a notifier", Batsir::Notifiers::AMQPNotifier
14
+
15
+ context "with respect to setting options" do
16
+ it "is a Batsir::Notifiers::Notifier" do
10
17
  notifier_class.ancestors.should include Batsir::Notifiers::Notifier
11
18
  end
12
19
 
13
- it "should be possible to set the queue on which to listen" do
20
+ it "can set the queue on which to listen" do
14
21
  notifier = notifier_class.new(:queue => :queue)
15
22
  notifier.queue.should == :queue
16
23
  end
17
24
 
18
- it "should be possible to set the host of the amqp broker" do
25
+ it "can set the host of the amqp broker" do
19
26
  notifier = notifier_class.new(:host => 'localhost')
20
27
  notifier.host.should == 'localhost'
21
28
  end
22
29
 
23
- it "should be possible to set the port of the amqp broker" do
30
+ it "can set the port of the amqp broker" do
24
31
  notifier = notifier_class.new(:port => 1234)
25
32
  notifier.port.should == 1234
26
33
  end
27
34
 
28
- it "should be possible to set the username with which to connect to the broker" do
35
+ it "can set the username with which to connect to the broker" do
29
36
  notifier = notifier_class.new(:username => 'some_user')
30
37
  notifier.username.should == 'some_user'
31
38
  end
32
39
 
33
- it "should be possible to set the password with which to connect to the broker" do
40
+ it "can set the password with which to connect to the broker" do
34
41
  notifier = notifier_class.new(:password => 'password')
35
42
  notifier.password.should == 'password'
36
43
  end
37
44
 
38
- it "should be possible to set the vhost to use on the broker" do
45
+ it "can set the vhost to use on the broker" do
39
46
  notifier = notifier_class.new(:vhost => '/blah')
40
47
  notifier.vhost.should == '/blah'
41
48
  end
42
49
 
43
- it "should be possible to set the exchange to use on the broker" do
50
+ it "can set the exchange to use on the broker" do
44
51
  notifier = notifier_class.new(:exchange => 'amq.direct')
45
52
  notifier.exchange.should == 'amq.direct'
46
53
  end
47
54
 
48
- it "should default to amqp://guest:guest@localhost:5672/ with direct exchange on vhost ''" do
55
+ it "defaults to amqp://guest:guest@localhost:5672/ with direct exchange on vhost ''" do
49
56
  notifier = notifier_class.new(:queue => :somequeue)
50
57
  notifier.queue.should == :somequeue
51
58
  notifier.host.should == 'localhost'
@@ -57,54 +64,62 @@ describe Batsir::Notifiers::AMQPNotifier do
57
64
  end
58
65
  end
59
66
 
60
- context "With respect to notifying" do
61
- def new_notifier(options = {})
62
- notifier_class.new(options)
67
+ context "with respect to notifying" do
68
+ before :each do
69
+ Batsir::Registry.reset
70
+ end
71
+
72
+ it 'has an #execute method' do
73
+ notifier_class.instance_methods.map{|m| m.to_s}.should include "execute"
63
74
  end
64
75
 
65
- it "should connect to the configured host" do
76
+ it 'has a #handle_error method' do
77
+ notifier_class.instance_methods.map{|m| m.to_s}.should include "handle_error"
78
+ end
79
+
80
+ it "connects to the configured host" do
66
81
  notifier = new_notifier(:host => 'somehost')
67
82
  notifier.execute({})
68
83
  instance = Bunny.instance
69
84
  instance.options[:host].should == 'somehost'
70
85
  end
71
86
 
72
- it "should connect to the configured port" do
87
+ it "connects to the configured port" do
73
88
  notifier = new_notifier(:port => 1234)
74
89
  notifier.execute({})
75
90
  instance = Bunny.instance
76
91
  instance.options[:port].should == 1234
77
92
  end
78
93
 
79
- it "should connect with the configured username" do
94
+ it "connects with the configured username" do
80
95
  notifier = new_notifier(:username => 'user')
81
96
  notifier.execute({})
82
97
  instance = Bunny.instance
83
- instance.options[:user].should == 'user'
98
+ instance.user.should == 'user'
84
99
  end
85
100
 
86
- it "should connect with the configured password" do
101
+ it "connects with the configured password" do
87
102
  notifier = new_notifier(:password => 'pass')
88
103
  notifier.execute({})
89
104
  instance = Bunny.instance
90
105
  instance.options[:pass].should == 'pass'
91
106
  end
92
107
 
93
- it "should connect to the configured vhost" do
108
+ it "connects to the configured vhost" do
94
109
  notifier = new_notifier(:vhost => '/vhost')
95
110
  notifier.execute({})
96
111
  instance = Bunny.instance
97
112
  instance.options[:vhost].should == '/vhost'
98
113
  end
99
114
 
100
- it "should declare the configured exchange" do
115
+ it "declares the configured exchange" do
101
116
  notifier = new_notifier(:exchange => 'some_exchange')
102
117
  notifier.execute({})
103
118
  instance = Bunny.instance
104
119
  instance.exchange.name.should == 'some_exchange'
105
120
  end
106
121
 
107
- it "should bind the configured exchange to the queue and publish the message with the queue set as routing key" do
122
+ it "binds the configured exchange to the queue and publish the message with the queue set as routing key" do
108
123
  notifier = new_notifier(:exchange => 'some_exchange', :queue => :queue)
109
124
  notifier.execute({})
110
125
  instance = Bunny.instance
@@ -112,6 +127,12 @@ describe Batsir::Notifiers::AMQPNotifier do
112
127
  instance.exchange.message.should == {}
113
128
  instance.exchange.key.should == :queue
114
129
  end
130
+ end
115
131
 
132
+ context "with respect to error handling" do
133
+ it 'uses a strategy object to resolve notification errors' do
134
+ notifier = new_notifier(:exchange => 'some_exchange', :queue => :queue)
135
+ notifier.error_strategy.should be_a Batsir::Strategies::RetryStrategy
136
+ end
116
137
  end
117
138
  end
@@ -0,0 +1,62 @@
1
+ require File.join( File.dirname(__FILE__), "..", "..", "spec_helper" )
2
+ require File.join( File.dirname(__FILE__), 'shared_examples')
3
+
4
+ describe Batsir::Notifiers::ConditionalNotifier do
5
+
6
+ it_should_behave_like "a notifier", Batsir::Notifiers::ConditionalNotifier
7
+
8
+ true_block = ::Proc.new { |message| true }
9
+
10
+ let(:notifier){ Batsir::Notifiers::Notifier }
11
+
12
+ it "can add notifiers" do
13
+ subject.notifiers.size.should == 0
14
+ subject.add_notifier( true_block, notifier)
15
+ subject.notifiers.size.should == 1
16
+ end
17
+
18
+ it "stores notifier conditions" do
19
+ subject.add_notifier(true_block, notifier)
20
+
21
+ notifier_condition = subject.notifiers.first
22
+ notifier_condition.should be_a Batsir::Notifiers::ConditionalNotifier::NotifierCondition
23
+ notifier_condition.condition.should == true_block
24
+ notifier_condition.notifier.should == notifier
25
+ end
26
+
27
+ it "stores optional options for the notifier" do
28
+ subject.add_notifier( true_block, notifier, :some => :extra, :options => :foryou)
29
+
30
+ notifier_condition = subject.notifiers.first
31
+ notifier_condition.options.size.should == 2
32
+ notifier_condition.options.should have_key :some
33
+ notifier_condition.options.should have_key :options
34
+ end
35
+
36
+ context "sending messages" do
37
+ class FakeNotifier < Batsir::Notifiers::Notifier; end
38
+
39
+ it "executes notifiers if condition is true" do
40
+
41
+ notifier_class = FakeNotifier
42
+ notifier_class.any_instance.stub(:execute)
43
+ notifier_class.any_instance.should_receive(:execute)
44
+
45
+ conditional = Batsir::Notifiers::ConditionalNotifier.new
46
+ conditional.add_notifier( true_block, notifier_class.new({}) )
47
+ conditional.execute("some message")
48
+ end
49
+
50
+ it "does not execute when condition is false" do
51
+ false_block = lambda {|message| false}
52
+
53
+ notifier_class = FakeNotifier
54
+ notifier_class.any_instance.stub(:execute)
55
+ notifier_class.any_instance.should_not_receive(:execute)
56
+
57
+ conditional = Batsir::Notifiers::ConditionalNotifier.new
58
+ conditional.add_notifier( false_block, notifier_class.new({}) )
59
+ conditional.execute("some message")
60
+ end
61
+ end
62
+ end
@@ -1,73 +1,11 @@
1
1
  require File.join( File.dirname(__FILE__), "..", "..", "spec_helper" )
2
+ require File.join( File.dirname(__FILE__), 'shared_examples')
2
3
 
3
4
  describe Batsir::Notifiers::Notifier do
4
- let( :notifier_class ) do
5
- Batsir::Notifiers::Notifier
6
- end
7
-
8
- it "should have a transformer_queue" do
9
- notifier = notifier_class.new
10
- notifier.transformer_queue.should_not be_nil
11
- end
12
-
13
- it "should initially have an empty transformer_queue" do
14
- notifier = notifier_class.new
15
- notifier.transformer_queue.should_not be_nil
16
- notifier.transformer_queue.should be_empty
17
- end
18
-
19
- it "should be possible to add a transformer to the transformer_queue" do
20
- transformer = :transformer
21
-
22
- notifier = notifier_class.new
23
- notifier.add_transformer transformer
24
-
25
- notifier.transformer_queue.should_not be_empty
26
- notifier.transformer_queue.size.should == 1
27
- notifier.transformer_queue.first.should == :transformer
28
- end
29
-
30
- it "should be possible to add a transformer multiple times" do
31
- transformer = :transformer
32
5
 
33
- notifier = notifier_class.new
34
- notifier.add_transformer transformer
35
- notifier.add_transformer transformer
36
-
37
- notifier.transformer_queue.should_not be_empty
38
- notifier.transformer_queue.size.should == 2
39
- end
40
-
41
- it "should create a FieldTransformer when the 'fields' option is given during initialization" do
42
- fields = {:foo => :bar}
43
- notifier = notifier_class.new(:fields => fields)
44
- notifier.transformer_queue.should_not be_empty
45
- notifier.transformer_queue.first.class.should == Batsir::Transformers::FieldTransformer
46
- notifier.transformer_queue.first.fields.should == fields
47
- end
48
-
49
- it "should call #transform when #notify is called" do
50
- notifier = notifier_class.new
51
- notifier.should_receive(:transform).with({})
52
- notifier.notify({})
53
- end
54
-
55
- it "should call #transform on all transformers when #transform is called" do
56
- class MockTransformer < Batsir::Transformers::Transformer
57
- end
58
-
59
- notifier = notifier_class.new
60
- transformer = MockTransformer.new
61
- notifier.add_transformer transformer
62
- notifier.transformer_queue.size.should == 1
63
-
64
- transformer.should_receive(:transform).with({})
65
- notifier.notify({})
66
- end
6
+ it_should_behave_like "a notifier", Batsir::Notifiers::Notifier
67
7
 
68
- it "should call #execute when #notify is called" do
69
- notifier = notifier_class.new
70
- notifier.should_receive(:execute)
71
- notifier.notify({})
8
+ it 'raises an error when the #execute method is not implemented' do
9
+ lambda{ subject.notify({})}.should raise_error NotImplementedError
72
10
  end
73
11
  end
@@ -0,0 +1,100 @@
1
+ shared_examples_for "a notifier" do |notifier_class|
2
+ before :each do
3
+ subject{notifier_class.new}
4
+ end
5
+
6
+ context 'transformers' do
7
+ it "has a transformer_queue" do
8
+ subject.transformer_queue.should_not be_nil
9
+ end
10
+
11
+ it "initially has an empty transformer_queue" do
12
+ subject.transformer_queue.should_not be_nil
13
+ subject.transformer_queue.should be_empty
14
+ end
15
+
16
+ it "can add a transformer to the transformer_queue" do
17
+ transformer = :transformer
18
+
19
+ subject.add_transformer transformer
20
+
21
+ subject.transformer_queue.should_not be_empty
22
+ subject.transformer_queue.size.should == 1
23
+ subject.transformer_queue.first.should == :transformer
24
+ end
25
+
26
+ it "can add a transformer multiple times" do
27
+ transformer = :transformer
28
+
29
+ subject.add_transformer transformer
30
+ subject.add_transformer transformer
31
+
32
+ subject.transformer_queue.should_not be_empty
33
+ subject.transformer_queue.size.should == 2
34
+ end
35
+
36
+ it "creates a FieldTransformer when the 'fields' option is given during initialization" do
37
+ fields = {:foo => :bar}
38
+ subject = notifier_class.new(:fields => fields)
39
+ subject.transformer_queue.should_not be_empty
40
+ subject.transformer_queue.first.class.should == Batsir::Transformers::FieldTransformer
41
+ subject.transformer_queue.first.fields.should == fields
42
+ end
43
+ end
44
+
45
+ context 'methods calls' do
46
+ it "has an #execute method" do
47
+ notifier_class.instance_methods.map{|m| m.to_s}.should include "execute"
48
+ end
49
+
50
+
51
+ it "calls #transform when #notify is called" do
52
+ subject.stub(:execute)
53
+ subject.should_receive(:transform).with({})
54
+ subject.notify({})
55
+ end
56
+
57
+ it "calls #transform on all transformers when #transform is called" do
58
+ class MockTransformer < Batsir::Transformers::Transformer
59
+ end
60
+
61
+ subject.stub(:execute)
62
+ transformer = MockTransformer.new
63
+ subject.add_transformer transformer
64
+ subject.transformer_queue.size.should == 1
65
+
66
+ transformer.should_receive(:transform).with({})
67
+ subject.notify({})
68
+ end
69
+
70
+ it "calls #execute when #notify is called" do
71
+ subject.stub(:execute)
72
+ subject.should_receive(:execute)
73
+ subject.notify({})
74
+ end
75
+ end
76
+
77
+ context 'message unmodified' do
78
+ it 'has no transformers' do
79
+ message = {'test_id' => 123}
80
+ subject.add_transformer(Batsir::Transformers::JSONOutputTransformer.new)
81
+ begin
82
+ subject.notify(message).should == {'test_id' => 123}
83
+ rescue NotImplementedError => e
84
+ end
85
+ message.should == {'test_id' => 123}
86
+ end
87
+
88
+ it 'has a FieldTransformer' do
89
+ fields = {:foo => 'bar', 'test_id' => 'test'}
90
+ message = {'test_id' => 123}
91
+ subject = notifier_class.new(:fields => fields)
92
+ subject.add_transformer(Batsir::Transformers::JSONOutputTransformer.new)
93
+ begin
94
+ subject.notify(message).should == {'test_id' => 123}
95
+ rescue NotImplementedError => e
96
+ end
97
+ message.should == {'test_id' => 123}
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,48 @@
1
+ require File.join( File.dirname(__FILE__), "..", "spec_helper" )
2
+
3
+ describe Batsir::Registry do
4
+
5
+ before :all do
6
+ @class = Batsir::Registry
7
+ end
8
+
9
+ before :each do
10
+ @class.reset
11
+ end
12
+
13
+ it "outputs the whole registry" do
14
+ @class.registry.should == {}
15
+ end
16
+
17
+ it "registers a value" do
18
+ @class.register(:foo, :bar)
19
+ @class.registry.keys.size.should == 1
20
+ @class.registry.keys.should include :foo
21
+ @class.registry.values.size.should == 1
22
+ @class.registry.values.should include :bar
23
+ end
24
+
25
+ it "is able to retrieve a registered variable" do
26
+ @class.register('test', 'value')
27
+ @class.get('test').should == 'value'
28
+ end
29
+
30
+ it "returns nil when the requested key is not found" do
31
+ @class.get('foobar').should be_nil
32
+ end
33
+
34
+ context "resetting" do
35
+ it "is possible" do
36
+ @class.register('foo', 'bar')
37
+ @class.registry.should == {'foo' => 'bar'}
38
+ @class.reset
39
+ @class.registry.should == {}
40
+ end
41
+
42
+ it "returns its new state" do
43
+ @class.register('foo', 'bar')
44
+ result = @class.reset
45
+ result.should == {}
46
+ end
47
+ end
48
+ end