light-service 0.10.2 → 0.14.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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +6 -0
  4. data/.travis.yml +12 -10
  5. data/Appraisals +4 -0
  6. data/README.md +61 -21
  7. data/RELEASES.md +16 -0
  8. data/gemfiles/activesupport_6.gemfile +8 -0
  9. data/lib/light-service.rb +1 -0
  10. data/lib/light-service/context.rb +6 -2
  11. data/lib/light-service/localization_adapter.rb +1 -1
  12. data/lib/light-service/organizer.rb +32 -0
  13. data/lib/light-service/organizer/with_reducer.rb +11 -6
  14. data/lib/light-service/organizer/with_reducer_factory.rb +11 -7
  15. data/lib/light-service/organizer/with_reducer_log_decorator.rb +5 -2
  16. data/lib/light-service/testing/context_factory.rb +19 -22
  17. data/lib/light-service/version.rb +1 -1
  18. data/light-service.gemspec +5 -4
  19. data/spec/acceptance/after_actions_spec.rb +13 -0
  20. data/spec/acceptance/custom_log_from_organizer_spec.rb +60 -0
  21. data/spec/acceptance/fail_spec.rb +42 -16
  22. data/spec/acceptance/organizer/add_aliases_spec.rb +28 -0
  23. data/spec/acceptance/organizer/add_to_context_spec.rb +30 -0
  24. data/spec/acceptance/organizer/execute_spec.rb +1 -1
  25. data/spec/acceptance/organizer/iterate_spec.rb +7 -0
  26. data/spec/acceptance/organizer/reduce_if_spec.rb +38 -0
  27. data/spec/acceptance/organizer/reduce_until_spec.rb +6 -0
  28. data/spec/acceptance/testing/context_factory_spec.rb +25 -4
  29. data/spec/action_spec.rb +8 -0
  30. data/spec/organizer_spec.rb +42 -14
  31. data/spec/sample/provides_free_shipping_action_spec.rb +1 -1
  32. data/spec/spec_helper.rb +2 -1
  33. data/spec/test_doubles.rb +186 -0
  34. data/spec/testing/context_factory/iterate_spec.rb +39 -0
  35. data/spec/testing/context_factory/reduce_if_spec.rb +40 -0
  36. data/spec/testing/context_factory/reduce_until_spec.rb +40 -0
  37. data/spec/testing/context_factory/with_callback_spec.rb +38 -0
  38. data/spec/testing/context_factory_spec.rb +28 -6
  39. metadata +40 -15
  40. data/gemfiles/activesupport_3.gemfile.lock +0 -76
  41. data/gemfiles/activesupport_4.gemfile.lock +0 -82
  42. data/gemfiles/activesupport_5.gemfile.lock +0 -82
@@ -19,6 +19,11 @@ describe LightService::Organizer do
19
19
  result = TestDoubles::AnOrganizer.call(:user => user)
20
20
  expect(result).to eq(ctx)
21
21
  end
22
+
23
+ it "sets itself as the organizer" do
24
+ result = TestDoubles::AnOrganizer.call(:user => user)
25
+ expect(result.organized_by).to eq TestDoubles::AnOrganizer
26
+ end
22
27
  end
23
28
 
24
29
  context "when #with is called with Context" do
@@ -44,20 +49,6 @@ describe LightService::Organizer do
44
49
  end
45
50
  end
46
51
 
47
- context "when no starting context is specified" do
48
- it "creates one implicitly" do
49
- expect(TestDoubles::AnAction).to receive(:execute)
50
- .with({})
51
- .and_return(ctx)
52
- expect(TestDoubles::AnotherAction).to receive(:execute)
53
- .with(ctx)
54
- .and_return(ctx)
55
-
56
- expect { TestDoubles::AnOrganizer.do_something_with_no_starting_context }
57
- .not_to raise_error
58
- end
59
- end
60
-
61
52
  context "when aliases are declared" do
62
53
  let(:organizer) do
63
54
  Class.new do
@@ -83,4 +74,41 @@ describe LightService::Organizer do
83
74
  organizer.call
84
75
  end
85
76
  end
