observed 0.1.1 → 0.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +9 -9
  2. data/.travis.yml +4 -0
  3. data/README.md +53 -78
  4. data/examples/observed.rb +1 -1
  5. data/exe/observed-oneshot +3 -1
  6. data/features/explicit_routing.feature +33 -0
  7. data/features/oneshot.feature +4 -0
  8. data/features/test_in_single_ruby_source.feature +4 -0
  9. data/integrations/observed-clockwork/features/run_observed_inside_clockwork.feature +6 -7
  10. data/integrations/observed-clockwork/lib/observed/clockwork/version.rb +1 -1
  11. data/integrations/observed-clockwork/lib/observed/clockwork.rb +0 -1
  12. data/integrations/observed-clockwork/observed-clockwork.gemspec +1 -1
  13. data/integrations/observed-eventmachine/.gitignore +17 -0
  14. data/integrations/observed-eventmachine/Gemfile +8 -0
  15. data/integrations/observed-eventmachine/LICENSE.txt +22 -0
  16. data/integrations/observed-eventmachine/README.md +29 -0
  17. data/integrations/observed-eventmachine/Rakefile +1 -0
  18. data/integrations/observed-eventmachine/examples/observed.rb +30 -0
  19. data/integrations/observed-eventmachine/features/integration_via_single_ruby_source.feature +48 -0
  20. data/integrations/observed-eventmachine/features/support/env.rb +8 -0
  21. data/integrations/observed-eventmachine/lib/observed/eventmachine/version.rb +5 -0
  22. data/integrations/observed-eventmachine/lib/observed/eventmachine.rb +70 -0
  23. data/integrations/observed-eventmachine/observed-eventmachine.gemspec +28 -0
  24. data/lib/observed/application/oneshot.rb +14 -37
  25. data/lib/observed/builtin_plugins/file.rb +5 -14
  26. data/lib/observed/builtin_plugins/stdout.rb +7 -14
  27. data/lib/observed/config.rb +4 -4
  28. data/lib/observed/config_builder.rb +154 -87
  29. data/lib/observed/config_dsl.rb +2 -8
  30. data/lib/observed/configurable.rb +61 -3
  31. data/lib/observed/context.rb +90 -0
  32. data/lib/observed/default/observer.rb +2 -5
  33. data/lib/observed/default.rb +0 -1
  34. data/lib/observed/event_bus.rb +31 -0
  35. data/lib/observed/execution_job_factory.rb +95 -0
  36. data/lib/observed/job.rb +163 -0
  37. data/lib/observed/jobbed_event_bus.rb +33 -0
  38. data/lib/observed/logging.rb +40 -0
  39. data/lib/observed/observer.rb +1 -0
  40. data/lib/observed/observer_helpers/timer.rb +13 -5
  41. data/lib/observed/pluggable.rb +11 -0
  42. data/lib/observed/reporter/regexp_matching.rb +2 -1
  43. data/lib/observed/reporter/report_formatting.rb +57 -0
  44. data/lib/observed/reporter.rb +0 -2
  45. data/lib/observed/system.rb +11 -78
  46. data/lib/observed/translator.rb +22 -0
  47. data/lib/observed/version.rb +1 -1
  48. data/lib/observed.rb +10 -12
  49. data/omnibus-observed/.gitignore +9 -0
  50. data/omnibus-observed/Berksfile +3 -0
  51. data/omnibus-observed/Berksfile.lock +52 -0
  52. data/omnibus-observed/Gemfile +4 -0
  53. data/omnibus-observed/README.md +102 -0
  54. data/omnibus-observed/Vagrantfile +93 -0
  55. data/omnibus-observed/config/projects/observed.rb +20 -0
  56. data/omnibus-observed/config/software/observed.rb +19 -0
  57. data/omnibus-observed/package-scripts/observed/makeselfinst +27 -0
  58. data/omnibus-observed/package-scripts/observed/postinst +17 -0
  59. data/omnibus-observed/package-scripts/observed/postrm +9 -0
  60. data/plugins/observed-fluentd/lib/observed/fluentd/version.rb +1 -1
  61. data/plugins/observed-gauge/README.md +5 -0
  62. data/plugins/observed-gauge/lib/observed/gauge/version.rb +1 -1
  63. data/plugins/observed-gauge/lib/observed/gauge.rb +11 -13
  64. data/plugins/observed-gauge/observed-gauge.gemspec +1 -1
  65. data/plugins/observed-gauge/spec/gauge_spec.rb +7 -7
  66. data/plugins/observed-growl/Gemfile +6 -0
  67. data/plugins/observed-growl/lib/observed/growl.rb +80 -0
  68. data/plugins/observed-http/lib/observed/http/version.rb +1 -1
  69. data/plugins/observed-http/lib/observed/http.rb +10 -8
  70. data/plugins/observed-http/observed-http.gemspec +1 -1
  71. data/plugins/observed-http/spec/http_spec.rb +62 -7
  72. data/plugins/observed-http/spec/integration_spec.rb +14 -0
  73. data/plugins/observed-shell/Gemfile +5 -0
  74. data/plugins/observed-shell/lib/observed/shell.rb +54 -0
  75. data/run-integration-tests +81 -0
  76. data/spec/builtin_plugins/stdout_spec.rb +7 -3
  77. data/spec/config_builder_spec.rb +42 -59
  78. data/spec/config_dsl_spec.rb +4 -0
  79. data/spec/configurable_spec.rb +141 -31
  80. data/spec/event_bus_spec.rb +16 -0
  81. data/spec/execution_job_factory_spec.rb +35 -0
  82. data/spec/job_factory_spec.rb +16 -0
  83. data/spec/job_spec.rb +228 -0
  84. data/spec/jobbed_event_bus_spec.rb +38 -0
  85. data/spec/observed_spec.rb +203 -0
  86. data/spec/observer_helpers/timer_spec.rb +187 -0
  87. data/spec/oneshot_spec.rb +7 -2
  88. data/spec/system_spec.rb +5 -39
  89. metadata +55 -12
  90. data/lib/observed/default/reporter.rb +0 -17
  91. data/lib/observed/reader.rb +0 -14
  92. data/lib/observed/writer.rb +0 -14
  93. data/spec/reader_spec.rb +0 -15
  94. data/spec/writer_spec.rb +0 -16
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'observed/configurable'
3
+ require 'observed/pluggable'
3
4
 
