functional-light-service 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|