functional-light-service 0.5.4 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/project-build.yml +35 -11
  3. data/.rubocop.yml +101 -160
  4. data/AUDIT-functional-light-service.md +352 -0
  5. data/CHANGELOG.md +45 -0
  6. data/README.md +88 -2
  7. data/audit/bench.rb +99 -0
  8. data/audit/verify_findings.rb +172 -0
  9. data/functional-light-service.gemspec +15 -21
  10. data/lib/functional-light-service/action.rb +97 -101
  11. data/lib/functional-light-service/configuration.rb +26 -24
  12. data/lib/functional-light-service/context/key_verifier.rb +124 -118
  13. data/lib/functional-light-service/context.rb +63 -20
  14. data/lib/functional-light-service/deprecations.rb +26 -0
  15. data/lib/functional-light-service/errors.rb +8 -6
  16. data/lib/functional-light-service/functional/enum.rb +286 -250
  17. data/lib/functional-light-service/functional/maybe.rb +21 -15
  18. data/lib/functional-light-service/functional/monad.rb +77 -66
  19. data/lib/functional-light-service/functional/null.rb +88 -74
  20. data/lib/functional-light-service/functional/option.rb +100 -97
  21. data/lib/functional-light-service/functional/result.rb +129 -116
  22. data/lib/functional-light-service/functional/sequencer.rb +144 -0
  23. data/lib/functional-light-service/localization_adapter.rb +48 -47
  24. data/lib/functional-light-service/organizer/execute.rb +16 -14
  25. data/lib/functional-light-service/organizer/iterate.rb +30 -25
  26. data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
  27. data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
  28. data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
  29. data/lib/functional-light-service/organizer/with_callback.rb +28 -26
  30. data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
  31. data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
  32. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
  33. data/lib/functional-light-service/organizer.rb +114 -114
  34. data/lib/functional-light-service/testing/context_factory.rb +48 -42
  35. data/lib/functional-light-service/testing.rb +3 -1
  36. data/lib/functional-light-service/version.rb +5 -3
  37. data/lib/functional-light-service.rb +31 -28
  38. data/spec/acceptance/after_actions_spec.rb +87 -71
  39. data/spec/acceptance/before_actions_spec.rb +115 -98
  40. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
  41. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  42. data/spec/acceptance/fail_spec.rb +52 -50
  43. data/spec/acceptance/message_localization_spec.rb +119 -118
  44. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
  45. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
  46. data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
  47. data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
  48. data/spec/acceptance/rollback_spec.rb +183 -132
  49. data/spec/action_expects_and_promises_spec.rb +97 -93
  50. data/spec/action_promised_keys_spec.rb +126 -122
  51. data/spec/context_spec.rb +289 -197
  52. data/spec/examples/controller_spec.rb +63 -63
  53. data/spec/examples/validate_address_spec.rb +38 -37
  54. data/spec/lib/deterministic/currify_spec.rb +90 -88
  55. data/spec/lib/deterministic/null_spec.rb +6 -1
  56. data/spec/lib/deterministic/option_spec.rb +140 -137
  57. data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
  58. data/spec/lib/deterministic/result/result_shared.rb +3 -2
  59. data/spec/lib/deterministic/result_spec.rb +2 -2
  60. data/spec/lib/deterministic/sequencer_spec.rb +506 -0
  61. data/spec/lib/edge_cases_spec.rb +156 -0
  62. data/spec/lib/enum_spec.rb +1 -1
  63. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  64. data/spec/organizer_spec.rb +115 -114
  65. data/spec/readme_spec.rb +45 -47
  66. data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
  67. data/spec/sample/calculates_tax_spec.rb +1 -1
  68. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
  69. data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
  70. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
  71. data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
  72. data/spec/spec_helper.rb +6 -0
  73. data/spec/test_doubles.rb +628 -599
  74. data/spec/testing/context_factory_spec.rb +21 -0
  75. metadata +48 -162
  76. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
  77. data/spec/acceptance/include_warning_spec.rb +0 -29