4
5
  module ConfigurableSpec
5
6
  class Foo
@@ -11,55 +12,164 @@ module ConfigurableSpec
11
12
 
12
13
  default :bar => 345
13
14
  end
15
+
16
+ module ConfigurableModule
17
+ include Observed::Configurable
18
+
19
+ attribute :baz
20
+ end
21
+
22
+ module IntermediateModule
23
+ include Observed::Configurable
24
+ include ConfigurableModule
25
+
26
+ attribute :bar, default: 234
27
+ end
28
+
29
+ class ConfigurableModuleIncluder
30
+ include Observed::Configurable
31
+ include ConfigurableModule
32
+
33
+ attribute :bar, default: 234
34
+ default :bar => 345
35
+ attribute :foo, default: 123
36
+ end
37
+
38
+ class IntermediateModuleIncluder
39
+ include Observed::Configurable
40
+ include IntermediateModule
41
+
42
+ default :bar => 345
43
+ attribute :foo, default: 123
44
+ end
45
+
46
+ class SpecialCMI < ConfigurableModuleIncluder
47
+ include Observed::Configurable
48
+ end
49
+
50
+ class SpecialIMI < IntermediateModuleIncluder
51
+ include Observed::Configurable
52
+ end
53
+
54
+ class Plugin
55
+ include Observed::Configurable
56
+ include Observed::Pluggable
57
+ include ConfigurableModule
58
+ attribute :bar, default: 234
59
+ default :bar => 345
60
+ end
61
+
62
+ class PluginImpl < Plugin
63
+ include Observed::Configurable
64
+ attribute :foo, default: 123
65
+ end
66
+
67
+ class Overriding
68
+ include Observed::Configurable
69
+ include IntermediateModule
70
+
71
+ def foo
72
+ @attributes[:foo] || 123
73
+ end
74
+
75
+ default :bar => 345
76
+ attribute :foo, default: 1234
77
+ end
14
78
  end
