functional-light-service 6.1.0 → 6.3.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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/lib/functional-light-service.rb +6 -31
  3. metadata +15 -297
  4. data/.github/workflows/project-build.yml +0 -71
  5. data/.gitignore +0 -22
  6. data/.rspec +0 -3
  7. data/.rubocop.yml +0 -101
  8. data/.solargraph.yml +0 -11
  9. data/AUDIT-functional-light-service.md +0 -352
  10. data/Appraisals +0 -7
  11. data/CHANGELOG.md +0 -273
  12. data/CODE_OF_CONDUCT.md +0 -22
  13. data/Gemfile +0 -4
  14. data/LICENSE +0 -22
  15. data/README.md +0 -1578
  16. data/Rakefile +0 -12
  17. data/VERSION +0 -1
  18. data/audit/bench.rb +0 -99
  19. data/audit/verify_findings.rb +0 -172
  20. data/functional-light-service.gemspec +0 -33
  21. data/gemfiles/dry_inflector_0_2_1.gemfile +0 -5
  22. data/gemfiles/i18n_1_8_11.gemfile +0 -5
  23. data/lib/functional-light-service/action.rb +0 -97
  24. data/lib/functional-light-service/configuration.rb +0 -26
  25. data/lib/functional-light-service/context/key_verifier.rb +0 -124
  26. data/lib/functional-light-service/context.rb +0 -195
  27. data/lib/functional-light-service/deprecations.rb +0 -26
  28. data/lib/functional-light-service/errors.rb +0 -8
  29. data/lib/functional-light-service/functional/enum.rb +0 -286
  30. data/lib/functional-light-service/functional/maybe.rb +0 -21
  31. data/lib/functional-light-service/functional/monad.rb +0 -77
  32. data/lib/functional-light-service/functional/null.rb +0 -88
  33. data/lib/functional-light-service/functional/option.rb +0 -100
  34. data/lib/functional-light-service/functional/result.rb +0 -129
  35. data/lib/functional-light-service/functional/sequencer.rb +0 -144
  36. data/lib/functional-light-service/localization_adapter.rb +0 -48
  37. data/lib/functional-light-service/organizer/execute.rb +0 -16
  38. data/lib/functional-light-service/organizer/iterate.rb +0 -30
  39. data/lib/functional-light-service/organizer/reduce_if.rb +0 -19
  40. data/lib/functional-light-service/organizer/reduce_until.rb +0 -22
  41. data/lib/functional-light-service/organizer/scoped_reducable.rb +0 -15
  42. data/lib/functional-light-service/organizer/with_callback.rb +0 -28
  43. data/lib/functional-light-service/organizer/with_reducer.rb +0 -81
  44. data/lib/functional-light-service/organizer/with_reducer_factory.rb +0 -20
  45. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +0 -110
  46. data/lib/functional-light-service/organizer.rb +0 -114
  47. data/lib/functional-light-service/testing/context_factory.rb +0 -48
  48. data/lib/functional-light-service/testing.rb +0 -3
  49. data/lib/functional-light-service/version.rb +0 -5
  50. data/resources/fail_actions.png +0 -0
  51. data/resources/light-service.png +0 -0
  52. data/resources/organizer_and_actions.png +0 -0
  53. data/resources/skip_actions.png +0 -0
  54. data/spec/acceptance/add_numbers_spec.rb +0 -11
  55. data/spec/acceptance/after_actions_spec.rb +0 -87
  56. data/spec/acceptance/around_each_spec.rb +0 -19
  57. data/spec/acceptance/before_actions_spec.rb +0 -115
  58. data/spec/acceptance/custom_log_from_organizer_spec.rb +0 -61
  59. data/spec/acceptance/deprecation_warnings_spec.rb +0 -82
  60. data/spec/acceptance/fail_spec.rb +0 -52
  61. data/spec/acceptance/log_from_organizer_spec.rb +0 -154
  62. data/spec/acceptance/message_localization_spec.rb +0 -119
  63. data/spec/acceptance/organizer/add_aliases_spec.rb +0 -28
  64. data/spec/acceptance/organizer/add_to_context_spec.rb +0 -30
  65. data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +0 -42
  66. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +0 -68
  67. data/spec/acceptance/organizer/execute_spec.rb +0 -46
  68. data/spec/acceptance/organizer/iterate_spec.rb +0 -44
  69. data/spec/acceptance/organizer/reduce_if_spec.rb +0 -89
  70. data/spec/acceptance/organizer/reduce_until_spec.rb +0 -49
  71. data/spec/acceptance/organizer/with_callback_spec.rb +0 -113
  72. data/spec/acceptance/organizer_entry_point_spec.rb +0 -35
  73. data/spec/acceptance/rollback_spec.rb +0 -183
  74. data/spec/acceptance/testing/context_factory_spec.rb +0 -54
  75. data/spec/action_expected_keys_spec.rb +0 -63
  76. data/spec/action_expects_and_promises_spec.rb +0 -97
  77. data/spec/action_promised_keys_spec.rb +0 -126
  78. data/spec/action_spec.rb +0 -97
  79. data/spec/context/inspect_spec.rb +0 -42
  80. data/spec/context_spec.rb +0 -289
  81. data/spec/examples/amount_spec.rb +0 -77
  82. data/spec/examples/controller_spec.rb +0 -63
  83. data/spec/examples/validate_address_spec.rb +0 -38
  84. data/spec/lib/deterministic/class_mixin_spec.rb +0 -24
  85. data/spec/lib/deterministic/currify_spec.rb +0 -90
  86. data/spec/lib/deterministic/monad_axioms.rb +0 -46
  87. data/spec/lib/deterministic/monad_spec.rb +0 -47
  88. data/spec/lib/deterministic/null_spec.rb +0 -65
  89. data/spec/lib/deterministic/option_spec.rb +0 -140
  90. data/spec/lib/deterministic/result/failure_spec.rb +0 -65
  91. data/spec/lib/deterministic/result/result_map_spec.rb +0 -155
  92. data/spec/lib/deterministic/result/result_shared.rb +0 -25
  93. data/spec/lib/deterministic/result/success_spec.rb +0 -41
  94. data/spec/lib/deterministic/result_spec.rb +0 -63
  95. data/spec/lib/deterministic/sequencer_spec.rb +0 -506
  96. data/spec/lib/edge_cases_spec.rb +0 -156
  97. data/spec/lib/enum_spec.rb +0 -114
  98. data/spec/lib/native_pattern_matching_spec.rb +0 -74
  99. data/spec/localization_adapter_spec.rb +0 -83
  100. data/spec/organizer/with_reducer_spec.rb +0 -56
  101. data/spec/organizer_key_aliases_spec.rb +0 -29
  102. data/spec/organizer_spec.rb +0 -115
  103. data/spec/readme_spec.rb +0 -45
  104. data/spec/sample/calculates_order_tax_action_spec.rb +0 -16
  105. data/spec/sample/calculates_tax_spec.rb +0 -30
  106. data/spec/sample/looks_up_tax_percentage_action_spec.rb +0 -55
  107. data/spec/sample/provides_free_shipping_action_spec.rb +0 -25
  108. data/spec/sample/tax/calculates_order_tax_action.rb +0 -10
  109. data/spec/sample/tax/calculates_tax.rb +0 -11
  110. data/spec/sample/tax/looks_up_tax_percentage_action.rb +0 -28
  111. data/spec/sample/tax/provides_free_shipping_action.rb +0 -11
  112. data/spec/spec_helper.rb +0 -29
  113. data/spec/support.rb +0 -1
  114. data/spec/test_doubles.rb +0 -628
  115. data/spec/testing/context_factory/iterate_spec.rb +0 -39
  116. data/spec/testing/context_factory/reduce_if_spec.rb +0 -40
  117. data/spec/testing/context_factory/reduce_until_spec.rb +0 -40
  118. data/spec/testing/context_factory/with_callback_spec.rb +0 -38
  119. data/spec/testing/context_factory_spec.rb +0 -76
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- require "rubygems"
4
- require "bundler/setup"
5
-
6
- require 'rspec/core/rake_task'
7
- require 'rubocop/rake_task'
8
- RuboCop::RakeTask.new
9
- RSpec::Core::RakeTask.new(:spec)
10
-
11
- task(:default).clear
12
- task :default => %i[spec rubocop]
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.5.4
data/audit/bench.rb DELETED
@@ -1,99 +0,0 @@
1
- # Micro-benchmark dei costi nascosti individuati nell'audit
2
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
3
- require 'functional-light-service'
4
- require 'benchmark/ips'
5
-
6
- include FunctionalLightService::Prelude::Result
7
- include FunctionalLightService::Prelude::Option
8
-
9
- puts "Ruby #{RUBY_VERSION}, YJIT: #{defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? ? 'on' : 'off'}"
10
- puts
11
-
12
- some = Some(42)
13
- success = Success(42)
14
-
15
- # 1) Costo del motore match (Option#value_or usa match) vs equivalente diretto
16
- Benchmark.ips do |x|
17
- x.report("Option#value_or (match engine)") { some.value_or(0) }
18
- x.report("equivalente is_a? diretto") { some.is_a?(FunctionalLightService::Option::Some) ? some.value : 0 }
19
- x.report("case/in nativo Ruby") do
20
- case some
21
- in FunctionalLightService::Option::Some then some.value
22
- else 0
23
- end
24
- end
25
- x.compare!
26
- end
27
-
28
- # 2) match esplicito con guard (Struct.new per chiamata) su Result#+
29
- Benchmark.ips do |x|
30
- other = Success(1)
31
- x.report("Result#+ (match con guard -> Struct.new/call)") { success + other }
32
- x.report("somma diretta is_a?") do
33
- if success.is_a?(FunctionalLightService::Result::Success) && other.is_a?(FunctionalLightService::Result::Success)
34
- FunctionalLightService::Result::Success.new(success.value + other.value)
35
- end
36
- end
37
- x.compare!
38
- end
39
-
40
- # 3) Result#map (bind, senza match) — per confronto: la parte "sana"
41
- Benchmark.ips do |x|
42
- f = ->(v) { Success(v + 1) }
43
- x.report("Result#map via bind") { success.map(f) }
44
- x.report("lambda diretta") { success.is_a?(FunctionalLightService::Result::Success) ? f.call(success.value) : success }
45
- x.compare!
46
- end
47
-
48
- # 4) Overhead di Organizer.with: caller(1..1) + methods.include?(:call)
49
- Benchmark.ips do |x|
50
- klass = Class.new { def self.call; end }
51
- x.report("caller(1..1) + methods.include?") do
52
- c = caller(1..1).first
53
- c =~ /`(.*)'/
54
- klass.methods.include?(:call)
55
- end
56
- x.report("respond_to?(:call) soltanto") { klass.respond_to?(:call) }
57
- x.compare!
58
- end
59
-
60
- # 5) define_accessor_methods_for_keys: singleton methods per ogni context
61
- Benchmark.ips do |x|
62
- keys = %i[number total counter]
63
- x.report("nuovo Context + define accessor per keys") do
64
- ctx = FunctionalLightService::Context.make(:number => 1, :total => 2, :counter => 3)
65
- ctx.define_accessor_methods_for_keys(keys)
66
- end
67
- x.report("nuovo Context senza accessor") do
68
- FunctionalLightService::Context.make(:number => 1, :total => 2, :counter => 3)
69
- end
70
- x.compare!
71
- end
72
-
73
- # 6) Context#[] con alias attivi (Hash#key reverse scan) vs Hash puro
74
- Benchmark.ips do |x|
75
- ctx = FunctionalLightService::Context.make(:a => 1, :b => 2, :c => 3)
76
- ctx.assign_aliases(:a => :alfa)
77
- h = { :a => 1, :b => 2, :c => 3 }
78
- x.report("Context#[] (con lookup alias)") { ctx[:b] }
79
- x.report("Hash#[] puro") { h[:b] }
80
- x.compare!
81
- end
82
-
83
- # 7) Organizer end-to-end: quota di overhead per call minimale
84
- class BenchAdd
85
- extend FunctionalLightService::Action
86
- expects :number
87
- executed { |ctx| ctx[:number] = ctx[:number] + 1 }
88
- end
89
- class BenchOrg
90
- extend FunctionalLightService::Organizer
91
- def self.call(n)
92
- with(:number => n).reduce([BenchAdd])
93
- end
94
- end
95
- Benchmark.ips do |x|
96
- x.report("Organizer.call 1 action") { BenchOrg.call(1) }
97
- x.report("lavoro utile equivalente") { { :number => 1 }.tap { |h| h[:number] += 1 } }
98
- x.compare!
99
- end
@@ -1,172 +0,0 @@
1
- # Verifica dei finding dell'audit — eseguito contro lib/ reale
2
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
3
- require 'functional-light-service'
4
-
5
- results = []
6
- def check(results, name)
7
- ok, detail = yield
8
- results << [name, ok, detail]
9
- rescue => e
10
- results << [name, :exception, "#{e.class}: #{e.message}"]
11
- end
12
-
13
- # ---------- F1: before_actions perso alla seconda chiamata ----------
14
- class AddOne
15
- extend FunctionalLightService::Action
16
- expects :number
17
- promises :number
18
- executed { |ctx| ctx.number = ctx.number + 1 }
19
- end
20
-
21
- class Org1
22
- extend FunctionalLightService::Organizer
23
- before_actions ->(ctx) { ctx.number -= 10 if ctx.current_action == AddOne }
24
- def self.call(number)
25
- with(:number => number).reduce([AddOne])
26
- end
27
- end
28
-
29
- check(results, "F1 before_actions perso alla 2a chiamata") do
30
- r1 = Org1.call(0).fetch(:number) # atteso: -10 + 1 = -9
31
- r2 = Org1.call(0).fetch(:number) # se bug: hook perso -> 1
32
- [r1 == -9 && r2 == 1, "prima call=#{r1}, seconda call=#{r2}"]
33
- end
34
-
35
- # ---------- F2: Context#fetch — nessun KeyError e scrittura su read ----------
36
- check(results, "F2a fetch(:missing) ritorna nil invece di KeyError e scrive la chiave") do
37
- ctx = FunctionalLightService::Context.make({})
38
- v = ctx.fetch(:missing)
39
- [v.nil? && ctx.to_h.key?(:missing), "valore=#{v.inspect}, chiavi dopo fetch=#{ctx.keys.inspect}"]
40
- end
41
-
42
- check(results, "F2b fetch con default sovrascrive un valore falsy esistente") do
43
- ctx = FunctionalLightService::Context.make(:flag => false)
44
- v = ctx.fetch(:flag, true)
45
- [v == true && ctx[:flag] == true, "fetch(:flag, true)=#{v.inspect}, ctx[:flag] ora=#{ctx[:flag].inspect} (era false)"]
46
- end
47
-
48
- # ---------- F3: alias asimmetrico lettura/scrittura ----------
49
- check(results, "F3 scrittura su alias persa in lettura") do
50
- ctx = FunctionalLightService::Context.make(:codice_fiscale => "ABC")
51
- ctx.assign_aliases(:codice_fiscale => :cf)
52
- ctx[:cf] = "NUOVO" # scrive la chiave :cf direttamente
53
- read = ctx[:cf] # legge tradotto -> :codice_fiscale -> "ABC"
54
- [read == "ABC", "dopo ctx[:cf]='NUOVO', ctx[:cf]=#{read.inspect}; hash=#{ctx.to_h.inspect}"]
55
- end
56
-
57
- # ---------- F4: accessor non definito per chiavi che collidono con metodi Hash ----------
58
- class UsesSize
59
- extend FunctionalLightService::Action
60
- expects :size
61
- executed { |ctx| ctx[:observed] = ctx.size } # l'utente si aspetta il valore di :size
62
- end
63
-
64
- check(results, "F4 expects :size -> accessor non definito, ritorna Hash#size") do
65
- result = UsesSize.execute(:size => 999)
66
- [result[:observed] != 999, "ctx.size dentro l'action=#{result[:observed].inspect} (atteso dal punto di vista utente: 999)"]
67
- end
68
-
69
- # ---------- F5: @ctx a livello di classe Action (retention + race) ----------
70
- check(results, "F5 la classe Action trattiene l'ultimo context in @ctx") do
71
- AddOne.execute(:number => 5)
72
- held = AddOne.instance_variable_get(:@ctx)
73
- [held.is_a?(FunctionalLightService::Context), "AddOne @ctx = #{held.inspect[0,80]}"]
74
- end
75
-
76
- # ---------- F6: fail! muta l'hash di opzioni del chiamante ----------
77
- check(results, "F6 fail! cancella :error_code dall'hash del chiamante") do
78
- ctx = FunctionalLightService::Context.make({})
79
- opts = { :error_code => 500 }
80
- ctx.fail!("boom", opts)
81
- [!opts.key?(:error_code), "opts dopo fail! = #{opts.inspect}"]
82
- end
83
-
84
- # ---------- F7: Null#respond_to? con firma sbagliata ----------
85
- check(results, "F7 Null#respond_to?(m, true) solleva ArgumentError") do
86
- begin
87
- Null.instance.respond_to?(:foo, true)
88
- [false, "nessuna eccezione"]
89
- rescue ArgumentError => e
90
- [true, "ArgumentError: #{e.message}"]
91
- end
92
- end
93
-
94
- # ---------- F8: Context#[] con aliases usa Hash#key (reverse lookup O(n)) ----------
95
- check(results, "F8 lettura di chiave originale con molti alias resta corretta (sanity)") do
96
- ctx = FunctionalLightService::Context.make(:a => 1)
97
- ctx.assign_aliases(:a => :b)
98
- [ctx[:b] == 1 && ctx[:a] == 1, "ctx[:a]=#{ctx[:a]}, ctx[:b]=#{ctx[:b]}"]
99
- end
100
-
101
- # ---------- F9: skip_remaining! dentro iterate viene resettato ad ogni item ----------
102
- class SkipAll
103
- extend FunctionalLightService::Action
104
- executed { |ctx| ctx.skip_remaining!("basta") if ctx[:counter] == 2 }
105
- end
106
- class CollectAction
107
- extend FunctionalLightService::Action
108
- executed { |ctx| (ctx[:seen] ||= []) << ctx[:counter] }
109
- end
110
- class OrgIter
111
- extend FunctionalLightService::Organizer
112
- def self.call(ctx)
113
- with(ctx).reduce([iterate(:counters, [SkipAll, CollectAction])])
114
- end
115
- end
116
-
117
- check(results, "F9 skip_remaining! dentro iterate NON ferma l'iterazione") do
118
- r = OrgIter.call(:counters => [1, 2, 3])
119
- [r[:seen] == [1, 3], "seen=#{r[:seen].inspect} (2 saltato, ma 3 processato: lo skip e' stato resettato)"]
120
- end
121
-
122
- # ---------- F10: succeed! + message perso da reset_skip_remaining! in scoped_reduce ----------
123
- check(results, "F10 reset_skip_remaining! azzera anche message/outcome") do
124
- ctx = FunctionalLightService::Context.make({})
125
- ctx.succeed!("fatto bene")
126
- ctx.reset_skip_remaining!
127
- [ctx.message == '', "message dopo reset=#{ctx.message.inspect}"]
128
- end
129
-
130
- # ---------- F11: Context#select/map degradano a Hash ----------
131
- check(results, "F11 Context#select ritorna Hash, non Context (perde outcome)") do
132
- ctx = FunctionalLightService::Context.make(:a => 1)
133
- sel = ctx.select { |_k, _v| true }
134
- [!sel.is_a?(FunctionalLightService::Context), "select.class=#{sel.class}"]
135
- end
136
-
137
- # ---------- F12: Some(nil) e' costruibile ----------
138
- check(results, "F12 Some(nil) e' consentito (Option non valida il nil)") do
139
- s = FunctionalLightService::Option::Some.new(nil)
140
- [s.some? && s.value.nil?, "Some(nil).some?=#{s.some?}, value=nil"]
141
- end
142
-
143
- # ---------- F13: rollback con azione duplicata nella lista ----------
144
- class RollA
145
- extend FunctionalLightService::Action
146
- executed { |ctx| (ctx[:trace] ||= []) << :a }
147
- rolled_back { |ctx| (ctx[:rb] ||= []) << :a }
148
- end
149
- class RollB
150
- extend FunctionalLightService::Action
151
- executed { |ctx| (ctx[:trace] ||= []) << :b; ctx.fail_with_rollback!("ko") if ctx[:trace].count(:b) == 2 }
152
- rolled_back { |ctx| (ctx[:rb] ||= []) << :b }
153
- end
154
- class OrgRoll
155
- extend FunctionalLightService::Organizer
156
- def self.call(ctx)
157
- with(ctx).reduce([RollB, RollA, RollB]) # RollB duplicata; fallisce la SECONDA RollB
158
- end
159
- end
160
-
161
- check(results, "F13 rollback con azione duplicata: index trova la 1a occorrenza -> rollback parziale") do
162
- r = OrgRoll.call({})
163
- # eseguite: B, A, B(fail). Rollback atteso: B, A, B. Con il bug: index(RollB)=0 -> solo [RollB].take(1) -> rollback solo B
164
- [r[:rb] == [:b], "trace=#{r[:trace].inspect}, rollback eseguiti=#{r[:rb].inspect} (attesi: [:b, :a, :b])"]
165
- end
166
-
167
- puts
168
- results.each do |name, ok, detail|
169
- status = ok == true ? "CONFERMATO" : (ok == :exception ? "ECCEZIONE " : "NON RIPRODOTTO")
170
- puts "[#{status}] #{name}"
171
- puts " -> #{detail}"
172
- end
@@ -1,33 +0,0 @@
1
- require File.expand_path('../lib/functional-light-service/version', __FILE__)
2
-
3
- Gem::Specification.new do |gem|
4
- gem.authors = ["Boscolo Michele"]
5
- gem.email = ["miboscol@gmail.com"]
6
- gem.description = %q{FunctionalLightService combines the Organizer/Action/Context pattern of LightService with functional programming constructs (Result/Option monads, pattern matching) inspired by Deterministic: complex workflows are organized into small single-purpose actions with functional error handling.}
7
- gem.summary = %q{A service skeleton with an emphasis on simplicity with a pinch a functional programming}
8
- gem.homepage = "https://github.com/sphynx79/functional-light-service"
9
- gem.license = "MIT"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.name = "functional-light-service"
14
- gem.require_paths = ["lib"]
15
- gem.version = FunctionalLightService::VERSION
16
- gem.required_ruby_version = '>= 3.1.0'
17
-
18
- gem.add_runtime_dependency("dry-inflector", ">= 0.2.1", "< 2")
19
- gem.add_runtime_dependency("i18n", "~> 1.8", ">= 1.8.11")
20
- # logger non e' piu una default gem da Ruby 3.5/4.0: senza questa
21
- # dichiarazione `require 'logger'` fallisce sotto Bundler
22
- gem.add_runtime_dependency("logger", "~> 1.5")
23
-
24
- gem.add_development_dependency("rake", "~> 13.0")
25
- gem.add_development_dependency("rspec", "~> 3.13")
26
- gem.add_development_dependency("simplecov", "~> 0.22")
27
- gem.add_development_dependency("simplecov-cobertura", "~> 3.0")
28
- gem.add_development_dependency("rubocop", "~> 1.75")
29
- gem.add_development_dependency("rubocop-performance", "~> 1.20")
30
- gem.add_development_dependency("pry", "~> 0.15")
31
- gem.add_development_dependency("solargraph", "~> 0.50")
32
- gem.add_development_dependency("benchmark-ips", "~> 2.13")
33
- end
@@ -1,5 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "dry-inflector", "~> 0.2.1"
4
-
5
- gemspec :path => "../"
@@ -1,5 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem "i18n", "~> 1.8.11"
4
-
5
- gemspec :path => "../"
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- module Action
5
- def self.extended(base_class)
6
- base_class.extend Macros
7
- base_class.extend FunctionalLightService::Prelude::Result
8
- end
9
-
10
- def self.included(base_class)
11
- FunctionalLightService::Deprecations.warn(
12
- "Including FunctionalLightService::Action is deprecated; " \
13
- "use `extend FunctionalLightService::Action` instead"
14
- )
15
- base_class.extend Macros
16
- end
17
-
18
- module Macros
19
- def expects(*args)
20
- expected_keys.concat(args)
21
- end
22
-
23
- def promises(*args)
24
- promised_keys.concat(args)
25
- end
26
-
27
- def expected_keys
28
- @expected_keys ||= []
29
- end
30
-
31
- def promised_keys
32
- @promised_keys ||= []
33
- end
34
-
35
- def executed
36
- define_singleton_method :execute do |context = {}|
37
- action_context = create_action_context(context)
38
- return action_context if action_context.stop_processing?
39
-
40
- # Store the action within the context
41
- action_context.current_action = self
42
-
43
- Context::KeyVerifier.verify_keys(action_context, self) do
44
- action_context.define_accessor_methods_for_keys(all_keys)
45
-
46
- catch(:jump_when_failed) do
47
- call_before_action(action_context)
48
- yield(action_context)
49
- call_after_action(action_context)
50
- end
51
- end
52
- end
53
- end
54
-
55
- def rolled_back
56
- msg = "`rolled_back` macro can not be invoked again"
57
- raise msg if respond_to?(:rollback)
58
-
59
- define_singleton_method :rollback do |context = {}|
60
- yield(context)
61
-
62
- context
63
- end
64
- end
65
-
66
- private
67
-
68
- def create_action_context(context)
69
- return context if context.is_a? FunctionalLightService::Context
70
-
71
- FunctionalLightService::Context.make(context)
72
- end
73
-
74
- def all_keys
75
- expected_keys + promised_keys
76
- end
77
-
78
- def call_before_action(context)
79
- invoke_callbacks(context[:_before_actions], context)
80
- end
81
-
82
- def call_after_action(context)
83
- invoke_callbacks(context[:_after_actions], context)
84
- end
85
-
86
- def invoke_callbacks(callbacks, context)
87
- return context unless callbacks
88
-
89
- callbacks.each do |cb|
90
- cb.call(context)
91
- end
92
-
93
- context
94
- end
95
- end
96
- end
97
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- class Configuration
5
- class << self
6
- attr_writer :logger, :localization_adapter
7
-
8
- def logger
9
- @logger = _default_logger unless instance_variable_defined?("@logger")
10
- @logger
11
- end
12
-
13
- def localization_adapter
14
- @localization_adapter ||= LocalizationAdapter.new
15
- end
16
-
17
- private
18
-
19
- def _default_logger
20
- logger = Logger.new(nil)
21
- logger.level = Logger::WARN
22
- logger
23
- end
24
- end
25
- end
26
- end
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- class Context
5
- class KeyVerifier
6
- attr_reader :context, :action
7
-
8
- def initialize(context, action)
9
- @context = context
10
- @action = action
11
- end
12
-
13
- def are_all_keys_in_context?(keys)
14
- not_found_keys = keys_not_found(keys)
15
- not_found_keys.none?
16
- end
17
-
18
- def keys_not_found(keys)
19
- keys ||= context.keys
20
- # context.key? risolve anche gli alias
21
- keys.reject { |key| context.key?(key) }
22
- end
23
-
24
- def format_keys(keys)
25
- keys.map { |k| ":#{k}" }.join(', ')
26
- end
27
-
28
- def error_message
29
- "#{type_name} #{format_keys(keys_not_found(keys))} " \
30
- "to be in the context during #{action}"
31
- end
32
-
33
- def throw_error_predicate(_keys)
34
- raise NotImplementedError, 'Sorry, you have to override length'
35
- end
36
-
37
- def verify
38
- return context if context.failure?
39
-
40
- if throw_error_predicate(keys)
41
- Configuration.logger.error error_message
42
- raise error_to_throw, error_message
43
- end
44
-
45
- context
46
- end
47
-
48
- def self.verify_keys(context, action, &block)
49
- ReservedKeysVerifier.new(context, action).verify
50
- ExpectedKeyVerifier.new(context, action).verify
51
-
52
- block.call
53
-
54
- PromisedKeyVerifier.new(context, action).verify
55
- end
56
- end
57
-
58
- class ExpectedKeyVerifier < KeyVerifier
59
- def type_name
60
- "expected"
61
- end
62
-
63
- def keys
64
- action.expected_keys
65
- end
66
-
67
- def error_to_throw
68
- ExpectedKeysNotInContextError
69
- end
70
-
71
- def throw_error_predicate(keys)
72
- !are_all_keys_in_context?(keys)
73
- end
74
- end
75
-
76
- class PromisedKeyVerifier < KeyVerifier
77
- def type_name
78
- "promised"
79
- end
80
-
81
- def keys
82
- action.promised_keys
83
- end
84
-
85
- def error_to_throw
86
- PromisedKeysNotInContextError
87
- end
88
-
89
- def throw_error_predicate(keys)
90
- !are_all_keys_in_context?(keys)
91
- end
92
- end
93
-
94
- class ReservedKeysVerifier < KeyVerifier
95
- def violated_keys
96
- (action.promised_keys + action.expected_keys) & reserved_keys
97
- end
98
-
99
- def error_message
100
- "promised or expected keys cannot be a " \
101
- "reserved key: [#{format_keys(violated_keys)}]"
102
- end
103
-
104
- def keys
105
- violated_keys
106
- end
107
-
108
- def error_to_throw
109
- ReservedKeysInContextError
110
- end
111
-
112
- def throw_error_predicate(keys)
113
- keys.any?
114
- end
115
-
116
- def reserved_keys
117
- # _aliases/_before_actions/_after_actions sono chiavi infrastrutturali
118
- # scritte da Organizer.with nel context
119
- %i[message error_code current_action
120
- _aliases _before_actions _after_actions].freeze
121
- end
122
- end
123
- end
124
- end