functional-light-service 0.2.4
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.rubocop.yml +72 -0
- data/.travis.yml +22 -0
- data/Appraisals +3 -0
- data/CHANGELOG.md +37 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +1424 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/functional-light-service.gemspec +26 -0
- data/gemfiles/activesupport_5.gemfile +8 -0
- data/gemfiles/activesupport_5.gemfile.lock +82 -0
- data/lib/functional-light-service/action.rb +102 -0
- data/lib/functional-light-service/configuration.rb +24 -0
- data/lib/functional-light-service/context/key_verifier.rb +118 -0
- data/lib/functional-light-service/context.rb +165 -0
- data/lib/functional-light-service/errors.rb +6 -0
- data/lib/functional-light-service/functional/enum.rb +254 -0
- data/lib/functional-light-service/functional/maybe.rb +14 -0
- data/lib/functional-light-service/functional/monad.rb +66 -0
- data/lib/functional-light-service/functional/null.rb +74 -0
- data/lib/functional-light-service/functional/option.rb +99 -0
- data/lib/functional-light-service/functional/result.rb +122 -0
- data/lib/functional-light-service/localization_adapter.rb +44 -0
- data/lib/functional-light-service/organizer/execute.rb +14 -0
- data/lib/functional-light-service/organizer/iterate.rb +22 -0
- data/lib/functional-light-service/organizer/reduce_if.rb +17 -0
- data/lib/functional-light-service/organizer/reduce_until.rb +20 -0
- data/lib/functional-light-service/organizer/scoped_reducable.rb +13 -0
- data/lib/functional-light-service/organizer/verify_call_method_exists.rb +28 -0
- data/lib/functional-light-service/organizer/with_callback.rb +26 -0
- data/lib/functional-light-service/organizer/with_reducer.rb +71 -0
- data/lib/functional-light-service/organizer/with_reducer_factory.rb +18 -0
- data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +105 -0
- data/lib/functional-light-service/organizer.rb +105 -0
- data/lib/functional-light-service/testing/context_factory.rb +40 -0
- data/lib/functional-light-service/testing.rb +1 -0
- data/lib/functional-light-service/version.rb +3 -0
- data/lib/functional-light-service.rb +29 -0
- data/resources/fail_actions.png +0 -0
- data/resources/light-service.png +0 -0
- data/resources/organizer_and_actions.png +0 -0
- data/resources/skip_actions.png +0 -0
- data/spec/acceptance/add_numbers_spec.rb +11 -0
- data/spec/acceptance/after_actions_spec.rb +71 -0
- data/spec/acceptance/around_each_spec.rb +19 -0
- data/spec/acceptance/before_actions_spec.rb +98 -0
- data/spec/acceptance/custom_log_from_organizer_spec.rb +60 -0
- data/spec/acceptance/fail_spec.rb +24 -0
- data/spec/acceptance/include_warning_spec.rb +29 -0
- data/spec/acceptance/log_from_organizer_spec.rb +154 -0
- data/spec/acceptance/message_localization_spec.rb +118 -0
- data/spec/acceptance/not_having_call_method_warning_spec.rb +39 -0
- data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +42 -0
- data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +65 -0
- data/spec/acceptance/organizer/execute_spec.rb +46 -0
- data/spec/acceptance/organizer/iterate_spec.rb +37 -0
- data/spec/acceptance/organizer/reduce_if_spec.rb +51 -0
- data/spec/acceptance/organizer/reduce_until_spec.rb +43 -0
- data/spec/acceptance/organizer/with_callback_spec.rb +110 -0
- data/spec/acceptance/rollback_spec.rb +132 -0
- data/spec/acceptance/skip_all_warning_spec.rb +20 -0
- data/spec/acceptance/testing/context_factory_spec.rb +54 -0
- data/spec/action_expected_keys_spec.rb +63 -0
- data/spec/action_expects_and_promises_spec.rb +93 -0
- data/spec/action_promised_keys_spec.rb +122 -0
- data/spec/action_spec.rb +89 -0
- data/spec/context/inspect_spec.rb +57 -0
- data/spec/context_spec.rb +197 -0
- data/spec/examples/amount_spec.rb +77 -0
- data/spec/examples/controller_spec.rb +63 -0
- data/spec/examples/validate_address_spec.rb +37 -0
- data/spec/lib/deterministic/class_mixin_spec.rb +24 -0
- data/spec/lib/deterministic/currify_spec.rb +88 -0
- data/spec/lib/deterministic/monad_axioms.rb +44 -0
- data/spec/lib/deterministic/monad_spec.rb +45 -0
- data/spec/lib/deterministic/null_spec.rb +58 -0
- data/spec/lib/deterministic/option_spec.rb +133 -0
- data/spec/lib/deterministic/result/failure_spec.rb +65 -0
- data/spec/lib/deterministic/result/result_map_spec.rb +154 -0
- data/spec/lib/deterministic/result/result_shared.rb +24 -0
- data/spec/lib/deterministic/result/success_spec.rb +41 -0
- data/spec/lib/deterministic/result_spec.rb +63 -0
- data/spec/lib/enum_spec.rb +112 -0
- data/spec/localization_adapter_spec.rb +83 -0
- data/spec/organizer/with_reducer_spec.rb +56 -0
- data/spec/organizer_key_aliases_spec.rb +29 -0
- data/spec/organizer_spec.rb +93 -0
- data/spec/readme_spec.rb +47 -0
- data/spec/sample/calculates_order_tax_action_spec.rb +16 -0
- data/spec/sample/calculates_tax_spec.rb +30 -0
- data/spec/sample/looks_up_tax_percentage_action_spec.rb +53 -0
- data/spec/sample/provides_free_shipping_action_spec.rb +25 -0
- data/spec/sample/tax/calculates_order_tax_action.rb +9 -0
- data/spec/sample/tax/calculates_tax.rb +11 -0
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +27 -0
- data/spec/sample/tax/provides_free_shipping_action.rb +10 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support.rb +1 -0
- data/spec/test_doubles.rb +552 -0
- data/spec/testing/context_factory/iterate_spec.rb +39 -0
- data/spec/testing/context_factory/reduce_if_spec.rb +40 -0
- data/spec/testing/context_factory/reduce_until_spec.rb +40 -0
- data/spec/testing/context_factory/with_callback_spec.rb +38 -0
- data/spec/testing/context_factory_spec.rb +55 -0
- metadata +285 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
|
5
|
+
let(:empty_context) { FunctionalLightService::Context.make }
|
|
6
|
+
|
|
7
|
+
it 'reduces each item of a collection and singularizes the collection key' do
|
|
8
|
+
result = TestDoubles::TestIterate.call(:number => 1,
|
|
9
|
+
:counters => [1, 2, 3, 4])
|
|
10
|
+
|
|
11
|
+
expect(result).to be_success
|
|
12
|
+
expect(result.number).to eq(5)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'accepts a single action or organizer' do
|
|
16
|
+
result = TestDoubles::TestIterate.call_single(:number => 1,
|
|
17
|
+
:counters => [1, 2, 3, 4])
|
|
18
|
+
|
|
19
|
+
expect(result).to be_success
|
|
20
|
+
expect(result.number).to eq(5)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'will not iterate over a failed context' do
|
|
24
|
+
empty_context.fail!('Something bad happened')
|
|
25
|
+
|
|
26
|
+
result = TestDoubles::TestIterate.call(empty_context)
|
|
27
|
+
|
|
28
|
+
expect(result).to be_failure
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'does not iterate over a skipped context' do
|
|
32
|
+
empty_context.skip_remaining!('No more needed')
|
|
33
|
+
|
|
34
|
+
result = TestDoubles::TestIterate.call(empty_context)
|
|
35
|
+
expect(result).to be_success
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
|
5
|
+
class TestReduceIf
|
|
6
|
+
extend FunctionalLightService::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
TestDoubles::AddsOneAction,
|
|
15
|
+
reduce_if(->(ctx) { ctx.number == 1 },
|
|
16
|
+
TestDoubles::AddsOneAction)
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
let(:empty_context) { FunctionalLightService::Context.make }
|
|
22
|
+
|
|
23
|
+
it 'reduces if the block evaluates to true' do
|
|
24
|
+
result = TestReduceIf.call(:number => 0)
|
|
25
|
+
|
|
26
|
+
expect(result).to be_success
|
|
27
|
+
expect(result[:number]).to eq(2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'does not reduce if the block evaluates to false' do
|
|
31
|
+
result = TestReduceIf.call(:number => 2)
|
|
32
|
+
|
|
33
|
+
expect(result).to be_success
|
|
34
|
+
expect(result[:number]).to eq(3)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'will not reduce over a failed context' do
|
|
38
|
+
empty_context.fail!('Something bad happened')
|
|
39
|
+
|
|
40
|
+
result = TestReduceIf.call(empty_context)
|
|
41
|
+
|
|
42
|
+
expect(result).to be_failure
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'does not reduce over a skipped context' do
|
|
46
|
+
empty_context.skip_remaining!('No more needed')
|
|
47
|
+
|
|
48
|
+
result = TestReduceIf.call(empty_context)
|
|
49
|
+
expect(result).to be_success
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
|
5
|
+
class TestReduceUntil
|
|
6
|
+
extend FunctionalLightService::Organizer
|
|
7
|
+
|
|
8
|
+
def self.call(context)
|
|
9
|
+
with(context).reduce(actions)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.actions
|
|
13
|
+
[
|
|
14
|
+
reduce_until(->(ctx) { ctx.number == 3 },
|
|
15
|
+
TestDoubles::AddsOneAction)
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:empty_context) { FunctionalLightService::Context.make }
|
|
21
|
+
|
|
22
|
+
it 'reduces until the block evaluates to true' do
|
|
23
|
+
context = { :number => 1 }
|
|
24
|
+
result = TestReduceUntil.call(context)
|
|
25
|
+
|
|
26
|
+
expect(result).to be_success
|
|
27
|
+
expect(result.number).to eq(3)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'does not execute on failed context' do
|
|
31
|
+
empty_context.fail!('Something bad happened')
|
|
32
|
+
|
|
33
|
+
result = TestReduceUntil.call(empty_context)
|
|
34
|
+
expect(result).to be_failure
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'does not execute a skipped context' do
|
|
38
|
+
empty_context.skip_remaining!('No more needed')
|
|
39
|
+
|
|
40
|
+
result = TestReduceUntil.call(empty_context)
|
|
41
|
+
expect(result).to be_success
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
|
5
|
+
describe 'a simple case with a single callback' do
|
|
6
|
+
it 'calls the actions defined with callback' do
|
|
7
|
+
result = TestDoubles::TestWithCallback.call
|
|
8
|
+
|
|
9
|
+
expect(result.counter).to eq(3)
|
|
10
|
+
expect(result.total).to eq(6)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'a more complex example with nested callbacks' do
|
|
15
|
+
class TestWithNestedCallback
|
|
16
|
+
extend FunctionalLightService::Organizer
|
|
17
|
+
|
|
18
|
+
def self.call(context = {})
|
|
19
|
+
with(context).reduce(actions)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.actions
|
|
23
|
+
[
|
|
24
|
+
SetUpNestedContextAction,
|
|
25
|
+
with_callback(IterateOuterCollectionAction,
|
|
26
|
+
[IncrementOuterCountAction,
|
|
27
|
+
with_callback(TestDoubles::IterateCollectionAction,
|
|
28
|
+
[TestDoubles::IncrementCountAction,
|
|
29
|
+
TestDoubles::AddToTotalAction])])
|
|
30
|
+
]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class SetUpNestedContextAction
|
|
35
|
+
extend FunctionalLightService::Action
|
|
36
|
+
promises :outer_numbers, :outer_counter,
|
|
37
|
+
:numbers, :counter, :total
|
|
38
|
+
|
|
39
|
+
executed do |ctx|
|
|
40
|
+
ctx.outer_numbers = [12, 17]
|
|
41
|
+
ctx.outer_counter = 0
|
|
42
|
+
ctx.numbers = [1, 2, 3]
|
|
43
|
+
ctx.counter = 0
|
|
44
|
+
ctx.total = 0
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class IterateOuterCollectionAction
|
|
49
|
+
extend FunctionalLightService::Action
|
|
50
|
+
expects :outer_numbers, :callback
|
|
51
|
+
promises :outer_number
|
|
52
|
+
|
|
53
|
+
executed do |ctx|
|
|
54
|
+
ctx.outer_numbers.each do |outer_number|
|
|
55
|
+
ctx.outer_number = outer_number
|
|
56
|
+
ctx.callback.call(ctx)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class IncrementOuterCountAction
|
|
62
|
+
extend FunctionalLightService::Action
|
|
63
|
+
expects :outer_counter
|
|
64
|
+
|
|
65
|
+
executed do |ctx|
|
|
66
|
+
ctx.outer_counter = ctx.outer_counter + 1
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'calls both the action and the nested callbacks' do
|
|
71
|
+
result = TestWithNestedCallback.call
|
|
72
|
+
|
|
73
|
+
expect(result.outer_counter).to eq(2)
|
|
74
|
+
# Counts and total are the duplicates of
|
|
75
|
+
# what you'll see in the simple spec,
|
|
76
|
+
# as the internal callback logic is called
|
|
77
|
+
# twice due to 2 items in the outer_numbers
|
|
78
|
+
# collection.
|
|
79
|
+
expect(result.counter).to eq(6)
|
|
80
|
+
expect(result.total).to eq(12)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe 'with failed or skipped context' do
|
|
85
|
+
class TestWithFailureCallback
|
|
86
|
+
extend FunctionalLightService::Organizer
|
|
87
|
+
|
|
88
|
+
def self.call(context = {})
|
|
89
|
+
with(context).reduce(actions)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.actions
|
|
93
|
+
[
|
|
94
|
+
TestDoubles::SetUpContextAction,
|
|
95
|
+
with_callback(TestDoubles::IterateCollectionAction,
|
|
96
|
+
[TestDoubles::IncrementCountAction,
|
|
97
|
+
TestDoubles::FailureAction])
|
|
98
|
+
]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'will not process the routine' do
|
|
103
|
+
result = TestWithFailureCallback.call
|
|
104
|
+
|
|
105
|
+
expect(result).to be_failure
|
|
106
|
+
expect(result.counter).to eq(1)
|
|
107
|
+
expect(result.total).to eq(0)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
class RollbackOrganizer
|
|
5
|
+
extend FunctionalLightService::Organizer
|
|
6
|
+
|
|
7
|
+
def self.call(number)
|
|
8
|
+
with(:number => number).reduce(
|
|
9
|
+
AddsOneWithRollbackAction,
|
|
10
|
+
TestDoubles::AddsTwoAction,
|
|
11
|
+
AddsThreeWithRollbackAction
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class AddsOneWithRollbackAction
|
|
17
|
+
extend FunctionalLightService::Action
|
|
18
|
+
expects :number
|
|
19
|
+
promises :number
|
|
20
|
+
|
|
21
|
+
executed do |context|
|
|
22
|
+
context.fail_with_rollback! if context.number.zero?
|
|
23
|
+
|
|
24
|
+
context.number += 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
rolled_back do |context|
|
|
28
|
+
context.number -= 1
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class AddsThreeWithRollbackAction
|
|
33
|
+
extend FunctionalLightService::Action
|
|
34
|
+
expects :number
|
|
35
|
+
|
|
36
|
+
executed do |context|
|
|
37
|
+
context.number = context.number + 3
|
|
38
|
+
|
|
39
|
+
context.fail_with_rollback!("I did not like this!")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
rolled_back do |context|
|
|
43
|
+
context.number -= 3
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class RollbackOrganizerWithNoRollback
|
|
48
|
+
extend FunctionalLightService::Organizer
|
|
49
|
+
|
|
50
|
+
def self.call(number)
|
|
51
|
+
with(:number => number).reduce(
|
|
52
|
+
TestDoubles::AddsOneAction,
|
|
53
|
+
TestDoubles::AddsTwoAction,
|
|
54
|
+
AddsThreeWithNoRollbackAction
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class AddsThreeWithNoRollbackAction
|
|
60
|
+
extend FunctionalLightService::Action
|
|
61
|
+
expects :number
|
|
62
|
+
|
|
63
|
+
executed do |context|
|
|
64
|
+
context.number = context.number + 3
|
|
65
|
+
|
|
66
|
+
context.fail_with_rollback!("I did not like this!")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class RollbackOrganizerWithMiddleRollback
|
|
71
|
+
extend FunctionalLightService::Organizer
|
|
72
|
+
|
|
73
|
+
def self.call(number)
|
|
74
|
+
with(:number => number).reduce(
|
|
75
|
+
TestDoubles::AddsOneAction,
|
|
76
|
+
AddsTwoActionWithRollback,
|
|
77
|
+
TestDoubles::AddsThreeAction
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class AddsTwoActionWithRollback
|
|
83
|
+
extend FunctionalLightService::Action
|
|
84
|
+
expects :number
|
|
85
|
+
|
|
86
|
+
executed do |context|
|
|
87
|
+
context.number = context.number + 2
|
|
88
|
+
|
|
89
|
+
context.fail_with_rollback!("I did not like this a bit!")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
rolled_back do |context|
|
|
93
|
+
context.number -= 2
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "Rolling back actions when there is a failure" do
|
|
98
|
+
it "Adds 1, 2, 3 to 1 and rolls back " do
|
|
99
|
+
result = RollbackOrganizer.call 1
|
|
100
|
+
number = result.fetch(:number)
|
|
101
|
+
|
|
102
|
+
expect(result).to be_failure
|
|
103
|
+
expect(result.message).to eq("I did not like this!")
|
|
104
|
+
expect(number).to eq(3)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "won't error out when actions don't define rollback" do
|
|
108
|
+
result = RollbackOrganizerWithNoRollback.call 1
|
|
109
|
+
number = result.fetch(:number)
|
|
110
|
+
|
|
111
|
+
expect(result).to be_failure
|
|
112
|
+
expect(result.message).to eq("I did not like this!")
|
|
113
|
+
expect(number).to eq(7)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "rolls back properly when triggered with an action in the middle" do
|
|
117
|
+
result = RollbackOrganizerWithMiddleRollback.call 1
|
|
118
|
+
number = result.fetch(:number)
|
|
119
|
+
|
|
120
|
+
expect(result).to be_failure
|
|
121
|
+
expect(result.message).to eq("I did not like this a bit!")
|
|
122
|
+
expect(number).to eq(2)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "rolls back from the first action" do
|
|
126
|
+
result = RollbackOrganizer.call 0
|
|
127
|
+
number = result.fetch(:number)
|
|
128
|
+
|
|
129
|
+
expect(result).to be_failure
|
|
130
|
+
expect(number).to eq(-1)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe "skip_all! has been deprecated" do
|
|
4
|
+
it "is now skip_remaining!" do
|
|
5
|
+
class SkipAllDeprecatedAction
|
|
6
|
+
extend FunctionalLightService::Action
|
|
7
|
+
|
|
8
|
+
executed do |ctx|
|
|
9
|
+
ctx.skip_all!("No need to execute other actions.")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
expected_msg = "Using skip_all! has been deprecated, " \
|
|
14
|
+
"please use `skip_remaining!` instead."
|
|
15
|
+
expect(ActiveSupport::Deprecation).to receive(:warn)
|
|
16
|
+
.with(expected_msg)
|
|
17
|
+
|
|
18
|
+
SkipAllDeprecatedAction.execute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
class AdditionOrganizerContextFactory
|
|
5
|
+
def self.make_for(action, number)
|
|
6
|
+
number += 3 # You can add more logic to prepare your context
|
|
7
|
+
|
|
8
|
+
FunctionalLightService::Testing::ContextFactory
|
|
9
|
+
.make_from(TestDoubles::AdditionOrganizer)
|
|
10
|
+
.for(action)
|
|
11
|
+
.with(number)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RSpec.describe TestDoubles::AddsThreeAction do
|
|
16
|
+
it 'creates a context for the action with ContextFactory wrapper' do
|
|
17
|
+
context =
|
|
18
|
+
AdditionOrganizerContextFactory
|
|
19
|
+
.make_for(TestDoubles::AddsThreeAction, 1)
|
|
20
|
+
|
|
21
|
+
expect(context.number).to eq(7)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'creates a context for the action using the ContextFactory' do
|
|
25
|
+
context =
|
|
26
|
+
FunctionalLightService::Testing::ContextFactory
|
|
27
|
+
.make_from(TestDoubles::AdditionOrganizer)
|
|
28
|
+
.for(TestDoubles::AddsThreeAction)
|
|
29
|
+
.with(4) # Context is a "glorified" hash
|
|
30
|
+
|
|
31
|
+
expect(context.number).to eq(7)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "works with multiple arguments passed to Organizer's call method" do
|
|
35
|
+
context = FunctionalLightService::Testing::ContextFactory
|
|
36
|
+
.make_from(TestDoubles::ExtraArgumentAdditionOrganizer)
|
|
37
|
+
.for(described_class)
|
|
38
|
+
.with(4, 2)
|
|
39
|
+
|
|
40
|
+
expect(context.number).to eq(9)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
RSpec.describe TestDoubles::AddsTwoAction do
|
|
45
|
+
it 'does not execute a callback entirely from a ContextFactory' do
|
|
46
|
+
context = FunctionalLightService::Testing::ContextFactory
|
|
47
|
+
.make_from(TestDoubles::CallbackOrganizer)
|
|
48
|
+
.for(described_class)
|
|
49
|
+
.with(:number => 0)
|
|
50
|
+
|
|
51
|
+
# add 1, add 10, then stop before executing first add 2
|
|
52
|
+
expect(context.number).to eq(11)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'test_doubles'
|
|
3
|
+
|
|
4
|
+
describe ":expects macro" do
|
|
5
|
+
context "when expected keys are in the context" do
|
|
6
|
+
it "can access the keys as class methods" do
|
|
7
|
+
resulting_context = TestDoubles::MakesTeaWithMilkAction.execute(
|
|
8
|
+
:tea => "black",
|
|
9
|
+
:milk => "full cream",
|
|
10
|
+
:something => "else"
|
|
11
|
+
)
|
|
12
|
+
expect(resulting_context[:milk_tea]).to eq("black - full cream")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context "when an expected key is not in the context" do
|
|
17
|
+
it "raises an FunctionalLightService::ExpectedKeysNotInContextError" do
|
|
18
|
+
exception_msg = "expected :milk to be in the context during " \
|
|
19
|
+
"TestDoubles::MakesTeaWithMilkAction"
|
|
20
|
+
expect do
|
|
21
|
+
TestDoubles::MakesTeaWithMilkAction.execute(:tea => "black")
|
|
22
|
+
end.to \
|
|
23
|
+
raise_error(FunctionalLightService::ExpectedKeysNotInContextError, exception_msg)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "when the `expects` macro is called multiple times" do
|
|
28
|
+
it "can collect expected keys " do
|
|
29
|
+
result = TestDoubles::MultipleExpectsAction.execute(
|
|
30
|
+
:tea => "black",
|
|
31
|
+
:milk => "full cream",
|
|
32
|
+
:chocolate => "dark chocolate"
|
|
33
|
+
)
|
|
34
|
+
expect(result[:milk_tea]).to \
|
|
35
|
+
eq("black - full cream - with dark chocolate")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "when a reserved key is listed as an expected key" do
|
|
40
|
+
it "raises an error indicating a reserved key is expected" do
|
|
41
|
+
exception_msg = "promised or expected keys cannot be a reserved key: " \
|
|
42
|
+
"[:message]"
|
|
43
|
+
expect do
|
|
44
|
+
TestDoubles::MakesTeaExpectingReservedKey.execute(:tea => "black",
|
|
45
|
+
:message => "no no")
|
|
46
|
+
end.to \
|
|
47
|
+
raise_error(FunctionalLightService::ReservedKeysInContextError, exception_msg)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "raises an error indicating that multiple reserved keys are expected" do
|
|
51
|
+
exception_msg = "promised or expected keys cannot be a reserved key: " \
|
|
52
|
+
"[:message, :error_code, :current_action]"
|
|
53
|
+
expect do
|
|
54
|
+
TestDoubles::MakesTeaExpectingMultipleReservedKeys
|
|
55
|
+
.execute(:tea => "black",
|
|
56
|
+
:message => "no no",
|
|
57
|
+
:error_code => 1,
|
|
58
|
+
:current_action => "update")
|
|
59
|
+
end.to raise_error(FunctionalLightService::ReservedKeysInContextError,
|
|
60
|
+
exception_msg)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ":expects and :promises macros" do
|
|
4
|
+
describe "actions are backward compatible" do
|
|
5
|
+
class FooAction
|
|
6
|
+
extend FunctionalLightService::Action
|
|
7
|
+
|
|
8
|
+
executed do |context|
|
|
9
|
+
baz = context.fetch :baz
|
|
10
|
+
|
|
11
|
+
bar = baz + 2
|
|
12
|
+
context[:bar] = bar
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
it "works without expects and promises" do
|
|
16
|
+
result = FooAction.execute(:baz => 3)
|
|
17
|
+
expect(result).to be_success
|
|
18
|
+
expect(result[:bar]).to eq(5)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "when expected keys are not in context" do
|
|
23
|
+
class FooNoExpectedKeyAction
|
|
24
|
+
extend FunctionalLightService::Action
|
|
25
|
+
expects :baz
|
|
26
|
+
|
|
27
|
+
executed do |context|
|
|
28
|
+
baz = context.fetch :baz
|
|
29
|
+
|
|
30
|
+
bar = baz + 2
|
|
31
|
+
context[:bar] = bar
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
it "throws an ExpectedKeysNotInContextError" do
|
|
35
|
+
# FooAction invoked with nothing in the context
|
|
36
|
+
expect { FooNoExpectedKeyAction.execute }.to \
|
|
37
|
+
raise_error(FunctionalLightService::ExpectedKeysNotInContextError)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "expected keys" do
|
|
42
|
+
class FooWithReaderAction
|
|
43
|
+
extend FunctionalLightService::Action
|
|
44
|
+
expects :baz
|
|
45
|
+
|
|
46
|
+
executed do |context|
|
|
47
|
+
# Notice how I use `context.baz` here
|
|
48
|
+
bar = context.baz + 2
|
|
49
|
+
context[:bar] = bar
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
it "can be accessed through a reader" do
|
|
53
|
+
result = FooWithReaderAction.execute(:baz => 3)
|
|
54
|
+
expect(result).to be_success
|
|
55
|
+
expect(result[:bar]).to eq(5)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "when promised keys are not in context" do
|
|
60
|
+
class FooNoPromisedKeyAction
|
|
61
|
+
extend FunctionalLightService::Action
|
|
62
|
+
expects :baz
|
|
63
|
+
promises :bar
|
|
64
|
+
|
|
65
|
+
executed do |context|
|
|
66
|
+
# I am not adding anything to the context
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
it "throws a PromisedKeysNotInContextError" do
|
|
70
|
+
# FooAction invoked with nothing placed in the context
|
|
71
|
+
expect { FooNoPromisedKeyAction.execute(:baz => 3) }.to \
|
|
72
|
+
raise_error(FunctionalLightService::PromisedKeysNotInContextError)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "promised keys" do
|
|
77
|
+
class FooWithExpectsAndPromisesAction
|
|
78
|
+
extend FunctionalLightService::Action
|
|
79
|
+
expects :baz
|
|
80
|
+
promises :bar
|
|
81
|
+
|
|
82
|
+
executed do |context|
|
|
83
|
+
# Notice how I use `context.bar` here
|
|
84
|
+
context.bar = context.baz + 2
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
it "puts the value through the accessor into the context" do
|
|
88
|
+
result = FooWithExpectsAndPromisesAction.execute(:baz => 3)
|
|
89
|
+
expect(result).to be_success
|
|
90
|
+
expect(result[:bar]).to eq(5)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|