15
79
 
16
80
  describe Observed::Configurable do
17
81
 
18
- context 'without parameters for the constructor' do
19
- subject {
20
- ConfigurableSpec::Foo.new
21
- }
82
+ shared_examples_for 'a configurable object' do
22
83
  it 'uses default values for attributes' do
23
- expect(subject.foo).to eq(123)
84
+ expect(subject.new.foo).to eq(123)
24
85
  end
86
+
25
87
  it 'overrides default values on `attribute name, :default => default_value`' do
26
- expect(subject.bar).to eq(345)
88
+ expect(subject.new.bar).to eq(345)
27
89
  end
90
+
28
91
  it 'raises errors when attributes without values are read' do
29
- expect { subject.baz }.to raise_error
92
+ expect { subject.new.baz }.to raise_error
93
+ end
94
+
95
+ context 'when configured via the `configure` method' do
96
+ it 'prefers arguments of the method over defaults' do
97
+ instance = subject.new
98
+
99
+ instance.configure foo: 1, bar: 2
100
+
101
+ expect(instance.foo).to eq(1)
102
+ expect(instance.bar).to eq(2)
103
+ end
104
+ end
105
+
106
+ context 'when configured via constructor parameters' do
107
+ context 'when the keys are symbols' do
108
+ it 'prefers values from constructor parameters over defaults' do
109
+ instance = subject.new({foo: 1, bar: 2, baz: 3})
110
+ expect(instance.foo).to eq(1)
111
+ expect(instance.bar).to eq(2)
112
+ expect(instance.baz).to eq(3)
113
+ end
114
+ end
115
+ context 'when the keys are strings' do
116
+ it 'does not prefer constructor parameters' do
117
+ instance = subject.new({'foo' => 1, 'bar' => 2, 'baz' => 3})
118
+ expect(instance.foo).to eq(123)
119
+ expect(instance.bar).to eq(345)
120
+ expect { instance.baz }.to raise_error
121
+ end
122
+ end
30
123
  end
31
124
  end
32
125
 
33
- context 'with parameters for the constructor' do
126
+ context 'when included in a class' do
34
127
  subject {
35
- ConfigurableSpec::Foo.new({foo: 1, bar: 2, baz: 3})
128
+ ConfigurableSpec::Foo
36
129
  }
37
- it 'prefers values from constructor parameters over defaults' do
38
- expect(subject.foo).to eq(1)
39
- expect(subject.bar).to eq(2)
40
- expect(subject.baz).to eq(3)
41
- end
130
+ it_behaves_like 'a configurable object'
42
131
  end
43
132
 
44
- context 'configured through `configure(args)` method' do
133
+ context 'when included in a module' do
45
134
  subject {
46
- foo = ConfigurableSpec::Foo.new
47
- foo.configure(args)
48
- foo
135
+ ConfigurableSpec::ConfigurableModuleIncluder
49
136
  }
50
- shared_examples_for 'values are set' do
51
- it 'prefers values from `configure(args)` over defaults' do
52
- expect(subject.foo).to eq(1)
53
- expect(subject.bar).to eq(2)
54
- expect(subject.baz).to eq(3)
55
- end
56
- end
57
- context 'when args has symbol keys' do
58
- let(:args) {
59
- {foo: 1, bar: 2, baz: 3}
60
- }
61
- it_behaves_like 'values are set'
62
- end
137
+ it_behaves_like 'a configurable object'
138
+ end
139
+
140
+ context 'when included via a intermediate module' do
141
+ subject {
142
+ ConfigurableSpec::IntermediateModuleIncluder
143
+ }
144
+ it_behaves_like 'a configurable object'
145
+ end
146
+
147
+ context 'when extended from a class which is a Plugin' do
148
+ subject {
149
+ ConfigurableSpec::PluginImpl
150
+ }
151
+ it_behaves_like 'a configurable object'
152
+ end
153
+
154
+ context 'when inherited from a class included the intermediate module' do
155
+ subject {
156
+ ConfigurableSpec::SpecialIMI
157
+ }
158
+ it_behaves_like 'a configurable object'
159
+ end
160
+
161
+ context 'when inherited from a class included the module includes it' do
162
+ subject {
163
+ ConfigurableSpec::SpecialCMI
164
+ }
165
+ it_behaves_like 'a configurable object'
166
+ end
167
+
168
+ context 'when there is a method named exactly same as the attribute' do
169
+ subject {
170
+ ConfigurableSpec::Overriding
171
+ }
172
+ it_behaves_like 'a configurable object'
63
173
  end