77
+
78
+ context "when an organizer is nested and reduced within another" do
79
+ let(:reduced) { TestDoubles::NestingOrganizer.call(ctx) }
80
+ let(:organizer_result) do
81
+ TestDoubles::NotExplicitlyReturningContextOrganizer.call(ctx)
82
+ end
83
+
84
+ it "reduces an organizer which returns something" do
85
+ expect(organizer_result).to eq([1, 2, 3])
86
+ end
87
+
88
+ it "adds :foo and :bar to the context" do
89
+ reduced
90
+ expect(ctx[:foo]).to eq([1, 2, 3])
91
+ expect(ctx[:bar]).to eq(ctx[:foo])
92
+ end
93
+
94
+ it "returns the context" do
95
+ expect(reduced).to eq(ctx)
96
+ end
97
+ end
98
+
99
+ context 'can add items to the context' do
100
+ specify 'with #add_to_context' do
101
+ result = TestDoubles::AnOrganizerThatAddsToContext.call
102
+ expect(result[:strongest_avenger]).to eq 'The Thor'
103
+ expect(result[:last_jedi]).to eq 'Rey'
104
+ end
105
+ end
106
+
107
+ context 'can assign key aliaeses' do
108
+ it 'with #add_aliases' do
109
+ result = TestDoubles::AnOrganizerThatAddsAliases.call
110
+ expect(result[:foo]).to eq :bar
111
+ expect(result[:baz]).to eq :bar
112
+ end
113
+ end
86
114
  end
@@ -15,7 +15,7 @@ describe ProvidesFreeShippingAction do
15
15
  end
16
16
 
17
17
  context "when the order total with tax is <= 200" do
18
- specify "order gets free shipping" do
18
+ specify "order does not get free shipping" do
19
19
  allow(order).to receive_messages(:total_with_tax => 200)
20
20
  expect(order).not_to receive(:provide_free_shipping!)
21
21
 
@@ -15,8 +15,9 @@ end
15
15
  require 'light-service'
16
16
  require 'light-service/testing'
17
17
  require 'ostruct'
18
- require 'active_support/core_ext/string'
19
18
  require 'pry'
20
19
  require 'support'
20
+ require 'test_doubles'
21
+ require 'stringio'
21
22
 
22
23
  I18n.enforce_available_locales = true
@@ -102,6 +102,36 @@ module TestDoubles
102
102
  end
103
103
  end
104
104
 
105
+ class NotExplicitlyReturningContextOrganizer
106
+ extend LightService::Organizer
107
+
108
+ def self.call(context)
109
+ context[:foo] = [1, 2, 3]
110
+ end
111
+ end
112
+
113
+ class NestingOrganizer
114
+ extend LightService::Organizer
115
+
116
+ def self.call(context)
117
+ with(context).reduce(actions)
118
+ end
119
+
120
+ def self.actions
121
+ [NotExplicitlyReturningContextOrganizer, NestedAction]
122
+ end
123
+ end
124
+
125
+ class NestedAction
126
+ extend LightService::Action
127
+
128
+ expects :foo
129
+
130
+ executed do |context|
131
+ context[:bar] = context.foo
132
+ end
133
+ end
134
+
105
135
  class MakesTeaWithMilkAction
106
136
  extend LightService::Action
107
137
  expects :tea, :milk
@@ -223,6 +253,34 @@ module TestDoubles
223
253
  end
224
254
  end
225
255
 
256
+ class ExtraArgumentAdditionOrganizer
257
+ extend LightService::Organizer
258
+
259
+ def self.call(number, another_number)
260
+ with(:number => number + another_number).reduce(actions)
261
+ end
262
+
263
+ def self.actions
264
+ [
265
+ AddsOneAction,
266
+ AddsTwoAction,
267
+ AddsThreeAction
268
+ ]
269
+ end
270
+ end
271
+
272
+ class AddsOne
273
+ extend LightService::Organizer
274
+
275
+ def call(ctx)
276
+ with(ctx).reduce(actions)
277
+ end
278
+
279
+ def self.actions
280
+ [AddsOneAction]
281
+ end
282
+ end
283
+
226
284
  class AddsOneAction
227
285
  extend LightService::Action
228
286
  expects :number
@@ -251,6 +309,99 @@ module TestDoubles
251
309
  end
252
310
  end
253
311
 
