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,17 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class ReduceIf
|
4
|
+
extend ScopedReducable
|
5
|
+
|
6
|
+
def self.run(organizer, condition_block, steps)
|
7
|
+
->(ctx) do
|
8
|
+
return ctx if ctx.stop_processing?
|
9
|
+
|
10
|
+
ctx = scoped_reduce(organizer, ctx, steps) if condition_block.call(ctx)
|
11
|
+
|
12
|
+
ctx
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class ReduceUntil
|
4
|
+
extend ScopedReducable
|
5
|
+
|
6
|
+
def self.run(organizer, condition_block, steps)
|
7
|
+
->(ctx) do
|
8
|
+
return ctx if ctx.stop_processing?
|
9
|
+
|
10
|
+
loop do
|
11
|
+
ctx = scoped_reduce(organizer, ctx, steps)
|
12
|
+
break if condition_block.call(ctx) || ctx.failure?
|
13
|
+
end
|
14
|
+
|
15
|
+
ctx
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
module ScopedReducable
|
4
|
+
def scoped_reduce(organizer, ctx, steps)
|
5
|
+
ctx.reset_skip_remaining! unless ctx.failure?
|
6
|
+
ctx = organizer.with(ctx).reduce([steps])
|
7
|
+
ctx.reset_skip_remaining! unless ctx.failure?
|
8
|
+
|
9
|
+
ctx
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
# We need to make sure existing users will
|
4
|
+
# use `call` method name going forward.
|
5
|
+
# This should be removed eventually.
|
6
|
+
class VerifyCallMethodExists
|
7
|
+
def self.run(klass, first_caller = '')
|
8
|
+
invoker_method = caller_method(first_caller)
|
9
|
+
return if invoker_method == 'call'
|
10
|
+
|
11
|
+
call_method_exists = klass.methods.include?(:call)
|
12
|
+
return if call_method_exists
|
13
|
+
|
14
|
+
warning_msg = "The <#{klass.name}> class is an organizer, " \
|
15
|
+
"its entry method (the one that calls with & reduce) " \
|
16
|
+
"should be named `call`. " \
|
17
|
+
"Please use #{klass}.call going forward."
|
18
|
+
ActiveSupport::Deprecation.warn(warning_msg)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.caller_method(first_caller)
|
22
|
+
return nil unless first_caller =~ /`(.*)'/
|
23
|
+
|
24
|
+
Regexp.last_match[1]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class WithCallback
|
4
|
+
extend ScopedReducable
|
5
|
+
|
6
|
+
def self.run(organizer, action, steps)
|
7
|
+
->(ctx) do
|
8
|
+
return ctx if ctx.stop_processing?
|
9
|
+
|
10
|
+
# This will only allow 2 level deep nesting of callbacks
|
11
|
+
previous_callback = ctx[:callback]
|
12
|
+
|
13
|
+
ctx[:callback] = ->(context) do
|
14
|
+
ctx = scoped_reduce(organizer, context, steps)
|
15
|
+
ctx
|
16
|
+
end
|
17
|
+
|
18
|
+
ctx = action.execute(ctx)
|
19
|
+
ctx[:callback] = previous_callback
|
20
|
+
|
21
|
+
ctx
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class WithReducer
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def with(data = {})
|
7
|
+
@context = FunctionalLightService::Context.make(data)
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def around_each(handler)
|
12
|
+
@around_each_handler = handler
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def around_each_handler
|
17
|
+
@around_each_handler ||= Class.new do
|
18
|
+
def self.call(_context)
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def reduce(*actions)
|
25
|
+
raise "No action(s) were provided" if actions.empty?
|
26
|
+
|
27
|
+
actions.flatten!
|
28
|
+
|
29
|
+
actions.each_with_object(context) do |action, current_context|
|
30
|
+
invoke_action(current_context, action)
|
31
|
+
rescue FailWithRollbackError
|
32
|
+
reduce_rollback(actions)
|
33
|
+
ensure
|
34
|
+
# For logging
|
35
|
+
yield(current_context, action) if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def reduce_rollback(actions)
|
40
|
+
reversable_actions(actions)
|
41
|
+
.reverse
|
42
|
+
.reduce(context) do |context, action|
|
43
|
+
if action.respond_to?(:rollback)
|
44
|
+
action.rollback(context)
|
45
|
+
else
|
46
|
+
context
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def invoke_action(current_context, action)
|
54
|
+
around_each_handler.call(current_context) do
|
55
|
+
if action.respond_to?(:call)
|
56
|
+
action.call(current_context)
|
57
|
+
else
|
58
|
+
action.execute(current_context)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def reversable_actions(actions)
|
64
|
+
index_of_current_action = actions.index(@context.current_action) || 0
|
65
|
+
|
66
|
+
# Reverse from the point where the fail was triggered
|
67
|
+
actions.take(index_of_current_action + 1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class WithReducerFactory
|
4
|
+
def self.make(monitored_organizer)
|
5
|
+
logger = monitored_organizer.logger || FunctionalLightService::Configuration.logger
|
6
|
+
decorated = WithReducer.new
|
7
|
+
|
8
|
+
return decorated if logger.nil?
|
9
|
+
|
10
|
+
WithReducerLogDecorator.new(
|
11
|
+
monitored_organizer,
|
12
|
+
:decorated => decorated,
|
13
|
+
:logger => logger
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Organizer
|
3
|
+
class WithReducerLogDecorator
|
4
|
+
attr_reader :logged, :logger, :decorated, :organizer
|
5
|
+
|
6
|
+
alias logged? logged
|
7
|
+
|
8
|
+
def initialize(organizer, decorated: WithReducer.new, logger:)
|
9
|
+
@decorated = decorated
|
10
|
+
@organizer = organizer
|
11
|
+
@logger = logger
|
12
|
+
@logged = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def with(data = {})
|
16
|
+
logger.info { "[FunctionalLightService] - calling organizer <#{organizer}>" }
|
17
|
+
|
18
|
+
decorated.with(data)
|
19
|
+
|
20
|
+
logger.info do
|
21
|
+
"[FunctionalLightService] - keys in context: " \
|
22
|
+
"#{extract_keys(decorated.context.keys)}"
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def around_each(handler)
|
28
|
+
decorated.around_each(handler)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def reduce(*actions)
|
33
|
+
decorated.reduce(*actions) do |context, action|
|
34
|
+
next context if logged?
|
35
|
+
|
36
|
+
if has_failure?(context)
|
37
|
+
write_failure_log(context, action)
|
38
|
+
next context
|
39
|
+
end
|
40
|
+
|
41
|
+
if skip_remaining?(context)
|
42
|
+
write_skip_remaining_log(context, action)
|
43
|
+
next context
|
44
|
+
end
|
45
|
+
|
46
|
+
write_log(action, context)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def write_log(action, context)
|
53
|
+
return unless logger.info?
|
54
|
+
|
55
|
+
logger.info("[FunctionalLightService] - executing <#{action}>")
|
56
|
+
log_expects(action)
|
57
|
+
log_promises(action)
|
58
|
+
logger.info("[FunctionalLightService] - keys in context: "\
|
59
|
+
"#{extract_keys(context.keys)}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def log_expects(action)
|
63
|
+
return unless defined?(action.expects) && action.expects.any?
|
64
|
+
|
65
|
+
logger.info("[FunctionalLightService] - expects: " \
|
66
|
+
"#{extract_keys(action.expects)}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_promises(action)
|
70
|
+
return unless defined?(action.promises) && action.promises.any?
|
71
|
+
|
72
|
+
logger.info("[FunctionalLightService] - promises: " \
|
73
|
+
"#{extract_keys(action.promises)}")
|
74
|
+
end
|
75
|
+
|
76
|
+
def extract_keys(keys)
|
77
|
+
keys.map { |key| ":#{key}" }.join(', ')
|
78
|
+
end
|
79
|
+
|
80
|
+
def has_failure?(context)
|
81
|
+
context.respond_to?(:failure?) && context.failure?
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_failure_log(context, action)
|
85
|
+
logger.warn("[FunctionalLightService] - :-((( <#{action}> has failed...")
|
86
|
+
logger.warn("[FunctionalLightService] - context message: #{context.message}")
|
87
|
+
@logged = true
|
88
|
+
end
|
89
|
+
|
90
|
+
def skip_remaining?(context)
|
91
|
+
context.respond_to?(:skip_remaining?) && context.skip_remaining?
|
92
|
+
end
|
93
|
+
|
94
|
+
def write_skip_remaining_log(context, action)
|
95
|
+
return unless logger.info?
|
96
|
+
|
97
|
+
msg = "[FunctionalLightService] - ;-) <#{action}> has decided " \
|
98
|
+
"to skip the rest of the actions"
|
99
|
+
logger.info(msg)
|
100
|
+
logger.info("[FunctionalLightService] - context message: #{context.message}")
|
101
|
+
@logged = true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
3
|
+
module FunctionalLightService
|
4
|
+
module Organizer
|
5
|
+
def self.extended(base_class)
|
6
|
+
base_class.extend ClassMethods
|
7
|
+
base_class.extend Macros
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(base_class)
|
11
|
+
warning_msg = "including FunctionalLightService::Organizer is deprecated. " \
|
12
|
+
"Please use `extend FunctionalLightService::Organizer` instead"
|
13
|
+
ActiveSupport::Deprecation.warn(warning_msg)
|
14
|
+
extended(base_class)
|
15
|
+
end
|
16
|
+
|
17
|
+
# In case this module is included
|
18
|
+
module ClassMethods
|
19
|
+
def with(data = {})
|
20
|
+
VerifyCallMethodExists.run(self, caller(1..1).first)
|
21
|
+
data[:_aliases] = @aliases if @aliases
|
22
|
+
|
23
|
+
if @before_actions
|
24
|
+
data[:_before_actions] = @before_actions.dup
|
25
|
+
@before_actions = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
if @after_actions
|
29
|
+
data[:_after_actions] = @after_actions.dup
|
30
|
+
@after_actions = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
WithReducerFactory.make(self).with(data)
|
34
|
+
end
|
35
|
+
|
36
|
+
def reduce(*actions)
|
37
|
+
with({}).reduce(actions)
|
38
|
+
end
|
39
|
+
|
40
|
+
def reduce_if(condition_block, steps)
|
41
|
+
ReduceIf.run(self, condition_block, steps)
|
42
|
+
end
|
43
|
+
|
44
|
+
def reduce_until(condition_block, steps)
|
45
|
+
ReduceUntil.run(self, condition_block, steps)
|
46
|
+
end
|
47
|
+
|
48
|
+
def iterate(collection_key, steps)
|
49
|
+
Iterate.run(self, collection_key, steps)
|
50
|
+
end
|
51
|
+
|
52
|
+
def execute(code_block)
|
53
|
+
Execute.run(code_block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def with_callback(action, steps)
|
57
|
+
WithCallback.run(self, action, steps)
|
58
|
+
end
|
59
|
+
|
60
|
+
def log_with(logger)
|
61
|
+
@logger = logger
|
62
|
+
end
|
63
|
+
|
64
|
+
def logger
|
65
|
+
@logger
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module Macros
|
70
|
+
def aliases(key_hash)
|
71
|
+
@aliases = key_hash
|
72
|
+
end
|
73
|
+
|
74
|
+
# This looks like an accessor,
|
75
|
+
# but it's used as a macro in the Organizer
|
76
|
+
def before_actions(*logic)
|
77
|
+
self.before_actions = logic
|
78
|
+
end
|
79
|
+
|
80
|
+
def before_actions=(logic)
|
81
|
+
@before_actions = [logic].flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
def append_before_actions(action)
|
85
|
+
@before_actions ||= []
|
86
|
+
@before_actions.push(action)
|
87
|
+
end
|
88
|
+
|
89
|
+
# This looks like an accessor,
|
90
|
+
# but it's used as a macro in the Organizer
|
91
|
+
def after_actions(*logic)
|
92
|
+
self.after_actions = logic
|
93
|
+
end
|
94
|
+
|
95
|
+
def after_actions=(logic)
|
96
|
+
@after_actions = [logic].flatten
|
97
|
+
end
|
98
|
+
|
99
|
+
def append_after_actions(action)
|
100
|
+
@after_actions ||= []
|
101
|
+
@after_actions.push(action)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FunctionalLightService
|
2
|
+
module Testing
|
3
|
+
class ContextFactory
|
4
|
+
attr_reader :organizer
|
5
|
+
|
6
|
+
def self.make_from(organizer)
|
7
|
+
new(organizer)
|
8
|
+
end
|
9
|
+
|
10
|
+
def for(action)
|
11
|
+
@organizer.append_before_actions(
|
12
|
+
->(ctx) do
|
13
|
+
if ctx.current_action == action
|
14
|
+
# The last `:_before_actions` hook is for
|
15
|
+
# ContextFactory, remove it, so it won't
|
16
|
+
# be invoked again
|
17
|
+
ctx[:_before_actions].pop
|
18
|
+
|
19
|
+
throw(:return_ctx_from_execution, ctx)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
)
|
23
|
+
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# More than one arguments can be passed to the
|
28
|
+
# Organizer's #call method
|
29
|
+
def with(*args, &block)
|
30
|
+
catch(:return_ctx_from_execution) do
|
31
|
+
@organizer.call(*args, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(organizer)
|
36
|
+
@organizer = organizer
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'functional-light-service/testing/context_factory'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
require 'functional-light-service/version'
|
5
|
+
|
6
|
+
module FunctionalLightService; end
|
7
|
+
|
8
|
+
require 'functional-light-service/functional/monad'
|
9
|
+
require 'functional-light-service/functional/enum'
|
10
|
+
require 'functional-light-service/functional/result'
|
11
|
+
require 'functional-light-service/functional/option'
|
12
|
+
require 'functional-light-service/functional/null'
|
13
|
+
require 'functional-light-service/errors'
|
14
|
+
require 'functional-light-service/configuration'
|
15
|
+
require 'functional-light-service/localization_adapter'
|
16
|
+
require 'functional-light-service/context'
|
17
|
+
require 'functional-light-service/context/key_verifier'
|
18
|
+
require 'functional-light-service/organizer/scoped_reducable'
|
19
|
+
require 'functional-light-service/organizer/with_reducer'
|
20
|
+
require 'functional-light-service/organizer/with_reducer_log_decorator'
|
21
|
+
require 'functional-light-service/organizer/with_reducer_factory'
|
22
|
+
require 'functional-light-service/organizer/reduce_if'
|
23
|
+
require 'functional-light-service/organizer/reduce_until'
|
24
|
+
require 'functional-light-service/organizer/iterate'
|
25
|
+
require 'functional-light-service/organizer/execute'
|
26
|
+
require 'functional-light-service/organizer/with_callback'
|
27
|
+
require 'functional-light-service/organizer/verify_call_method_exists'
|
28
|
+
require 'functional-light-service/action'
|
29
|
+
require 'functional-light-service/organizer'
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe TestDoubles::AdditionOrganizer do
|
5
|
+
it 'Adds 1, 2 and 3 to the initial value of 1' do
|
6
|
+
result = TestDoubles::AdditionOrganizer.call(1)
|
7
|
+
number = result.fetch(:number)
|
8
|
+
|
9
|
+
expect(number).to eq(7)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
RSpec.describe 'Action after_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.after_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.after_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 AfterActions
|
31
|
+
class AdditionOrganizer
|
32
|
+
extend FunctionalLightService::Organizer
|
33
|
+
after_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 after_actions hook lambdas from organizer' do
|
55
|
+
result = AfterActions::AdditionOrganizer.call(0)
|
56
|
+
|
57
|
+
expect(result.fetch(:number)).to eq(1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'after_actions can be appended' do
|
62
|
+
it 'adds to the :_after_actions collection' do
|
63
|
+
TestDoubles::AdditionOrganizer.append_after_actions(
|
64
|
+
->(ctx) { ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction }
|
65
|
+
)
|
66
|
+
|
67
|
+
result = TestDoubles::AdditionOrganizer.call(0)
|
68
|
+
expect(result.fetch(:number)).to eq(3)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_doubles'
|
3
|
+
|
4
|
+
describe 'Executing arbitrary code around each action' do
|
5
|
+
it 'can be used to log data' do
|
6
|
+
context = { :number => 0, :logger => TestDoubles::TestLogger.new }
|
7
|
+
|
8
|
+
result = TestDoubles::AroundEachOrganizer.call(context)
|
9
|
+
|
10
|
+
expect(result.fetch(:number)).to eq(2)
|
11
|
+
expect(result[:logger].logs).to eq(
|
12
|
+
[{
|
13
|
+
:action => TestDoubles::AddsTwoActionWithFetch,
|
14
|
+
:before => 0,
|
15
|
+
:after => 2
|
16
|
+
}]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|