64
174
 
65
175
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'observed/event_bus'
3
+
4
+ describe Observed::EventBus do
5
+ it 'calls the handler for the emitted event' do
6
+ handler_one_called = false
7
+ handler_two_called = false
8
+ bus = Observed::EventBus.new
9
+ expect { bus.emit('foo') }.to_not raise_error
10
+ expect { bus.on_receive(/^bar$/) { handler_one_called = true } }.to_not raise_error
11
+ expect { bus.on_receive(/^baz$/) { handler_two_called = true } }.to_not raise_error
12
+ expect { bus.emit('bar') }.to_not raise_error
13
+ expect(handler_one_called).to be_true
14
+ expect(handler_two_called).to be_false
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'observed/execution_job_factory'
3
+
4
+ describe Observed::ExecutionJobFactory do
5
+ subject {
6
+ Observed::ExecutionJobFactory.new
7
+ }
8
+ it 'should convert observers, translators, reporters to jobs' do
9
+ output = mock('output')
10
+
11
+ the_observer = Class.new(Observed::Observer) do
12
+ def observe(data)
13
+ data.merge(b:2)
14
+ end
15
+ end.new
16
+ the_reporter = Class.new(Observed::Reporter) do
17
+ attribute :output
18
+ def report(tag, time, data)
19
+ output.write(tag: tag, time: time, data: data)
20
+ end
21
+ end.new(output: output)
22
+ the_translator = Class.new(Observed::Translator) do
23
+ def translate(tag, time, data)
24
+ data.merge(c:3)
25
+ end
26
+ end.new
27
+ job = subject.convert_to_job(the_observer)
28
+ .then(subject.convert_to_job(the_translator))
29
+ .then(subject.convert_to_job(the_reporter))
30
+ tag = 'the_tag'
31
+ time = Time.now
32
+ output.expects(:write).with(tag: tag, time: time, data: {a:1,b:2,c:3})
33
+ job.now({a:1}, {tag: tag, time: time})
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'observed/job'
3
+
4
+ describe Observed::JobFactory do
5
+ context 'when the executor not given' do
6
+ it 'fails to initialize' do
7
+ expect { Observed::JobFactory.new() }.to raise_error
8
+ end
9
+ end
10
+
11
+ context 'when a logger given' do
12
+ it 'may prefer the given logger over the default one' do
13
+ Observed::JobFactory.new(executor: mock('executor'), logger: ::Logger.new(STDERR))
14
+ end
15
+ end
16
+ end
data/spec/job_spec.rb ADDED
@@ -0,0 +1,228 @@
1
+ require 'spec_helper'
2
+ require 'observed/job'
3
+
4
+ describe Observed::MutableJob do
5
+ let(:factory) {
6
+ Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
7
+ }
8
+
9
+ it 'yields the given block' do
10
+ yielded = nil
11
+ job = factory.mutable_job { |data|
12
+ data
13
+ }
14
+ job.now({a:1}, {b:2}) do |data, options|
15
+ yielded = [data, options]
16
+ end
17
+ expect(yielded).to eq([{a:1}, {b:2}])
18
+ end
19
+
20
+ it 'executes the job regardless of whether or not a block is given' do
21
+ executed = nil
22
+ job = factory.mutable_job { |data, options|
23
+ executed = [data, options]
24
+ data
25
+ }
26
+ job.now({a:1}, {b:2})
27
+ expect(executed).to eq([{a:1}, {b:2}])
28
+ end
29
+ end
30
+
31
+ describe Observed::ParallelJob do
32
+ let(:factory) {
33
+ Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
34
+ }
35
+
36
+ it 'yields the given block' do
37
+ job1 = factory.job { |data, |
38
+ data.merge(c:3)
39
+ }
40
+ job2 = factory.job { |data|
41
+ data.merge(d:4)
42
+ }
43
+ par = Observed::ParallelJob.new([job1, job2])
44
+ yielded = []
45
+ par.now({a:1}, {b:2}) do |data, options|
46
+ yielded.push([data, options])
47
+ end
48
+ expect(yielded).to eq([[{a:1,c:3},{b:2}], [{a:1,d:4},{b:2}]])
49
+ end
50
+
51
+ it 'executes the job regardless of whether or not a block is given' do
52
+ executed = []
53
+ job1 = factory.job { |data, options|
54
+ r = data.merge(c:3)
55
+ executed.push([r, options])
56
+ r
57
+ }
58
+ job2 = factory.job { |data, options|
59
+ r = data.merge(d:4)
60
+ executed.push([r, options])
61
+ r
62
+ }
63
+ par = Observed::ParallelJob.new([job1, job2])
64
+ par.now({a:1}, {b:2})
65
+ expect(executed).to eq([[{a:1,c:3},{b:2}], [{a:1,d:4},{b:2}]])
66
+ end
67
+ end
68
+
69
+ describe Observed::SequenceJob do
70
+ let(:factory) {
71
+ Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
72
+ }
73
+
74
+ it 'yields the given block' do
75
+ job1 = factory.job { |data|
76
+ data.merge(c:3)
77
+ }
78
+ job2 = factory.job { |data|
79
+ data.merge(d:4)
80
+ }
81
+ seq = Observed::SequenceJob.new(job1, job2)
82
+ yielded = []
83
+ seq.now({a:1}, {b:2}) do |data, options|
84
+ yielded.push([data, options])
85
+ end
86
+ expect(yielded).to eq([[{a:1,c:3,d:4},{b:2}]])
87
+ end
88
+
89
+ it 'executes the job regardless of whether or not a block is given' do
90
+ executed = []
91
+ job1 = factory.job { |data, options|
92
+ r = data.merge(c:3)
93
+ executed.push([r, options])
94
+ r
95
+ }
96
+ job2 = factory.job { |data, options|
97
+ r = data.merge(d:4)
98
+ executed.push([r, options])
99
+ r
100
+ }
101
+ seq = Observed::SequenceJob.new(job1, job2)
102
+ seq.now({a:1}, {b:2})
103
+ expect(executed).to eq([[{a:1,c:3},{b:2}], [{a:1,c:3,d:4},{b:2}]])
104
+ end
105
+ end
106
+
107
+ describe Observed::Job do
108
+ context 'in simple use cases' do
109
+ let(:factory) {
110
+ Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
111
+ }
112
+ context 'when the options as input are given' do
113
+ it 'propagates the options from the input' do
114
+ job1 = factory.job { |data, options|
115
+ expect(options).to eq({b:2})
116
+ data
117
+ }
118
+ job2 = factory.job { |_, options|
119
+ expect(options).to eq({b:2})
120
+ }
121
+ seq = job1.then(job2)
122
+ seq.now({a:1}, {b:2})
123
+ end
124
+ it 'allows to override the options from the input in subsequent jobs' do
125
+ job1 = factory.job { |data, options|
126
+ expect(options).to eq({b:2})
127
+ [data, {b:3}]
128
+ }
129
+ job2 = factory.job { |_, options|
130
+ expect(options).to eq({b:3})
131
+ }
132
+ seq = job1.then(job2)
133
+ seq.now({a:1}, {b:2})
134
+ end
135
+ end
136
+ context 'when the options as input are not given' do
137
+ it 'provides nil in the block parameter and allows to override it in subsequent jobs' do
138
+ job1 = factory.job { |data, options|
139
+ expect(options).to be_nil
140
+ [data, {b:3}]
141
+ }
142
+ job2 = factory.job { |_, options|
143
+ expect(options).to eq({b:3})
144
+ }
145
+ seq = job1.then(job2)
146
+ seq.now({a:1})
147
+ end
148
+ end
149
+ end
150
+ context 'when used in an immutable way' do
151
+ it 'propagates the resulting data to next jobs' do
152
+ factory = Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
153
+ output = mock('output')
154
+ input_data = { input: 1 }
155
+ a = factory.job { |data|
156
+ data.merge(a: 2)
157
+ }
158
+ b = factory.job { |data, options|
159
+ data.merge(b: 3)
160
+ }
161
+ c = factory.job { |data, options|
162
+ output.write data.merge(c: 4)
163
+ }
164
+ d = factory.job { |data|
165
+ output.write data.merge(d: 5)
166
+ }
167
+ foo = a.then(b).then(c, d)
168
+ output.expects(:write).with({input:1,a:2,b:3,c:4})
169
+ output.expects(:write).with({input:1,a:2,b:3,d:5})
170
+ foo.now(input_data)
171
+ end
172
+ end
173
+
174
+ context 'when used in a mutable way' do
175
+ it 'propagates the resulting data to next jobs' do
176
+ factory = Observed::JobFactory.new(:executor => Observed::BlockingJobExecutor.new)
177
+ output = mock('output')
178
+ input_data = { input: 1 }
179
+ a = factory.mutable_job { |data|
180
+ data.merge(a: 2)
181
+ }
182
+ b = factory.job { |data, options|
183
+ data.merge(b: 3)
184
+ }
185
+ c = factory.job { |data, options|
186
+ output.write data.merge(c: 4)
187
+ }
188
+ d = factory.job { |data|
189
+ output.write data.merge(d: 5)
190
+ }
191
+ a.then(b).then(c, d)
192
+ output.expects(:write).with({input:1,a:2,b:3,c:4})
193
+ output.expects(:write).with({input:1,a:2,b:3,d:5})
194
+ a.now(input_data)
195
+ end
196
+ end
197
+
198
+ context 'when listeners given' do
199
+ it 'notifies listeners with resulting data' do
200
+
201
+ listener = mock('listener')
202
+ factory = Observed::JobFactory.new(
203
+ :executor => Observed::BlockingJobExecutor.new,
204
+ :listener => listener
205
+ )
206
+ output = mock('output')
207
+ input_data = { input: 1 }
208
+ a = factory.job { |data|
209
+ data.merge(a: 2)
210
+ }
211
+ b = factory.job { |data, options|
212
+ data.merge(b: 3)
213
+ }
214
+ c = factory.job { |data, options|
215
+ output.write data.merge(c: 4)
216
+ }
217
+ d = factory.job { |data|
218
+ output.write data.merge(d: 5)
219
+ }
220
+ foo = a.then(b).then(c, d)
221
+ output.expects(:write).with({input:1,a:2,b:3,c:4})
222
+ output.expects(:write).with({input:1,a:2,b:3,d:5})
223
+ listener.expects(:on_result).with({input:1,a:2}, {opt:1})
224
+ listener.expects(:on_result).with({input:1,a:2,b:3}, {opt:1})
225
+ foo.now(input_data, {opt:1})
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'observed/job'
4
+ require 'observed/jobbed_event_bus'
5
+
6
+ describe Observed::JobbedEventBus do
7
+ let(:out) {
8
+ mock('out')
9
+ }
10
+ let(:factory) {
11
+ executor = Observed::BlockingJobExecutor.new
12
+ Observed::JobFactory.new(executor: executor)
13
+ }
14
+ let(:the_job) {
15
+ factory.job { |data, options|
16
+ out.write data, options
17
+ }
18
+ }
19
+ let(:bus) {
20
+ Observed::JobbedEventBus.new(job_factory: factory)
21
+ }
22
+ it 'should invoke jobs when the corresponding events are emitted' do
23
+ bus.emit('foo')
24
+ bus.receive(/^bar$/).then(the_job)
25
+ bus.emit('baz')
26
+ out.expects(:write).with({a:1}, {b:2})
27
+ bus.emit('bar', {a:1}, {b:2})
28
+ bus.emit('blah')
29
+ end
30
+ it 'should return the job to emit events' do
31
+ bus.pipe_to_emit('foo').now
32
+ bus.receive(/^bar$/).then(the_job)
33
+ bus.pipe_to_emit('baz').now
34
+ out.expects(:write).with({a:1}, {b:2})
35
+ bus.pipe_to_emit('bar').now({a:1}, {b:2})
36
+ bus.pipe_to_emit('blah').now
37
+ end
38
+ end