312
+ class IterateOrganizer
313
+ extend LightService::Organizer
314
+
315
+ def self.call(ctx)
316
+ with(ctx).reduce(actions)
317
+ end
318
+
319
+ def self.actions
320
+ [
321
+ AddsOneIteratesAction,
322
+ iterate(:numbers, [
323
+ AddsTwoAction,
324
+ AddsThreeAction
325
+ ])
326
+ ]
327
+ end
328
+ end
329
+
330
+ class AddsOneIteratesAction
331
+ extend LightService::Action
332
+ expects :numbers
333
+ promises :numbers
334
+
335
+ executed do |context|
336
+ context.numbers = context.numbers.map { |n| n + 1 }
337
+ end
338
+ end
339
+
340
+ class CallbackOrganizer
341
+ extend LightService::Organizer
342
+
343
+ def self.call(ctx)
344
+ with(ctx).reduce(actions)
345
+ end
346
+
347
+ def self.actions
348
+ [
349
+ AddsOneAction,
350
+ with_callback(AddTenCallbackAction, [
351
+ AddsTwoAction,
352
+ AddsThreeAction
353
+ ])
354
+ ]
355
+ end
356
+ end
357
+
358
+ class AddTenCallbackAction
359
+ extend LightService::Action
360
+ expects :number, :callback
361
+
362
+ executed do |context|
363
+ context.number += 10
364
+ context.number =
365
+ context.callback.call(context).fetch(:number)
366
+ end
367
+ end
368
+
369
+ class ReduceUntilOrganizer
370
+ extend LightService::Organizer
371
+
372
+ def self.call(ctx)
373
+ with(ctx).reduce(actions)
374
+ end
375
+
376
+ def self.actions
377
+ [
378
+ AddsOneAction,
379
+ reduce_until(->(ctx) { ctx.number > 3 }, [
380
+ AddsTwoAction,
381
+ AddsThreeAction
382
+ ])
383
+ ]
384
+ end
385
+ end
386
+
387
+ class ReduceIfOrganizer
388
+ extend LightService::Organizer
389
+
390
+ def self.call(ctx)
391
+ with(ctx).reduce(actions)
392
+ end
393
+
394
+ def self.actions
395
+ [
396
+ AddsOneAction,
397
+ reduce_if(->(ctx) { ctx.number > 1 }, [
398
+ AddsTwoAction,
399
+ AddsThreeAction
400
+ ])
401
+ ]
402
+ end
403
+ end
404
+
254
405
  class MakesTeaExpectingReservedKey
255
406
  extend LightService::Action
256
407
  expects :tea, :message
@@ -404,4 +555,39 @@ module TestDoubles
404
555
  ctx.total += ctx.number
405
556
  end
406
557
  end
558
+
559
+ class CapitalizeMessage
560
+ extend LightService::Action
561
+ expects :a_message
562
+ promises :final_message
563
+
564
+ executed do |ctx|
565
+ ctx.final_message = ctx.a_message.upcase
566
+ end
567
+ end
568
+
569
+ class AnOrganizerThatAddsToContext
570
+ extend LightService::Organizer
571
+ def self.call
572
+ with.reduce(actions)
573
+ end
574
+
575
+ def self.actions
576
+ [add_to_context(
577
+ :strongest_avenger => 'The Thor',
578
+ :last_jedi => 'Rey'
579
+ )]
580
+ end
581
+ end
582
+
583
+ class AnOrganizerThatAddsAliases
584
+ extend LightService::Organizer
585
+ def self.call
586
+ with(:foo => :bar).reduce(actions)
587
+ end
588
+
589
+ def self.actions
590
+ [add_aliases(:foo => :baz)]
591
+ end
592
+ end
407
593
  end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'ContextFactory - used with IterateOrganizer' do