@@ -1,114 +1,114 @@
1
- module FunctionalLightService
2
- module Organizer
3
- def self.extended(base_class)
4
- base_class.extend ClassMethods
5
- base_class.extend Macros
6
- end
7
-
8
- def self.included(base_class)
9
- msg = "DEPRECATION WARNING:\n" \
10
- "Including FunctionalLightService::Organizer is deprecated\n" \
11
- "Please use `extend FunctionalLightService::Organizer` instead"
12
- print msg
13
- extended(base_class)
14
- end
15
-
16
- # In case this module is included
17
- module ClassMethods
18
- def with(data = {})
19
- VerifyCallMethodExists.run(self, caller(1..1).first)
20
- data[:_aliases] = @aliases if @aliases
21
-
22
- if @before_actions
23
- data[:_before_actions] = @before_actions.dup
24
- @before_actions = nil
25
- end
26
-
27
- if @after_actions
28
- data[:_after_actions] = @after_actions.dup
29
- @after_actions = nil
30
- end
31
-
32
- WithReducerFactory.make(self).with(data)
33
- end
34
-
35
- def reduce(*actions)
36
- with({}).reduce(actions)
37
- end
38
-
39
- def reduce_if(condition_block, steps)
40
- ReduceIf.run(self, condition_block, steps)
41
- end
42
-
43
- def reduce_until(condition_block, steps)
44
- ReduceUntil.run(self, condition_block, steps)
45
- end
46
-
47
- def iterate(collection_key, steps)
48
- Iterate.run(self, collection_key, steps)
49
- end
50
-
51
- def execute(code_block)
52
- Execute.run(code_block)
53
- end
54
-
55
- def with_callback(action, steps)
56
- WithCallback.run(self, action, steps)
57
- end
58
-
59
- def log_with(logger)
60
- @logger = logger
61
- end
62
-
63
- def logger
64
- @logger
65
- end
66
-
67
- def add_to_context(**args)
68
- args.map do |key, value|
69
- execute(->(ctx) { ctx[key.to_sym] = value })
70
- end
71
- end
72
-
73
- def add_aliases(args)
74
- execute(->(ctx) { ctx.assign_aliases(ctx.aliases.merge(args)) })
75
- end
76
- end
77
-
78
- module Macros
79
- def aliases(key_hash)
80
- @aliases = key_hash
81
- end
82
-
83
- # This looks like an accessor,
84
- # but it's used as a macro in the Organizer
85
- def before_actions(*logic)
86
- self.before_actions = logic
87
- end
88
-
89
- def before_actions=(logic)
90
- @before_actions = [logic].flatten
91
- end
92
-
93
- def append_before_actions(action)
94
- @before_actions ||= []
95
- @before_actions.push(action)
96
- end
97
-
98
- # This looks like an accessor,
99
- # but it's used as a macro in the Organizer
100
- def after_actions(*logic)
101
- self.after_actions = logic
102
- end
103
-
104
- def after_actions=(logic)
105
- @after_actions = [logic].flatten
106
- end
107
-
108
- def append_after_actions(action)
109
- @after_actions ||= []
110
- @after_actions.push(action)
111
- end
112
- end
113
- end
114
- end
1
+ # frozen_string_literal: true
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
+ FunctionalLightService::Deprecations.warn(
12
+ "Including FunctionalLightService::Organizer is deprecated; " \
13
+ "use `extend FunctionalLightService::Organizer` instead"
14
+ )
15
+ extended(base_class)
16
+ end
17
+
18
+ # In case this module is included
19
+ module ClassMethods
20
+ def with(data = {})
21
+ data[:_aliases] = @aliases if @aliases
22
+
23
+ # Gli hook di classe vengono solo letti (mai azzerati): devono valere
24
+ # per ogni chiamata, anche concorrente
25
+ data[:_before_actions] = @before_actions.dup if @before_actions
26
+ data[:_after_actions] = @after_actions.dup if @after_actions
27
+
28
+ WithReducerFactory.make(self).with(data)
29
+ end
30
+
31
+ def reduce(*actions)
32
+ with({}).reduce(actions)
33
+ end
34
+
35
+ def reduce_if(condition_block, steps)
36
+ ReduceIf.run(self, condition_block, steps)
37
+ end
38
+
39
+ def reduce_until(condition_block, steps)
40
+ ReduceUntil.run(self, condition_block, steps)
41
+ end
42
+
43
+ def iterate(collection_key, steps)
44
+ Iterate.run(self, collection_key, steps)
45
+ end
46
+
47
+ def execute(code_block)
48
+ Execute.run(code_block)
49
+ end
50
+
51
+ def with_callback(action, steps)
52
+ WithCallback.run(self, action, steps)
53
+ end
54
+
55
+ def log_with(logger)
56
+ @logger = logger
57
+ end
58
+
59
+ def logger
60
+ @logger
61
+ end
62
+
63
+ def add_to_context(**args)
64
+ args.map do |key, value|
65
+ execute(->(ctx) { ctx[key.to_sym] = value })
66
+ end
67
+ end
68
+
69
+ def add_aliases(args)
70
+ execute(->(ctx) { ctx.assign_aliases(ctx.aliases.merge(args)) })
71
+ end
72
+ end
73
+
74
+ module Macros
75
+ def aliases(key_hash)
76
+ @aliases = key_hash
77
+ end
78
+
79
+ # This looks like an accessor,
80
+ # but it's used as a macro in the Organizer
81
+ def before_actions(*logic)
82
+ self.before_actions = logic
83
+ end
84
+
85
+ def before_actions=(logic)
86
+ @before_actions = logic.nil? ? nil : [logic].flatten
87
+ end
88
+
89
+ def append_before_actions(action)
90
+ @before_actions ||= []
91
+ @before_actions.push(action)
92
+ end
93
+
94
+ def remove_before_actions(action)
95
+ @before_actions&.delete(action)
96
+ end
97
+
98
+ # This looks like an accessor,
99
+ # but it's used as a macro in the Organizer
100
+ def after_actions(*logic)
101
+ self.after_actions = logic
102
+ end
103
+
104
+ def after_actions=(logic)
105
+ @after_actions = logic.nil? ? nil : [logic].flatten
106
+ end
107
+
108
+ def append_after_actions(action)
109
+ @after_actions ||= []
110
+ @after_actions.push(action)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,42 +1,48 @@
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
- # rubocop:disable Style/ArgumentsForwarding
30
- def with(*args, &block)
31
- catch(:return_ctx_from_execution) do
32
- @organizer.call(*args, &block)
33
- end
34
- end
35
- # rubocop:enable Style/ArgumentsForwarding
36
-
37
- def initialize(organizer)
38
- @organizer = organizer
39
- end
40
- end
41
- end
42
- end
1
+ # frozen_string_literal: true
2
+
3
+ module FunctionalLightService
4
+ module Testing
5
+ class ContextFactory
6
+ attr_reader :organizer
7
+
8
+ def self.make_from(organizer)
9
+ new(organizer)
10
+ end
11
+
12
+ def for(action)
13
+ @target_action = action
14
+ self
15
+ end
16
+
17
+ # More than one arguments can be passed to the
18
+ # Organizer's #call method
19
+ def with(...)
20
+ hook = nil
21
+ hook = ->(ctx) do
22
+ if ctx.current_action == @target_action
23
+ # L'hook non deve essere re-invocato quando il context
24
+ # verra' usato con Action#execute nel test
25
+ ctx[:_before_actions].delete(hook)
26
+
27
+ throw(:return_ctx_from_execution, ctx)
28
+ end
29
+ end
30
+
31
+ @organizer.append_before_actions(hook)
32
+
33
+ begin
34
+ catch(:return_ctx_from_execution) do
35
+ @organizer.call(...)
36
+ end
37
+ ensure
38
+ # L'hook e' per-chiamata: la classe organizer non deve conservarlo
39
+ @organizer.remove_before_actions(hook)
40
+ end
41
+ end
42
+
43
+ def initialize(organizer)
44
+ @organizer = organizer
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1 +1,3 @@
1
- require 'functional-light-service/testing/context_factory'
1
+ # frozen_string_literal: true
2
+
3
+ require 'functional-light-service/testing/context_factory'
@@ -1,3 +1,5 @@
1
- module FunctionalLightService
2
- VERSION = "0.5.4".freeze
3
- end
1
+ # frozen_string_literal: true
2
+
3
+ module FunctionalLightService
4
+ VERSION = "6.1.0"
5
+ end
@@ -1,28 +1,31 @@
1
- require 'logger'
2
-
3
- require 'functional-light-service/version'
4
-
5
- module FunctionalLightService; end
6
-
7
- require 'functional-light-service/functional/monad'
8
- require 'functional-light-service/functional/enum'
9
- require 'functional-light-service/functional/result'
10
- require 'functional-light-service/functional/option'
11
- require 'functional-light-service/functional/null'
12
- require 'functional-light-service/errors'
13
- require 'functional-light-service/configuration'
14
- require 'functional-light-service/localization_adapter'
15
- require 'functional-light-service/context'
16
- require 'functional-light-service/context/key_verifier'
17
- require 'functional-light-service/organizer/scoped_reducable'
18
- require 'functional-light-service/organizer/with_reducer'
19
- require 'functional-light-service/organizer/with_reducer_log_decorator'
20
- require 'functional-light-service/organizer/with_reducer_factory'
21
- require 'functional-light-service/organizer/reduce_if'
22
- require 'functional-light-service/organizer/reduce_until'
23
- require 'functional-light-service/organizer/iterate'
24
- require 'functional-light-service/organizer/execute'
25
- require 'functional-light-service/organizer/with_callback'
26
- require 'functional-light-service/organizer/verify_call_method_exists'
27
- require 'functional-light-service/action'
28
- require 'functional-light-service/organizer'
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ require 'functional-light-service/version'
6
+
7
+ module FunctionalLightService; end
8
+
9
+ require 'functional-light-service/deprecations'
10
+ require 'functional-light-service/functional/monad'
11
+ require 'functional-light-service/functional/enum'
12
+ require 'functional-light-service/functional/result'
13
+ require 'functional-light-service/functional/option'
14
+ require 'functional-light-service/functional/null'
15
+ require 'functional-light-service/functional/sequencer'
16
+ require 'functional-light-service/errors'
17
+ require 'functional-light-service/configuration'
18
+ require 'functional-light-service/localization_adapter'
19
+ require 'functional-light-service/context'
20
+ require 'functional-light-service/context/key_verifier'
21
+ require 'functional-light-service/organizer/scoped_reducable'
22
+ require 'functional-light-service/organizer/with_reducer'
23
+ require 'functional-light-service/organizer/with_reducer_log_decorator'
24
+ require 'functional-light-service/organizer/with_reducer_factory'
25
+ require 'functional-light-service/organizer/reduce_if'
26
+ require 'functional-light-service/organizer/reduce_until'
27
+ require 'functional-light-service/organizer/iterate'
28
+ require 'functional-light-service/organizer/execute'
29
+ require 'functional-light-service/organizer/with_callback'
30
+ require 'functional-light-service/action'
31
+ require 'functional-light-service/organizer'
@@ -1,71 +1,87 @@
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
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'Action after_actions' do
5
+ # Gli hook restano sulla classe (non vengono piu consumati da #with):
6
+ # i test double sono condivisi, quindi vanno ripuliti dopo ogni esempio
7
+ after do
8
+ TestDoubles::AdditionOrganizer.after_actions = nil
9
+ TestDoubles::TestIterate.after_actions = nil
10
+ end
11
+
12
+ describe 'works with simple organizers - from outside' do
13
+ it 'can be used to inject code block before each action' do
14
+ TestDoubles::AdditionOrganizer.after_actions = ->(ctx) do
15
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsThreeAction
16
+ end
17
+
18
+ result = TestDoubles::AdditionOrganizer.call(0)
19
+
20
+ expect(result.fetch(:number)).to eq(4)
21
+ end
22
+
23
+ it 'works with iterator' do
24
+ TestDoubles::TestIterate.after_actions = [
25
+ ->(ctx) { ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction }
26
+ ]
27
+
28
+ result = TestDoubles::TestIterate.call(:number => 0,
29
+ :counters => [1, 2, 3, 4])
30
+
31
+ expect(result).to be_success
32
+ expect(result.number).to eq(-4)
33
+ end
34
+ end
35
+
36
+ describe 'can be added to organizers declaratively' do
37
+ module AfterActions
38
+ class AdditionOrganizer
39
+ extend FunctionalLightService::Organizer
40
+
41
+ after_actions ->(ctx) do
42
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
43
+ end,
44
+ ->(ctx) do
45
+ ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction
46
+ end
47
+
48
+ def self.call(number)
49
+ with(:number => number).reduce(actions)
50
+ end
51
+
52
+ def self.actions
53
+ [
54
+ TestDoubles::AddsOneAction,
55
+ TestDoubles::AddsTwoAction,
56
+ TestDoubles::AddsThreeAction
57
+ ]
58
+ end
59
+ end
60
+ end
61
+
62
+ it 'accepts after_actions hook lambdas from organizer' do
63
+ result = AfterActions::AdditionOrganizer.call(0)
64
+
65
+ expect(result.fetch(:number)).to eq(1)
66
+ end
67
+
68
+ it 'keeps the declarative hooks on every subsequent call' do
69
+ first = AfterActions::AdditionOrganizer.call(0)
70
+ second = AfterActions::AdditionOrganizer.call(0)
71
+
72
+ expect(first.fetch(:number)).to eq(1)
73
+ expect(second.fetch(:number)).to eq(1)
74
+ end
75
+ end
76
+
77
+ describe 'after_actions can be appended' do
78
+ it 'adds to the :_after_actions collection' do
79
+ TestDoubles::AdditionOrganizer.append_after_actions(
80
+ ->(ctx) { ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction }
81
+ )
82
+
83
+ result = TestDoubles::AdditionOrganizer.call(0)
84
+ expect(result.fetch(:number)).to eq(3)
85
+ end
86
+ end
87
+ end