standard-procedure-plumbing 0.4.3 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,386 +0,0 @@
1
- require "spec_helper"
2
-
3
- require_relative "../../lib/plumbing/actor/async"
4
- require_relative "../../lib/plumbing/actor/threaded"
5
- require_relative "../../lib/plumbing/actor/rails"
6
-
7
- RSpec.describe Plumbing::Actor do
8
- # standard:disable Lint/ConstantDefinitionInBlock
9
- class Counter
10
- include Plumbing::Actor
11
- async :name, :count, :slow_query, "slowly_increment", "raises_error"
12
- attr_reader :name, :count
13
-
14
- def initialize name, initial_value: 0
15
- @name = name
16
- @count = initial_value
17
- end
18
-
19
- protected
20
-
21
- def slowly_increment
22
- sleep 0.2
23
- @count += 1
24
- end
25
-
26
- def slow_query
27
- sleep 0.2
28
- @count
29
- end
30
-
31
- def raises_error = raise "I'm an error"
32
- end
33
-
34
- class StepCounter < Counter
35
- async :step_value
36
- attr_reader :step_value
37
-
38
- def initialize name, initial_value: 0, step_value: 5
39
- super(name, initial_value: initial_value)
40
- @step_value = step_value
41
- end
42
-
43
- protected
44
-
45
- def slowly_increment
46
- sleep 0.2
47
- @count += @step_value
48
- end
49
-
50
- def failing_query
51
- raise "I'm a failure"
52
- end
53
- end
54
-
55
- class WhoAmI
56
- include Plumbing::Actor
57
- async :me_as_actor, :me_as_self
58
-
59
- private
60
-
61
- def me_as_actor = as_actor
62
-
63
- def me_as_self = self
64
-
65
- def prepare = @calling_thread = Thread.current
66
-
67
- def check = @calling_thread == Thread.current
68
- end
69
-
70
- class Actor
71
- include Plumbing::Actor
72
- async :get_object_id, :get_object
73
-
74
- private def get_object_id(record) = record.object_id
75
- private def get_object(record) = record
76
- end
77
-
78
- class SafetyCheck
79
- include Plumbing::Actor
80
- async :called_from_actor_thread?
81
-
82
- def initialize tester
83
- @tester = tester
84
- @called_from_actor_thread = false
85
- configure_safety_check
86
- end
87
-
88
- private
89
-
90
- def called_from_actor_thread? = @called_from_actor_thread
91
-
92
- def configure_safety_check
93
- @tester.on_safety_check do
94
- safely do
95
- @called_from_actor_thread = proxy.within_actor?
96
- end
97
- end
98
- end
99
- end
100
-
101
- class Tester
102
- include Plumbing::Actor
103
- async :on_safety_check, :do_safety_check
104
-
105
- def initialize
106
- @on_safety_check = nil
107
- end
108
-
109
- private
110
-
111
- def on_safety_check(&block) = @on_safety_check = block
112
-
113
- def do_safety_check = @on_safety_check&.call
114
- end
115
-
116
- class ParameterHandler
117
- include Plumbing::Actor
118
- async :set_values, :args, :params, :block
119
- attr_reader :args, :params, :block
120
-
121
- def initialize
122
- @args = nil
123
- @params = nil
124
- @block = nil
125
- end
126
-
127
- private
128
-
129
- def set_values *args, **params, &block
130
- @args = args
131
- @params = params
132
- @block = block
133
- end
134
- end
135
- # standard:enable Lint/ConstantDefinitionInBlock
136
-
137
- [:inline, :async, :threaded].each do |mode|
138
- context "In #{mode} mode" do
139
- it "knows which async messages are understood" do
140
- expect(Counter.async_messages).to eq [:name, :count, :slow_query, :slowly_increment, :raises_error]
141
- end
142
-
143
- it "reuses existing proxy classes" do
144
- @counter = Counter.start "inline counter", initial_value: 100
145
- @proxy_class = @counter.class
146
-
147
- @counter = Counter.start "another inline counter", initial_value: 200
148
- expect(@counter.class).to eq @proxy_class
149
- end
150
-
151
- it "includes async messages from the superclass" do
152
- expect(StepCounter.async_messages).to eq [:name, :count, :slow_query, :slowly_increment, :raises_error, :step_value]
153
-
154
- @step_counter = StepCounter.start "step counter", initial_value: 100, step_value: 10
155
-
156
- expect(@step_counter.count.value).to eq 100
157
- expect(@step_counter.step_value.value).to eq 10
158
- @step_counter.slowly_increment
159
- expect(@step_counter.count.value).to eq 110
160
- end
161
-
162
- it "can access its own proxy" do
163
- @actor = WhoAmI.start
164
-
165
- expect(await { @actor.me_as_self }).to_not eq @actor
166
- expect(await { @actor.me_as_actor }).to eq @actor
167
- end
168
- it "sends a single positional parameter" do
169
- @parameter_handler = ParameterHandler.start
170
-
171
- @parameter_handler.set_values "this"
172
- expect(await { @parameter_handler.args }).to eq ["this"]
173
- end
174
-
175
- it "sends multiple positional parameters" do
176
- @parameter_handler = ParameterHandler.start
177
-
178
- @parameter_handler.set_values "this", "that"
179
- expect(await { @parameter_handler.args }).to eq ["this", "that"]
180
- end
181
-
182
- it "sends keyword parameters" do
183
- @parameter_handler = ParameterHandler.start
184
-
185
- @parameter_handler.set_values something: "for nothing", cat: "dog", number: 123
186
- expect(await { @parameter_handler.params }).to eq({something: "for nothing", cat: "dog", number: 123})
187
- end
188
-
189
- it "sends a mix of positional and keyword parameters" do
190
- @parameter_handler = ParameterHandler.start
191
-
192
- @parameter_handler.set_values "what do you say", 123, something: "for nothing"
193
- expect(await { @parameter_handler.args }).to eq ["what do you say", 123]
194
- expect(await { @parameter_handler.params }).to eq({something: "for nothing"})
195
- end
196
-
197
- it "sends a block parameter" do
198
- @parameter_handler = ParameterHandler.start
199
-
200
- @parameter_handler.set_values do
201
- "HELLO"
202
- end
203
-
204
- @block = await { @parameter_handler.block }
205
- expect(@block.call).to eq "HELLO"
206
- end
207
-
208
- it "sends a mix of positional and keyword parameters with a block" do
209
- @parameter_handler = ParameterHandler.start
210
-
211
- @parameter_handler.set_values "what do you say", 123, something: "for nothing" do
212
- "BOOM"
213
- end
214
-
215
- expect(await { @parameter_handler.args }).to eq ["what do you say", 123]
216
- expect(await { @parameter_handler.params }).to eq({something: "for nothing"})
217
- @block = await { @parameter_handler.block }
218
- expect(@block.call).to eq "BOOM"
219
- end
220
- end
221
- end
222
-
223
- context "Inline mode only" do
224
- around :example do |example|
225
- Plumbing.configure mode: :inline, &example
226
- end
227
-
228
- it "returns the result from a message immediately" do
229
- @counter = Counter.start "inline counter", initial_value: 100
230
- @time = Time.now
231
-
232
- expect(@counter.name.value).to eq "inline counter"
233
- expect(@counter.count.value).to eq 100
234
- expect(Time.now - @time).to be < 0.1
235
-
236
- expect(@counter.slow_query.value).to eq 100
237
- expect(Time.now - @time).to be > 0.1
238
- end
239
-
240
- it "sends all commands immediately" do
241
- @counter = Counter.start "inline counter", initial_value: 100
242
- @time = Time.now
243
-
244
- @counter.slowly_increment
245
-
246
- expect(@counter.count.value).to eq 101
247
- expect(Time.now - @time).to be > 0.1
248
- end
249
-
250
- it "can safely access its own data" do
251
- @tester = Tester.start
252
- @safety_check = SafetyCheck.start @tester
253
-
254
- @tester.do_safety_check
255
-
256
- expect(true).to become_equal_to { @safety_check.called_from_actor_thread?.value }
257
- end
258
- end
259
-
260
- [:threaded, :async].each do |mode|
261
- context "Asynchronously (#{mode})" do
262
- around :example do |example|
263
- Sync do
264
- Plumbing.configure mode: mode, &example
265
- end
266
- end
267
-
268
- it "performs queries in the background and waits for the response" do
269
- @counter = Counter.start "async counter", initial_value: 100
270
- @time = Time.now
271
-
272
- expect(@counter.name.value).to eq "async counter"
273
- expect(@counter.count.value).to eq 100
274
- expect(Time.now - @time).to be < 0.1
275
-
276
- expect(@counter.slow_query.value).to eq 100
277
- expect(Time.now - @time).to be > 0.1
278
- ensure
279
- @counter.stop
280
- end
281
-
282
- it "performs queries ignoring the response and returning immediately" do
283
- @counter = Counter.start "threaded counter", initial_value: 100
284
- @time = Time.now
285
-
286
- @counter.slow_query
287
-
288
- expect(Time.now - @time).to be < 0.1
289
- ensure
290
- @counter.stop
291
- end
292
-
293
- it "performs commands in the background and returning immediately" do
294
- @counter = Counter.start "threaded counter", initial_value: 100
295
- @time = Time.now
296
-
297
- @counter.slowly_increment
298
- expect(Time.now - @time).to be < 0.1
299
-
300
- # wait for the background task to complete
301
- expect(101).to become_equal_to { @counter.count.value }
302
- expect(Time.now - @time).to be > 0.1
303
- ensure
304
- @counter.stop
305
- end
306
-
307
- it "re-raises exceptions when checking the result" do
308
- @counter = Counter.start "failure"
309
-
310
- expect { @counter.raises_error.value }.to raise_error "I'm an error"
311
- ensure
312
- @counter.stop
313
- end
314
-
315
- it "does not raise exceptions if ignoring the result" do
316
- @counter = Counter.start "failure"
317
-
318
- expect { @counter.raises_error }.not_to raise_error
319
- ensure
320
- @counter.stop
321
- end
322
- end
323
- end
324
-
325
- context "Threaded mode only" do
326
- around :example do |example|
327
- Plumbing.configure mode: :threaded, &example
328
- end
329
-
330
- before do
331
- GlobalID.app = "rspec"
332
- GlobalID::Locator.use :rspec do |gid, options|
333
- Record.new gid.model_id
334
- end
335
- end
336
-
337
- # standard:disable Lint/ConstantDefinitionInBlock
338
- class Record
339
- include GlobalID::Identification
340
- attr_reader :id
341
- def initialize id
342
- @id = id
343
- end
344
-
345
- def == other
346
- other.id == @id
347
- end
348
- end
349
- # standard:enable Lint/ConstantDefinitionInBlock
350
-
351
- it "packs and unpacks arguments when sending them across threads" do
352
- @actor = Actor.start
353
- @record = Record.new "999"
354
-
355
- @object_id = @actor.get_object_id(@record).value
356
-
357
- expect(@object_id).to_not eq @record.object_id
358
- ensure
359
- @actor.stop
360
- end
361
-
362
- it "packs and unpacks results when sending them across threads" do
363
- @actor = Actor.start
364
- @record = Record.new "999"
365
-
366
- @object = @actor.get_object(@record).value
367
-
368
- expect(@object.id).to eq @record.id
369
- expect(@object.object_id).to_not eq @record.object_id
370
- ensure
371
- @actor.stop
372
- end
373
-
374
- it "can safely access its own data" do
375
- @tester = Tester.start
376
- @safety_check = SafetyCheck.start @tester
377
-
378
- @tester.do_safety_check
379
-
380
- expect(true).to become_equal_to { @safety_check.called_from_actor_thread?.value }
381
- ensure
382
- @tester.stop
383
- @safety_check.stop
384
- end
385
- end
386
- end
@@ -1,29 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Plumbing::CustomFilter do
4
- it "raises a TypeError if it is connected to a non-Pipe" do
5
- @invalid_source = Object.new
6
-
7
- expect { described_class.start source: @invalid_source }.to raise_error(TypeError)
8
- end
9
-
10
- it "defines a custom filter" do
11
- # standard:disable Lint/ConstantDefinitionInBlock
12
- class ReversingFilter < Plumbing::CustomFilter
13
- def received(event) = notify event.type.reverse, event.data
14
- end
15
- # standard:enable Lint/ConstantDefinitionInBlock
16
-
17
- @pipe = Plumbing::Pipe.start
18
- @filter = ReversingFilter.start(source: @pipe)
19
- @result = []
20
- @filter.add_observer do |event|
21
- @result << event.type
22
- end
23
-
24
- @pipe.notify "hello"
25
- @pipe.notify "world"
26
-
27
- expect(@result).to eq ["olleh", "dlrow"]
28
- end
29
- end
@@ -1,32 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Plumbing::Filter do
4
- it "raises a TypeError if it is connected to a non-Pipe" do
5
- @invalid_source = Object.new
6
-
7
- expect { described_class.start source: @invalid_source }.to raise_error(TypeError)
8
- end
9
-
10
- it "accepts event types" do
11
- @pipe = Plumbing::Pipe.start
12
-
13
- @filter = described_class.start source: @pipe do |event|
14
- %w[first_type third_type].include? event.type.to_s
15
- end
16
-
17
- @results = []
18
- @filter.add_observer do |event|
19
- @results << event
20
- end
21
-
22
- @pipe << Plumbing::Event.new(type: "first_type", data: nil)
23
- expect(@results.count).to eq 1
24
-
25
- @pipe << Plumbing::Event.new(type: "second_type", data: nil)
26
- expect(@results.count).to eq 1
27
-
28
- # Use the alternative syntax
29
- @pipe.notify "third_type"
30
- expect(@results.count).to eq 2
31
- end
32
- end
@@ -1,67 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Plumbing::Junction do
4
- it "raises a TypeError if it is connected to a non-Pipe" do
5
- @sources = [Plumbing::Pipe.start, Object.new, Plumbing::Pipe.start]
6
-
7
- expect { described_class.start(*@sources) }.to raise_error(TypeError)
8
- end
9
-
10
- it "publishes events from a single source" do
11
- @source = Plumbing::Pipe.start
12
- @junction = described_class.start @source
13
-
14
- @results = []
15
- @junction.add_observer do |event|
16
- @results << event
17
- end
18
-
19
- @event = Plumbing::Event.new type: "test_event", data: {test: "event"}
20
- @source << @event
21
-
22
- expect([@event]).to become_equal_to { @results }
23
- end
24
-
25
- it "publishes events from two sources" do
26
- @first_source = Plumbing::Pipe.start
27
- @second_source = Plumbing::Pipe.start
28
- @junction = described_class.start @first_source, @second_source
29
-
30
- @results = []
31
- @junction.add_observer do |event|
32
- @results << event
33
- end
34
-
35
- @first_event = Plumbing::Event.new type: "test_event", data: {test: "one"}
36
- @first_source << @first_event
37
- expect([@first_event]).to become_equal_to { @results }
38
-
39
- @second_event = Plumbing::Event.new type: "test_event", data: {test: "two"}
40
- @second_source << @second_event
41
- expect([@first_event, @second_event]).to become_equal_to { @results }
42
- end
43
-
44
- it "publishes events from multiple sources" do
45
- @first_source = Plumbing::Pipe.start
46
- @second_source = Plumbing::Pipe.start
47
- @third_source = Plumbing::Pipe.start
48
- @junction = described_class.start @first_source, @second_source, @third_source
49
-
50
- @results = []
51
- @junction.add_observer do |event|
52
- @results << event
53
- end
54
-
55
- @first_event = Plumbing::Event.new type: "test_event", data: {test: "one"}
56
- @first_source << @first_event
57
- expect([@first_event]).to become_equal_to { @results }
58
-
59
- @second_event = Plumbing::Event.new type: "test_event", data: {test: "two"}
60
- @second_source << @second_event
61
- expect([@first_event, @second_event]).to become_equal_to { @results }
62
-
63
- @third_event = Plumbing::Event.new type: "test_event", data: {test: "three"}
64
- @third_source << @third_event
65
- expect([@first_event, @second_event, @third_event]).to become_equal_to { @results }
66
- end
67
- end
@@ -1,31 +0,0 @@
1
- require "spec_helper"
2
- require_relative "a_pipe"
3
- require "async"
4
-
5
- RSpec.describe Plumbing::Pipe do
6
- context "inline" do
7
- around :example do |example|
8
- Plumbing.configure mode: :inline, &example
9
- end
10
-
11
- it_behaves_like "a pipe"
12
- end
13
-
14
- context "async" do
15
- around :example do |example|
16
- Sync do
17
- Plumbing.configure mode: :async, &example
18
- end
19
- end
20
-
21
- it_behaves_like "a pipe"
22
- end
23
-
24
- context "threaded" do
25
- around :example do |example|
26
- Plumbing.configure mode: :threaded, &example
27
- end
28
-
29
- it_behaves_like "a pipe"
30
- end
31
- end