functional-light-service 0.5.4 → 6.0.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 (75) 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 +38 -0
  6. data/README.md +54 -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/localization_adapter.rb +48 -47
  23. data/lib/functional-light-service/organizer/execute.rb +16 -14
  24. data/lib/functional-light-service/organizer/iterate.rb +30 -25
  25. data/lib/functional-light-service/organizer/reduce_if.rb +19 -17
  26. data/lib/functional-light-service/organizer/reduce_until.rb +22 -20
  27. data/lib/functional-light-service/organizer/scoped_reducable.rb +15 -13
  28. data/lib/functional-light-service/organizer/with_callback.rb +28 -26
  29. data/lib/functional-light-service/organizer/with_reducer.rb +81 -77
  30. data/lib/functional-light-service/organizer/with_reducer_factory.rb +20 -18
  31. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +110 -108
  32. data/lib/functional-light-service/organizer.rb +114 -114
  33. data/lib/functional-light-service/testing/context_factory.rb +48 -42
  34. data/lib/functional-light-service/testing.rb +3 -1
  35. data/lib/functional-light-service/version.rb +5 -3
  36. data/lib/functional-light-service.rb +30 -28
  37. data/spec/acceptance/after_actions_spec.rb +87 -71
  38. data/spec/acceptance/before_actions_spec.rb +115 -98
  39. data/spec/acceptance/custom_log_from_organizer_spec.rb +61 -60
  40. data/spec/acceptance/deprecation_warnings_spec.rb +82 -0
  41. data/spec/acceptance/fail_spec.rb +52 -50
  42. data/spec/acceptance/message_localization_spec.rb +119 -118
  43. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +68 -65
  44. data/spec/acceptance/organizer/reduce_if_spec.rb +89 -89
  45. data/spec/acceptance/organizer/with_callback_spec.rb +113 -110
  46. data/spec/acceptance/{not_having_call_method_warning_spec.rb → organizer_entry_point_spec.rb} +10 -7
  47. data/spec/acceptance/rollback_spec.rb +183 -132
  48. data/spec/action_expects_and_promises_spec.rb +97 -93
  49. data/spec/action_promised_keys_spec.rb +126 -122
  50. data/spec/context_spec.rb +289 -197
  51. data/spec/examples/controller_spec.rb +63 -63
  52. data/spec/examples/validate_address_spec.rb +38 -37
  53. data/spec/lib/deterministic/currify_spec.rb +90 -88
  54. data/spec/lib/deterministic/null_spec.rb +6 -1
  55. data/spec/lib/deterministic/option_spec.rb +140 -137
  56. data/spec/lib/deterministic/result/result_map_spec.rb +155 -154
  57. data/spec/lib/deterministic/result/result_shared.rb +3 -2
  58. data/spec/lib/deterministic/result_spec.rb +2 -2
  59. data/spec/lib/edge_cases_spec.rb +156 -0
  60. data/spec/lib/enum_spec.rb +1 -1
  61. data/spec/lib/native_pattern_matching_spec.rb +74 -0
  62. data/spec/organizer_spec.rb +115 -114
  63. data/spec/readme_spec.rb +45 -47
  64. data/spec/sample/calculates_order_tax_action_spec.rb +16 -16
  65. data/spec/sample/calculates_tax_spec.rb +1 -1
  66. data/spec/sample/looks_up_tax_percentage_action_spec.rb +55 -55
  67. data/spec/sample/tax/calculates_order_tax_action.rb +10 -9
  68. data/spec/sample/tax/looks_up_tax_percentage_action.rb +28 -27
  69. data/spec/sample/tax/provides_free_shipping_action.rb +11 -10
  70. data/spec/spec_helper.rb +6 -0
  71. data/spec/test_doubles.rb +628 -599
  72. data/spec/testing/context_factory_spec.rb +21 -0
  73. metadata +45 -161
  74. data/lib/functional-light-service/organizer/verify_call_method_exists.rb +0 -29
  75. data/spec/acceptance/include_warning_spec.rb +0 -29
