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,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe 'Action before_actions' do
|
5
|
+
describe 'works with simple organizers - from outside' do
|
6
|
+
it 'can be used to inject code block before each action' do
|
7
|
+
TestDoubles::AdditionOrganizer.before_actions = ->(ctx) do
|
8
|
+
ctx.number -= 2 if ctx.current_action == TestDoubles::AddsThreeAction
|
9
|
+
end
|
10
|
+
|
11
|
+
result = TestDoubles::AdditionOrganizer.call(0)
|
12
|
+
|
13
|
+
expect(result.fetch(:number)).to eq(4)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'works with iterator' do
|
17
|
+
TestDoubles::TestIterate.before_actions = [
|
18
|
+
->(ctx) { ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction }
|
19
|
+
]
|
20
|
+
|
21
|
+
result = TestDoubles::TestIterate.call(:number => 0,
|
22
|
+
:counters => [1, 2, 3, 4])
|
23
|
+
|
24
|
+
expect(result).to be_success
|
25
|
+
expect(result.number).to eq(-4)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'can be added to organizers declaratively' do
|
30
|
+
module BeforeActions
|
31
|
+
class AdditionOrganizer
|
32
|
+
extend FunctionalLightService::Organizer
|
33
|
+
before_actions (->(ctx) do
|
34
|
+
ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
|
35
|
+
end),
|
36
|
+
(->(ctx) do
|
37
|
+
ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction
|
38
|
+
end)
|
39
|
+
|
40
|
+
def self.call(number)
|
41
|
+
with(:number => number).reduce(actions)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.actions
|
45
|
+
[
|
46
|
+
TestDoubles::AddsOneAction,
|
47
|
+
TestDoubles::AddsTwoAction,
|
48
|
+
TestDoubles::AddsThreeAction
|
49
|
+
]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'accepts before_actions hook lambdas from organizer' do
|
55
|
+
result = BeforeActions::AdditionOrganizer.call(0)
|
56
|
+
|
57
|
+
expect(result.fetch(:number)).to eq(1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'works with callbacks' do
|
62
|
+
it 'can interact with actions from the outside' do
|
63
|
+
TestDoubles::TestWithCallback.before_actions = [
|
64
|
+
->(ctx) { ctx.total -= 1000 if ctx.current_action == TestDoubles::AddToTotalAction }
|
65
|
+
]
|
66
|
+
result = TestDoubles::TestWithCallback.call
|
67
|
+
|
68
|
+
expect(result.counter).to eq(3)
|
69
|
+
expect(result.total).to eq(-2994)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'can halt all execution with a raised error' do
|
74
|
+
it 'does not call the rest of the callback steps' do
|
75
|
+
class SkipContextError < StandardError
|
76
|
+
attr_reader :ctx
|
77
|
+
|
78
|
+
def initialize(msg, ctx)
|
79
|
+
@ctx = ctx
|
80
|
+
super(msg)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
TestDoubles::TestWithCallback.before_actions = [
|
84
|
+
->(ctx) do
|
85
|
+
if ctx.current_action == TestDoubles::IncrementCountAction
|
86
|
+
ctx.total -= 1000
|
87
|
+
raise SkipContextError.new("stop context now", ctx)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
]
|
91
|
+
begin
|
92
|
+
TestDoubles::TestWithCallback.call
|
93
|
+
rescue SkipContextError => e
|
94
|
+
expect(e.ctx).not_to be_empty
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Log from an organizer with a custom logger" do
|
4
|
+
context "when overriding the global FunctionalLightService organizer" do
|
5
|
+
let(:global_logger_organizer) do
|
6
|
+
Class.new do
|
7
|
+
extend FunctionalLightService::Organizer
|
8
|
+
|
9
|
+
def self.call(number)
|
10
|
+
with(:number => number).reduce(actions)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.actions
|
14
|
+
[
|
15
|
+
TestDoubles::AddsOneAction,
|
16
|
+
TestDoubles::AddsTwoAction,
|
17
|
+
TestDoubles::AddsThreeAction
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:global_logger_string) { StringIO.new }
|
24
|
+
|
25
|
+
let(:custom_logger_string) { StringIO.new }
|
26
|
+
let(:custom_logger_organizer) do
|
27
|
+
custom_logger = Logger.new(custom_logger_string)
|
28
|
+
|
29
|
+
Class.new do
|
30
|
+
extend FunctionalLightService::Organizer
|
31
|
+
log_with custom_logger
|
32
|
+
|
33
|
+
def self.call(coffee, this_hot = :very_hot)
|
34
|
+
with(:milk => this_hot, :coffee => coffee)
|
35
|
+
.reduce(TestDoubles::MakesLatteAction,
|
36
|
+
TestDoubles::AddsTwoActionWithFetch)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
before do
|
42
|
+
@original_global_logger = FunctionalLightService::Configuration.logger
|
43
|
+
FunctionalLightService::Configuration.logger = Logger.new(global_logger_string)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "logs in own logger" do
|
47
|
+
global_logger_organizer.call(1)
|
48
|
+
custom_logger_organizer.call(:coffee => "Cappucino")
|
49
|
+
|
50
|
+
expect(custom_logger_string.string).to include("MakesLatteAction")
|
51
|
+
expect(custom_logger_string.string).to_not include("AddsOneAction")
|
52
|
+
expect(global_logger_string.string).to include("AddsOneAction")
|
53
|
+
expect(global_logger_string.string).to_not include("MakesLatteAction")
|
54
|
+
end
|
55
|
+
|
56
|
+
after do
|
57
|
+
FunctionalLightService::Configuration.logger = @original_global_logger
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "fail! returns immediately from executed block" do
|
4
|
+
class FailAction
|
5
|
+
extend FunctionalLightService::Action
|
6
|
+
promises :one, :two
|
7
|
+
|
8
|
+
executed do |ctx|
|
9
|
+
ctx.one = 1
|
10
|
+
# Have to set it in Context
|
11
|
+
ctx.two = nil
|
12
|
+
|
13
|
+
ctx.fail_and_return!('Something went wrong')
|
14
|
+
ctx.two = 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns immediately from executed block" do
|
19
|
+
result = FailAction.execute
|
20
|
+
|
21
|
+
expect(result).to be_failure
|
22
|
+
expect(result.two).to be_nil
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Including is discouraged" do
|
4
|
+
context "when including FunctionalLightService::Organizer" do
|
5
|
+
it "gives warning" do
|
6
|
+
expected_msg = "including FunctionalLightService::Organizer is deprecated. " \
|
7
|
+
"Please use `extend FunctionalLightService::Organizer` instead"
|
8
|
+
expect(ActiveSupport::Deprecation).to receive(:warn)
|
9
|
+
.with(expected_msg)
|
10
|
+
|
11
|
+
class OrganizerIncludingLS
|
12
|
+
include FunctionalLightService::Organizer
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when including FunctionalLightService::Action" do
|
18
|
+
it "gives warning" do
|
19
|
+
expected_msg = "including FunctionalLightService::Action is deprecated. " \
|
20
|
+
"Please use `extend FunctionalLightService::Action` instead"
|
21
|
+
expect(ActiveSupport::Deprecation).to receive(:warn)
|
22
|
+
.with(expected_msg)
|
23
|
+
|
24
|
+
class ActionIncludingLS
|
25
|
+
include FunctionalLightService::Action
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
describe "Logs from organizer" do
|
6
|
+
def collects_log
|
7
|
+
original_logger = FunctionalLightService::Configuration.logger
|
8
|
+
|
9
|
+
strio = StringIO.new
|
10
|
+
FunctionalLightService::Configuration.logger = Logger.new(strio)
|
11
|
+
|
12
|
+
yield
|
13
|
+
|
14
|
+
FunctionalLightService::Configuration.logger = original_logger
|
15
|
+
|
16
|
+
strio.string
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when every action has expects or promises" do
|
20
|
+
subject(:log_message) do
|
21
|
+
collects_log do
|
22
|
+
TestDoubles::MakesTeaAndCappuccino
|
23
|
+
.call("black tea", "2% milk", "espresso coffee")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "describes what organizer was invoked" do
|
28
|
+
organizer_log_message = "[FunctionalLightService] - calling organizer " \
|
29
|
+
"<TestDoubles::MakesTeaAndCappuccino>"
|
30
|
+
expect(log_message).to include(organizer_log_message)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "describes the actions invoked" do
|
34
|
+
organizer_log_message = "[FunctionalLightService] - executing " \
|
35
|
+
"<TestDoubles::MakesTeaWithMilkAction>"
|
36
|
+
expect(log_message).to include(organizer_log_message)
|
37
|
+
organizer_log_message = "[FunctionalLightService] - executing " \
|
38
|
+
"<TestDoubles::MakesLatteAction>"
|
39
|
+
expect(log_message).to include(organizer_log_message)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "lists the keys in context before the actions are executed" do
|
43
|
+
organizer_log_message = "[FunctionalLightService] - " \
|
44
|
+
"keys in context: :tea, :milk, :coffee"
|
45
|
+
expect(log_message).to include(organizer_log_message)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "lists the expects actions are expecting" do
|
49
|
+
organizer_log_message = "[FunctionalLightService] - expects: :tea, :milk"
|
50
|
+
expect(log_message).to include(organizer_log_message)
|
51
|
+
organizer_log_message = "[FunctionalLightService] - expects: :coffee, :milk"
|
52
|
+
expect(log_message).to include(organizer_log_message)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "lists the promises actions are promising" do
|
56
|
+
organizer_log_message = "[FunctionalLightService] - promises: :milk_tea"
|
57
|
+
expect(log_message).to include(organizer_log_message)
|
58
|
+
organizer_log_message = "[FunctionalLightService] - promises: :latte"
|
59
|
+
expect(log_message).to include(organizer_log_message)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "lists the keys in contect after the actions are executed" do
|
63
|
+
organizer_log_message = "[FunctionalLightService] - keys in context: " \
|
64
|
+
":tea, :milk, :coffee, :milk_tea, :latte"
|
65
|
+
expect(log_message).to include(organizer_log_message)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when NOT every action expects or promises" do
|
70
|
+
subject(:log_message) do
|
71
|
+
collects_log do
|
72
|
+
TestDoubles::MakesCappuccinoAddsTwo.call("2% milk", "espresso coffee")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "describes what organizer was invoked" do
|
77
|
+
organizer_log_message = "[FunctionalLightService] - calling organizer " \
|
78
|
+
"<TestDoubles::MakesCappuccinoAddsTwo>"
|
79
|
+
expect(log_message).to include(organizer_log_message)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "does not list empty expects or promises" do
|
83
|
+
organizer_log_message = "[FunctionalLightService] - expects:\n"
|
84
|
+
expect(log_message).not_to include(organizer_log_message)
|
85
|
+
organizer_log_message = "[FunctionalLightService] - promises:\n"
|
86
|
+
expect(log_message).not_to include(organizer_log_message)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when the context has failed" do
|
91
|
+
subject(:log_message) do
|
92
|
+
collects_log do
|
93
|
+
TestDoubles::MakesCappuccinoAddsTwoAndFails
|
94
|
+
.call("espresso coffee")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "logs it with a warning" do
|
99
|
+
organizer_log_message = "WARN -- : [FunctionalLightService] - :-((( " \
|
100
|
+
"<TestDoubles::MakesLatteAction> has failed..."
|
101
|
+
expect(log_message).to include(organizer_log_message)
|
102
|
+
organizer_log_message = "WARN -- : [FunctionalLightService] - context message: " \
|
103
|
+
"Can't make a latte from a milk that's very hot!"
|
104
|
+
expect(log_message).to include(organizer_log_message)
|
105
|
+
organizer_log_message = "[FunctionalLightService] - :-((( " \
|
106
|
+
"<TestDoubles::AddsTwoAction> has failed..."
|
107
|
+
expect(log_message).not_to include(organizer_log_message)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the context has failed with rollback" do
|
112
|
+
subject(:log_message) do
|
113
|
+
collects_log do
|
114
|
+
TestDoubles::MakesCappuccinoAddsTwoAndFails
|
115
|
+
.call("espresso coffee", :super_hot)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "logs it with a warning" do
|
120
|
+
organizer_log_message = "WARN -- : [FunctionalLightService] - :-((( " \
|
121
|
+
"<TestDoubles::MakesLatteAction> has failed..."
|
122
|
+
expect(log_message).to include(organizer_log_message)
|
123
|
+
organizer_log_message = "WARN -- : [FunctionalLightService] - context message: " \
|
124
|
+
"Can't make a latte from a milk that's super hot!"
|
125
|
+
expect(log_message).to include(organizer_log_message)
|
126
|
+
organizer_log_message = "[FunctionalLightService] - :-((( " \
|
127
|
+
"<TestDoubles::AddsTwoAction> " \
|
128
|
+
"has failed..."
|
129
|
+
expect(log_message).not_to include(organizer_log_message)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when the context is skipping the rest" do
|
134
|
+
subject(:log_message) do
|
135
|
+
collects_log do
|
136
|
+
TestDoubles::MakesCappuccinoSkipsAddsTwo.call("espresso coffee")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "logs it with a warning" do
|
141
|
+
organizer_log_message = "INFO -- : [FunctionalLightService] - ;-) " \
|
142
|
+
"<TestDoubles::MakesLatteAction> has decided " \
|
143
|
+
"to skip the rest of the actions"
|
144
|
+
expect(log_message).to include(organizer_log_message)
|
145
|
+
organizer_log_message = "INFO -- : [FunctionalLightService] - context message: " \
|
146
|
+
"Can't make a latte with a fatty milk like that!"
|
147
|
+
expect(log_message).to include(organizer_log_message)
|
148
|
+
organizer_log_message = "INFO -- : [FunctionalLightService] - ;-) " \
|
149
|
+
"<TestDoubles::AddsTwoAction> has decided " \
|
150
|
+
"to skip the rest of the actions"
|
151
|
+
expect(log_message).not_to include(organizer_log_message)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "test_doubles"
|
3
|
+
|
4
|
+
class TestsLocalizationAdapter
|
5
|
+
extend FunctionalLightService::Organizer
|
6
|
+
|
7
|
+
def self.call(pass_or_fail, message_or_key, i18n_options = {})
|
8
|
+
with(
|
9
|
+
:pass_or_fail => pass_or_fail,
|
10
|
+
:message_or_key => message_or_key,
|
11
|
+
:i18n_options => i18n_options
|
12
|
+
).reduce(TestsLocalizationInvocationOptionsAction)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestsLocalizationInvocationOptionsAction
|
17
|
+
extend FunctionalLightService::Action
|
18
|
+
expects :pass_or_fail, :message_or_key, :i18n_options
|
19
|
+
|
20
|
+
executed do |context|
|
21
|
+
if context.pass_or_fail == true
|
22
|
+
context.succeed!(context.message_or_key, context.i18n_options)
|
23
|
+
else
|
24
|
+
context.fail!(context.message_or_key, context.i18n_options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def pass_with(message_or_key, i18n_options = {})
|
30
|
+
TestsLocalizationAdapter.call(true, message_or_key, i18n_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fail_with(message_or_key, i18n_options = {})
|
34
|
+
TestsLocalizationAdapter.call(false, message_or_key, i18n_options)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Localization Adapter" do
|
38
|
+
before do
|
39
|
+
I18n.backend.store_translations(
|
40
|
+
:en,
|
41
|
+
:tests_localization_invocation_options_action =>
|
42
|
+
{
|
43
|
+
:light_service => {
|
44
|
+
:failures => {
|
45
|
+
:some_failure_reason => "This has failed",
|
46
|
+
:failure_with_interpolation => "Failed with %{reason}"
|
47
|
+
},
|
48
|
+
:successes => {
|
49
|
+
:some_success_reason => "This has passed",
|
50
|
+
:success_with_interpolation => "Passed with %{reason}"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "passing a simple string message" do
|
58
|
+
describe "by failing the context" do
|
59
|
+
it "returns the string" do
|
60
|
+
result = fail_with("string message")
|
61
|
+
|
62
|
+
expect(result).to be_failure
|
63
|
+
expect(result.message).to eq("string message")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "by passing the context" do
|
68
|
+
it "returns the string" do
|
69
|
+
result = pass_with("string message")
|
70
|
+
|
71
|
+
expect(result).to be_success
|
72
|
+
expect(result.message).to eq("string message")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "passing a Symbol" do
|
78
|
+
describe "by failing the context" do
|
79
|
+
it "performs a translation" do
|
80
|
+
result = fail_with(:some_failure_reason)
|
81
|
+
|
82
|
+
expect(result).to be_failure
|
83
|
+
expect(result.message).to eq("This has failed")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "by passing the contenxt" do
|
88
|
+
it "performs a translation" do
|
89
|
+
result = pass_with(:some_success_reason)
|
90
|
+
|
91
|
+
expect(result).to be_success
|
92
|
+
expect(result.message).to eq("This has passed")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "passing a Symbol with interpolation variables" do
|
98
|
+
describe "by failing the context" do
|
99
|
+
it "performs a translation with interpolation" do
|
100
|
+
result = fail_with(:failure_with_interpolation,
|
101
|
+
:reason => "bad account")
|
102
|
+
|
103
|
+
expect(result).to be_failure
|
104
|
+
expect(result.message).to eq("Failed with bad account")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "by passing the context" do
|
109
|
+
it "performs a translation with interpolation" do
|
110
|
+
result = pass_with(:success_with_interpolation,
|
111
|
+
:reason => "account in good standing")
|
112
|
+
|
113
|
+
expect(result).to be_success
|
114
|
+
expect(result.message).to eq("Passed with account in good standing")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
describe "Organizer should invoke with/reduce from a call method" do
|
5
|
+
context "when the organizer does not have a `call` method" do
|
6
|
+
it "gives warning" do
|
7
|
+
expect(ActiveSupport::Deprecation)
|
8
|
+
.to receive(:warn)
|
9
|
+
.with(/^The <OrganizerWithoutCallMethod> class is an organizer/)
|
10
|
+
|
11
|
+
class OrganizerWithoutCallMethod
|
12
|
+
extend FunctionalLightService::Organizer
|
13
|
+
|
14
|
+
def self.do_something
|
15
|
+
reduce([])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
OrganizerWithoutCallMethod.do_something
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when the organizer has the `call` method" do
|
24
|
+
it "does not issue a warning" do
|
25
|
+
expect(ActiveSupport::Deprecation)
|
26
|
+
.not_to receive(:warn)
|
27
|
+
|
28
|
+
class OrganizerWithCallMethod
|
29
|
+
extend FunctionalLightService::Organizer
|
30
|
+
|
31
|
+
def self.call
|
32
|
+
reduce([])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
OrganizerWithCallMethod.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
5
|
+
class TestReduceIfWithAroundEach
|
6
|
+
extend FunctionalLightService::Organizer
|
7
|
+
|
8
|
+
def self.call(context)
|
9
|
+
with(context)
|
10
|
+
.around_each(TestDoubles::AroundEachLoggerHandler)
|
11
|
+
.reduce(actions)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.actions
|
15
|
+
[
|
16
|
+
TestDoubles::AddsOneAction,
|
17
|
+
reduce_if(->(ctx) { ctx.number == 1 },
|
18
|
+
TestDoubles::AddsOneAction)
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can be used to log data' do
|
24
|
+
result =
|
25
|
+
TestReduceIfWithAroundEach
|
26
|
+
.call(:number => 0,
|
27
|
+
:logger => TestDoubles::TestLogger.new)
|
28
|
+
|
29
|
+
expect(result.fetch(:number)).to eq(2)
|
30
|
+
expect(result[:logger].logs).to eq(
|
31
|
+
[{
|
32
|
+
:action => TestDoubles::AddsOneAction,
|
33
|
+
:before => 0,
|
34
|
+
:after => 1
|
35
|
+
}, {
|
36
|
+
:action => TestDoubles::AddsOneAction,
|
37
|
+
:before => 1,
|
38
|
+
:after => 2
|
39
|
+
}]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
5
|
+
class TestSkipBefore
|
6
|
+
extend FunctionalLightService::Organizer
|
7
|
+
def self.call
|
8
|
+
with(:number => 1)
|
9
|
+
.reduce([
|
10
|
+
TestDoubles::SkipAllAction,
|
11
|
+
reduce_until(->(ctx) { ctx.number == 3 },
|
12
|
+
TestDoubles::AddsOneAction)
|
13
|
+
])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class TestSkipAfter
|
18
|
+
extend FunctionalLightService::Organizer
|
19
|
+
def self.call
|
20
|
+
with(:number => 1)
|
21
|
+
.reduce([
|
22
|
+
TestDoubles::AddsOneAction,
|
23
|
+
reduce_until(->(ctx) { ctx.number == 3 }, [
|
24
|
+
TestDoubles::AddsOneAction
|
25
|
+
]),
|
26
|
+
TestDoubles::SkipAllAction,
|
27
|
+
TestDoubles::AddsOneAction
|
28
|
+
])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestContextFailure
|
33
|
+
extend FunctionalLightService::Organizer
|
34
|
+
def self.call
|
35
|
+
with(:number => 1)
|
36
|
+
.reduce([
|
37
|
+
TestDoubles::FailureAction,
|
38
|
+
reduce_until(->(ctx) { ctx[:number] == 3 },
|
39
|
+
TestDoubles::AddsOneAction),
|
40
|
+
TestDoubles::AddsOneAction
|
41
|
+
])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'skips all the rest of the actions' do
|
46
|
+
result = TestSkipBefore.call
|
47
|
+
|
48
|
+
expect(result).to be_success
|
49
|
+
expect(result[:number]).to eq(1)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'skips after an action in nested context' do
|
53
|
+
result = TestSkipAfter.call
|
54
|
+
|
55
|
+
expect(result).to be_success
|
56
|
+
expect(result[:number]).to eq(3)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'respects failure across all nestings' do
|
60
|
+
result = TestContextFailure.call
|
61
|
+
|
62
|
+
expect(result).to be_failure
|
63
|
+
expect(result[:number]).to eq(1)
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe FunctionalLightService::Organizer do
|
5
|
+
class TestExecute
|
6
|
+
extend FunctionalLightService::Organizer
|
7
|
+
|
8
|
+
def self.call(context)
|
9
|
+
with(context).reduce(steps)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.steps
|
13
|
+
[
|
14
|
+
TestDoubles::AddsOneAction,
|
15
|
+
execute(->(ctx) { ctx.number += 1 }),
|
16
|
+
execute(->(ctx) { ctx[:something] = 'hello' }),
|
17
|
+
TestDoubles::AddsOne.actions
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:empty_context) { FunctionalLightService::Context.make }
|
23
|
+
|
24
|
+
it 'calls the lambda in the execute block using the context' do
|
25
|
+
result = TestExecute.call(:number => 0)
|
26
|
+
|
27
|
+
expect(result).to be_success
|
28
|
+
expect(result.number).to eq(3)
|
29
|
+
expect(result[:something]).to eq('hello')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'will not execute a failed context' do
|
33
|
+
empty_context.fail!('Something bad happened')
|
34
|
+
|
35
|
+
result = TestExecute.call(empty_context)
|
36
|
+
|
37
|
+
expect(result).to be_failure
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'does not execute over a skipped context' do
|
41
|
+
empty_context.skip_remaining!('No more needed')
|
42
|
+
|
43
|
+
result = TestExecute.call(empty_context)
|
44
|
+
expect(result).to be_success
|
45
|
+
end
|
46
|
+
end
|