5
+ let(:organizer) { TestDoubles::IterateOrganizer }
6
+
7
+ context 'when called with outside iterate steps' do
8
+ it 'creates a context up-to the action defined before the iteration' do
9
+ ctx =
10
+ LightService::Testing::ContextFactory
11
+ .make_from(organizer)
12
+ .for(TestDoubles::AddsOneIteratesAction)
13
+ .with(:numbers => [1, 2])
14
+
15
+ expect(ctx[:numbers]).to eq([1, 2])
16
+ end
17
+
18
+ it 'creates a context up-to iteration with empty context steps' do
19
+ ctx =
20
+ LightService::Testing::ContextFactory
21
+ .make_from(organizer)
22
+ .for(TestDoubles::AddsTwoAction)
23
+ .with(:numbers => [1, 2])
24
+
25
+ expect(ctx.numbers).to eq([2, 3])
26
+ end
27
+
28
+ it 'creates a context only to the first step of the iteration' do
29
+ ctx =
30
+ LightService::Testing::ContextFactory
31
+ .make_from(organizer)
32
+ .for(TestDoubles::AddsThreeAction)
33
+ .with(:numbers => [1, 2])
34
+
35
+ expect(ctx.numbers).to eq([2, 3])
36
+ expect(ctx.number).to eq(4)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'ContextFactory - used with ReduceIfOrganizer' do
5
+ let(:organizer) { TestDoubles::ReduceIfOrganizer }
6
+
7
+ context 'when called with a truthy argument action' do
8
+ it 'executes a context up-to the callback action' do
9
+ ctx =
10
+ LightService::Testing::ContextFactory
11
+ .make_from(organizer)
12
+ .for(TestDoubles::AddsThreeAction)
13
+ .with(:number => 1)
14
+
15
+ expect(ctx.number).to eq(4)
16
+ end
17
+
18
+ it 'creates a context up-to action with empty context steps' do
19
+ ctx =
20
+ LightService::Testing::ContextFactory
21
+ .make_from(organizer)
22
+ .for(TestDoubles::AddsTwoAction)
23
+ .with(:number => 1)
24
+
25
+ expect(ctx.number).to eq(2)
26
+ end
27
+ end
28
+
29
+ context 'when called with a false argument action' do
30
+ it 'does not execute the steps' do
31
+ ctx =
32
+ LightService::Testing::ContextFactory
33
+ .make_from(organizer)
34
+ .for(TestDoubles::AddsThreeAction)
35
+ .with(:number => 0)
36
+
37
+ expect(ctx.number).to eq(1)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'ContextFactory - used with ReduceUntilOrganizer' do
5
+ let(:organizer) { TestDoubles::ReduceUntilOrganizer }
6
+
7
+ context 'when called with truthy block' do
8
+ it 'creates a context up-to the action defined before the iteration' do
9
+ ctx =
10
+ LightService::Testing::ContextFactory
11
+ .make_from(organizer)
12
+ .for(TestDoubles::AddsTwoAction)
13
+ .with(:number => 1)
14
+
15
+ expect(ctx[:number]).to eq(2)
16
+ end
17
+
18
+ it 'creates a context only to the first step of the loop' do
19
+ ctx =
20
+ LightService::Testing::ContextFactory
21
+ .make_from(organizer)
22
+ .for(TestDoubles::AddsThreeAction)
23
+ .with(:number => 1)
24
+
25
+ expect(ctx.number).to eq(4)
26
+ end
27
+ end
28
+
29
+ context 'when called with falsey block' do
30
+ it 'creates a context up-to the first step' do
31
+ ctx =
32
+ LightService::Testing::ContextFactory
33
+ .make_from(organizer)
34
+ .for(TestDoubles::AddsThreeAction)
35
+ .with(:number => 7)
36
+
37
+ expect(ctx[:number]).to eq(10)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'ContextFactory - used with CallbackOrganizer' do
5
+ let(:organizer) { TestDoubles::CallbackOrganizer }
6
+
7
+ context 'when called with the callback action' do
8
+ it 'creates a context up-to the callback action' do
9
+ ctx =
10
+ LightService::Testing::ContextFactory
11
+ .make_from(organizer)
12
+ .for(TestDoubles::AddTenCallbackAction)
13
+ .with(:number => 1)
14
+
15
+ expect(ctx.number).to eq(2)
16
+ end
17
+
18
+ it 'creates a context up-to callback action with empty context steps' do
19
+ ctx =
20
+ LightService::Testing::ContextFactory
21
+ .make_from(organizer)
22
+ .for(TestDoubles::AddsTwoAction)
23
+ .with(:number => 1)
24
+
25
+ expect(ctx.number).to eq(12)
26
+ end
27
+
28
+ it 'creates a context up-to the action defined in context steps' do
29
+ ctx =
30
+ LightService::Testing::ContextFactory
31
+ .make_from(organizer)
32
+ .for(TestDoubles::AddsThreeAction)
33
+ .with(:number => 1)
34
+
35
+ expect(ctx.number).to eq(14)
36
+ end
37
+ end
38
+ end