@@ -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.0.0"
5
+ end
@@ -1,28 +1,30 @@
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/errors'
16
+ require 'functional-light-service/configuration'
17
+ require 'functional-light-service/localization_adapter'
18
+ require 'functional-light-service/context'
19
+ require 'functional-light-service/context/key_verifier'
20
+ require 'functional-light-service/organizer/scoped_reducable'
21
+ require 'functional-light-service/organizer/with_reducer'
22
+ require 'functional-light-service/organizer/with_reducer_log_decorator'
23
+ require 'functional-light-service/organizer/with_reducer_factory'
24
+ require 'functional-light-service/organizer/reduce_if'
25
+ require 'functional-light-service/organizer/reduce_until'
26
+ require 'functional-light-service/organizer/iterate'
27
+ require 'functional-light-service/organizer/execute'
28
+ require 'functional-light-service/organizer/with_callback'
29
+ require 'functional-light-service/action'
30
+ 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
@@ -1,98 +1,115 @@
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
1
+ require 'spec_helper'
2
+ require 'test_doubles'
3
+
4
+ RSpec.describe 'Action before_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.before_actions = nil
9
+ TestDoubles::TestIterate.before_actions = nil
10
+ TestDoubles::TestWithCallback.before_actions = nil
11
+ end
12
+
13
+ describe 'works with simple organizers - from outside' do
14
+ it 'can be used to inject code block before each action' do
15
+ TestDoubles::AdditionOrganizer.before_actions = ->(ctx) do
16
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsThreeAction
17
+ end
18
+
19
+ result = TestDoubles::AdditionOrganizer.call(0)
20
+
21
+ expect(result.fetch(:number)).to eq(4)
22
+ end
23
+
24
+ it 'works with iterator' do
25
+ TestDoubles::TestIterate.before_actions = [
26
+ ->(ctx) { ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction }
27
+ ]
28
+
29
+ result = TestDoubles::TestIterate.call(:number => 0,
30
+ :counters => [1, 2, 3, 4])
31
+
32
+ expect(result).to be_success
33
+ expect(result.number).to eq(-4)
34
+ end
35
+ end
36
+
37
+ describe 'can be added to organizers declaratively' do
38
+ module BeforeActions
39
+ class AdditionOrganizer
40
+ extend FunctionalLightService::Organizer
41
+
42
+ before_actions ->(ctx) do
43
+ ctx.number -= 2 if ctx.current_action == TestDoubles::AddsOneAction
44
+ end,
45
+ ->(ctx) do
46
+ ctx.number -= 3 if ctx.current_action == TestDoubles::AddsThreeAction
47
+ end
48
+
49
+ def self.call(number)
50
+ with(:number => number).reduce(actions)
51
+ end
52
+
53
+ def self.actions
54
+ [
55
+ TestDoubles::AddsOneAction,
56
+ TestDoubles::AddsTwoAction,
57
+ TestDoubles::AddsThreeAction
58
+ ]
59
+ end
60
+ end
61
+ end
62
+
63
+ it 'accepts before_actions hook lambdas from organizer' do
64
+ result = BeforeActions::AdditionOrganizer.call(0)
65
+
66
+ expect(result.fetch(:number)).to eq(1)
67
+ end
68
+
69
+ it 'keeps the declarative hooks on every subsequent call' do
70
+ first = BeforeActions::AdditionOrganizer.call(0)
71
+ second = BeforeActions::AdditionOrganizer.call(0)
72
+
73
+ expect(first.fetch(:number)).to eq(1)
74
+ expect(second.fetch(:number)).to eq(1)
75
+ end
76
+ end
77
+
78
+ describe 'works with callbacks' do
79
+ it 'can interact with actions from the outside' do
80
+ TestDoubles::TestWithCallback.before_actions = [
81
+ ->(ctx) { ctx.total -= 1000 if ctx.current_action == TestDoubles::AddToTotalAction }
82
+ ]
83
+ result = TestDoubles::TestWithCallback.call
84
+
85
+ expect(result.counter).to eq(3)
86
+ expect(result.total).to eq(-2994)
87
+ end
88
+ end
89
+
90
+ describe 'can halt all execution with a raised error' do
91
+ it 'does not call the rest of the callback steps' do
92
+ class SkipContextError < StandardError
93
+ attr_reader :ctx
94
+
95
+ def initialize(msg, ctx)
96
+ @ctx = ctx
97
+ super(msg)
98
+ end
99
+ end
100
+ TestDoubles::TestWithCallback.before_actions = [
101
+ ->(ctx) do
102
+ if ctx.current_action == TestDoubles::IncrementCountAction
103
+ ctx.total -= 1000
104
+ raise SkipContextError.new("stop context now", ctx)
105
+ end
106
+ end
107
+ ]
108
+ begin
109
+ TestDoubles::TestWithCallback.call
110
+ rescue SkipContextError => e
111
+ expect(e.ctx).not_to be_empty
112
+ end
113
+ end
114
+ end
115
+ end