functional-light-service 6.2.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/lib/functional-light-service.rb +6 -36
  3. metadata +14 -307
  4. data/.github/workflows/project-build.yml +0 -71
  5. data/.gitignore +0 -24
  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 -292
  12. data/CODE_OF_CONDUCT.md +0 -22
  13. data/Gemfile +0 -4
  14. data/LICENSE +0 -22
  15. data/README.md +0 -1663
  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 -35
  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 -145
  24. data/lib/functional-light-service/configuration.rb +0 -36
  25. data/lib/functional-light-service/context/key_verifier.rb +0 -124
  26. data/lib/functional-light-service/context.rb +0 -208
  27. data/lib/functional-light-service/deprecations.rb +0 -26
  28. data/lib/functional-light-service/errors.rb +0 -9
  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/i18n/localization_adapter.rb +0 -50
  37. data/lib/functional-light-service/localization_adapter.rb +0 -39
  38. data/lib/functional-light-service/localization_map.rb +0 -9
  39. data/lib/functional-light-service/organizer/execute.rb +0 -16
  40. data/lib/functional-light-service/organizer/iterate.rb +0 -30
  41. data/lib/functional-light-service/organizer/reduce_case.rb +0 -50
  42. data/lib/functional-light-service/organizer/reduce_if.rb +0 -19
  43. data/lib/functional-light-service/organizer/reduce_if_else.rb +0 -23
  44. data/lib/functional-light-service/organizer/reduce_until.rb +0 -22
  45. data/lib/functional-light-service/organizer/reduce_while.rb +0 -31
  46. data/lib/functional-light-service/organizer/scoped_reducable.rb +0 -15
  47. data/lib/functional-light-service/organizer/with_callback.rb +0 -28
  48. data/lib/functional-light-service/organizer/with_reducer.rb +0 -81
  49. data/lib/functional-light-service/organizer/with_reducer_factory.rb +0 -20
  50. data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +0 -111
  51. data/lib/functional-light-service/organizer.rb +0 -129
  52. data/lib/functional-light-service/testing/context_factory.rb +0 -48
  53. data/lib/functional-light-service/testing.rb +0 -3
  54. data/lib/functional-light-service/version.rb +0 -5
  55. data/resources/fail_actions.png +0 -0
  56. data/resources/light-service.png +0 -0
  57. data/resources/organizer_and_actions.png +0 -0
  58. data/resources/skip_actions.png +0 -0
  59. data/spec/acceptance/add_numbers_spec.rb +0 -11
  60. data/spec/acceptance/after_actions_spec.rb +0 -87
  61. data/spec/acceptance/around_each_spec.rb +0 -19
  62. data/spec/acceptance/before_actions_spec.rb +0 -115
  63. data/spec/acceptance/custom_log_from_organizer_spec.rb +0 -61
  64. data/spec/acceptance/deprecation_warnings_spec.rb +0 -82
  65. data/spec/acceptance/fail_spec.rb +0 -52
  66. data/spec/acceptance/log_from_organizer_spec.rb +0 -154
  67. data/spec/acceptance/message_localization_spec.rb +0 -119
  68. data/spec/acceptance/organizer/add_aliases_spec.rb +0 -28
  69. data/spec/acceptance/organizer/add_to_context_spec.rb +0 -54
  70. data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +0 -42
  71. data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +0 -90
  72. data/spec/acceptance/organizer/execute_spec.rb +0 -67
  73. data/spec/acceptance/organizer/iterate_spec.rb +0 -44
  74. data/spec/acceptance/organizer/reduce_case_spec.rb +0 -65
  75. data/spec/acceptance/organizer/reduce_if_else_spec.rb +0 -60
  76. data/spec/acceptance/organizer/reduce_if_spec.rb +0 -89
  77. data/spec/acceptance/organizer/reduce_until_spec.rb +0 -49
  78. data/spec/acceptance/organizer/reduce_while_spec.rb +0 -96
  79. data/spec/acceptance/organizer/with_callback_spec.rb +0 -113
  80. data/spec/acceptance/organizer_entry_point_spec.rb +0 -35
  81. data/spec/acceptance/rollback_spec.rb +0 -183
  82. data/spec/acceptance/skip_all_remaining_spec.rb +0 -139
  83. data/spec/acceptance/testing/context_factory_spec.rb +0 -54
  84. data/spec/action_expected_keys_spec.rb +0 -63
  85. data/spec/action_expects_and_promises_spec.rb +0 -97
  86. data/spec/action_optional_expected_keys_spec.rb +0 -107
  87. data/spec/action_promised_keys_spec.rb +0 -126
  88. data/spec/action_spec.rb +0 -97
  89. data/spec/context/inspect_spec.rb +0 -52
  90. data/spec/context_spec.rb +0 -301
  91. data/spec/examples/amount_spec.rb +0 -77
  92. data/spec/examples/controller_spec.rb +0 -63
  93. data/spec/examples/validate_address_spec.rb +0 -38
  94. data/spec/i18n_localization_adapter_spec.rb +0 -83
  95. data/spec/lib/deterministic/class_mixin_spec.rb +0 -24
  96. data/spec/lib/deterministic/currify_spec.rb +0 -90
  97. data/spec/lib/deterministic/monad_axioms.rb +0 -46
  98. data/spec/lib/deterministic/monad_spec.rb +0 -47
  99. data/spec/lib/deterministic/null_spec.rb +0 -65
  100. data/spec/lib/deterministic/option_spec.rb +0 -140
  101. data/spec/lib/deterministic/result/failure_spec.rb +0 -65
  102. data/spec/lib/deterministic/result/result_map_spec.rb +0 -155
  103. data/spec/lib/deterministic/result/result_shared.rb +0 -25
  104. data/spec/lib/deterministic/result/success_spec.rb +0 -41
  105. data/spec/lib/deterministic/result_spec.rb +0 -63
  106. data/spec/lib/deterministic/sequencer_spec.rb +0 -506
  107. data/spec/lib/edge_cases_spec.rb +0 -156
  108. data/spec/lib/enum_spec.rb +0 -114
  109. data/spec/lib/native_pattern_matching_spec.rb +0 -74
  110. data/spec/localization_adapter_spec.rb +0 -66
  111. data/spec/organizer/with_reducer_spec.rb +0 -56
  112. data/spec/organizer_key_aliases_spec.rb +0 -29
  113. data/spec/organizer_spec.rb +0 -115
  114. data/spec/readme_spec.rb +0 -45
  115. data/spec/sample/calculates_order_tax_action_spec.rb +0 -16
  116. data/spec/sample/calculates_tax_spec.rb +0 -30
  117. data/spec/sample/looks_up_tax_percentage_action_spec.rb +0 -55
  118. data/spec/sample/provides_free_shipping_action_spec.rb +0 -25
  119. data/spec/sample/tax/calculates_order_tax_action.rb +0 -10
  120. data/spec/sample/tax/calculates_tax.rb +0 -11
  121. data/spec/sample/tax/looks_up_tax_percentage_action.rb +0 -28
  122. data/spec/sample/tax/provides_free_shipping_action.rb +0 -11
  123. data/spec/spec_helper.rb +0 -32
  124. data/spec/support.rb +0 -1
  125. data/spec/test_doubles.rb +0 -656
  126. data/spec/testing/context_factory/iterate_spec.rb +0 -39
  127. data/spec/testing/context_factory/reduce_if_spec.rb +0 -40
  128. data/spec/testing/context_factory/reduce_until_spec.rb +0 -40
  129. data/spec/testing/context_factory/with_callback_spec.rb +0 -38
  130. data/spec/testing/context_factory_spec.rb +0 -76
@@ -1,208 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- # rubocop:disable Metrics/ClassLength
5
- class Context < Hash
6
- include FunctionalLightService::Prelude::Option
7
- include FunctionalLightService::Prelude::Result
8
-
9
- attr_reader :outcome
10
- attr_accessor :current_action, :organized_by
11
-
12
- # rubocop:disable Lint/MissingSuper
13
- def initialize(context = {},
14
- outcome = Success(:message => '', :error => nil))
15
- @outcome = outcome
16
- @skip_remaining = false
17
- @skip_all_remaining = false
18
- context.to_hash.each { |k, v| self[k] = v }
19
- end
20
- # rubocop:enable Lint/MissingSuper
21
-
22
- def self.make(context = {})
23
- unless context.is_a?(Hash) || context.is_a?(FunctionalLightService::Context)
24
- msg = 'Argument must be Hash or FunctionalLightService::Context'
25
- raise ArgumentError, msg
26
- end
27
-
28
- context = new(context) unless context.is_a?(Context)
29
-
30
- context.assign_aliases(context.delete(:_aliases)) if context[:_aliases]
31
- context
32
- end
33
-
34
- def add_to_context(values)
35
- merge! values
36
- end
37
-
38
- def success?
39
- @outcome.success?
40
- end
41
-
42
- def failure?
43
- @outcome.failure?
44
- end
45
-
46
- def skip_remaining?
47
- @skip_remaining
48
- end
49
-
50
- def skip_all_remaining?
51
- @skip_all_remaining
52
- end
53
-
54
- def reset_skip_remaining!
55
- # Resetta soltanto il flag: l'esito (e il suo messaggio) non vanno persi
56
- @skip_remaining = false
57
- end
58
-
59
- def message
60
- @outcome.value[:message]
61
- end
62
-
63
- def error_code
64
- @outcome.value[:error]
65
- end
66
-
67
- def succeed!(message = nil, options = {})
68
- message = Configuration.localization_adapter.success(message,
69
- current_action,
70
- options)
71
- @outcome = Success(:message => message)
72
- end
73
-
74
- def fail!(message = nil, options_or_error_code = {})
75
- options_or_error_code ||= {}
76
-
77
- if options_or_error_code.is_a?(Hash)
78
- # dup: l'hash di opzioni appartiene al chiamante e non va mutato
79
- options = options_or_error_code.dup
80
- error_code = options.delete(:error_code)
81
- else
82
- error_code = options_or_error_code
83
- options = {}
84
- end
85
-
86
- message = Configuration.localization_adapter.failure(message,
87
- current_action,
88
- options)
89
-
90
- @outcome = Failure(:message => message, :error => error_code)
91
- end
92
-
93
- def fail_and_return!(*args)
94
- fail!(*args)
95
- throw(:jump_when_failed)
96
- end
97
-
98
- def fail_with_rollback!(message = nil, error_code = nil)
99
- fail!(message, error_code)
100
- raise FailWithRollbackError
101
- end
102
-
103
- def skip_remaining!(message = nil)
104
- @outcome = Success(:message => message)
105
- @skip_remaining = true
106
- end
107
-
108
- # Variante non azzerabile di skip_remaining!: salta tutto il resto
109
- # dell'organizer, scope annidati compresi (scoped_reduce non la resetta)
110
- def skip_all_remaining!(message = nil)
111
- @outcome = Success(:message => message)
112
- @skip_all_remaining = true
113
- end
114
-
115
- def stop_processing?
116
- failure? || skip_remaining? || skip_all_remaining?
117
- end
118
-
119
- # Registra le chiavi come accessor consentiti: la lettura/scrittura passa
120
- # da method_missing con whitelist. Rispetto a define_singleton_method non
121
- # materializza una singleton class per ogni context (audit, finding 3.3)
122
- def define_accessor_methods_for_keys(keys)
123
- return if keys.nil?
124
-
125
- @accessor_methods ||= {}
126
- keys.each do |key|
127
- key = key.to_sym
128
- next if @accessor_methods.key?(key)
129
-
130
- # Prima il conflitto veniva saltato in silenzio e ctx.size (o :count,
131
- # :message, ...) ritornava il metodo di Hash invece del valore
132
- if respond_to?(key) || respond_to?("#{key}=")
133
- raise ReservedKeysInContextError,
134
- "expected or promised key :#{key} conflicts with an existing " \
135
- "#{self.class.name} method: rename the key or access it via ctx[:#{key}]"
136
- end
137
-
138
- @accessor_methods[key] = [:reader, key]
139
- @accessor_methods[:"#{key}="] = [:writer, key]
140
- end
141
- end
142
-
143
- def method_missing(name, *args)
144
- accessor = @accessor_methods && @accessor_methods[name]
145
- return super unless accessor
146
-
147
- accessor[0] == :reader ? fetch(accessor[1]) : self[accessor[1]] = args.first
148
- end
149
-
150
- def respond_to_missing?(name, _include_all = false)
151
- (!@accessor_methods.nil? && @accessor_methods.key?(name)) || super
152
- end
153
-
154
- def assign_aliases(aliases)
155
- @aliases = aliases
156
- # Hash inverso precomputato: la risoluzione in lettura/scrittura
157
- # resta O(1) invece del reverse-scan di Hash#key
158
- @inverse_aliases = aliases.invert
159
-
160
- self
161
- end
162
-
163
- def aliases
164
- @aliases ||= {}
165
- end
166
-
167
- def [](key)
168
- super(resolve_key(key))
169
- end
170
-
171
- def []=(key, value)
172
- super(resolve_key(key), value)
173
- end
174
-
175
- def fetch(key, ...)
176
- super(resolve_key(key), ...)
177
- end
178
-
179
- def key?(key)
180
- super(resolve_key(key))
181
- end
182
-
183
- alias has_key? key?
184
- alias member? key?
185
- alias include? key?
186
-
187
- def inspect
188
- "#{self.class}(#{self}, success: #{success?}, message: #{check_nil(message)}, error_code: " \
189
- "#{check_nil(error_code)}, skip_remaining: #{@skip_remaining}, " \
190
- "skip_all_remaining: #{@skip_all_remaining}, aliases: #{aliases})"
191
- end
192
-
193
- private
194
-
195
- def resolve_key(key)
196
- return key unless @inverse_aliases
197
-
198
- @inverse_aliases[key] || key
199
- end
200
-
201
- def check_nil(value)
202
- return 'nil' unless value
203
-
204
- "'#{value}'"
205
- end
206
- end
207
- # rubocop:enable Metrics/ClassLength
208
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- # Deprecation warnings non fatali: ogni messaggio viene emesso una sola
5
- # volta per processo (su stderr) e puo' essere silenziato globalmente,
6
- # ad esempio nelle suite di test.
7
- module Deprecations
8
- @emitted = {}
9
-
10
- class << self
11
- attr_accessor :silenced
12
-
13
- def warn(message)
14
- return if silenced
15
- return if @emitted[message]
16
-
17
- @emitted[message] = true
18
- Kernel.warn("DEPRECATION WARNING: #{message}")
19
- end
20
-
21
- def reset!
22
- @emitted = {}
23
- end
24
- end
25
- end
26
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- class FailWithRollbackError < StandardError; end
5
- class ExpectedKeysNotInContextError < StandardError; end
6
- class PromisedKeysNotInContextError < StandardError; end
7
- class ReservedKeysInContextError < StandardError; end
8
- class UnusableExpectKeyDefaultError < StandardError; end
9
- end
@@ -1,286 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- module Enum
5
- class MatchError < StandardError; end
6
- end
7
-
8
- class EnumBuilder
9
- def initialize(parent)
10
- @parent = parent
11
- end
12
-
13
- class DataType
14
- module AnyEnum
15
- include FunctionalLightService::Monad
16
-
17
- def match(&)
18
- parent.match(self, &)
19
- end
20
-
21
- def to_s
22
- value.to_s
23
- end
24
-
25
- def name
26
- self.class.name.split("::")[-1]
27
- end
28
-
29
- # Returns array. Will fail on Nullary objects.
30
- # TODO: define a Unary module so we can define this method differently on Unary vs Binary
31
- def wrapped_values
32
- if is_a?(FunctionalLightService::EnumBuilder::DataType::Binary)
33
- value.values
34
- else
35
- [value]
36
- end
37
- end
38
-
39
- # Supporto al pattern matching nativo di Ruby (case/in):
40
- # case result
41
- # in FunctionalLightService::Result::Success(s) then ...
42
- # in FunctionalLightService::Result::Failure(f) then ...
43
- # end
44
- def deconstruct
45
- is_a?(FunctionalLightService::EnumBuilder::DataType::Nullary) ? [] : wrapped_values
46
- end
47
-
48
- def deconstruct_keys(_keys)
49
- if is_a?(FunctionalLightService::EnumBuilder::DataType::Binary)
50
- value.dup
51
- elsif is_a?(FunctionalLightService::EnumBuilder::DataType::Nullary)
52
- {}
53
- else
54
- { args[0] => value }
55
- end
56
- end
57
- end
58
-
59
- module Nullary
60
- def initialize(*_args)
61
- @value = nil
62
- end
63
-
64
- def inspect
65
- name
66
- end
67
- end
68
-
69
- # TODO: this should probably be named Multary
70
- module Binary
71
- def initialize(*init)
72
- unless (init.one? && init[0].is_a?(Hash)) || init.count == args.count
73
- raise ArgumentError, "Expected arguments for #{args}, got #{init}"
74
- end
75
-
76
- @value = if init.one? && init[0].is_a?(Hash)
77
- args.zip(init[0].values).to_h
78
- else
79
- args.zip(init).to_h
80
- end
81
- end
82
-
83
- def inspect
84
- params = value.map { |k, v| "#{k}: #{v.inspect}" }
85
- "#{name}(#{params.join(', ')})"
86
- end
87
- end
88
-
89
- # rubocop:disable Metrics/MethodLength
90
- def self.create(parent, args)
91
- if args.include? :value
92
- raise ArgumentError, "#{args} may not contain the reserved name :value"
93
- end
94
-
95
- dt = Class.new(parent)
96
-
97
- dt.instance_eval do
98
- public_class_method :new
99
- include AnyEnum
100
-
101
- define_method(:args) { args }
102
-
103
- define_method(:parent) { parent }
104
- private :parent
105
- end
106
-
107
- case args.count
108
- when 0
109
- dt.instance_eval do
110
- include Nullary
111
-
112
- private :value
113
- end
114
- when 1
115
- dt.instance_eval do
116
- define_method(args[0].to_sym) { value }
117
- end
118
- else
119
- dt.instance_eval do
120
- include Binary
121
-
122
- args.each do |m|
123
- define_method(m) do
124
- @value[m]
125
- end
126
- end
127
- end
128
- end
129
-
130
- dt
131
- end
132
- # rubocop:enable Metrics/MethodLength
133
-
134
- class << self
135
- public :new
136
- end
137
- end
138
-
139
- def method_missing(m, *args)
140
- if @parent.const_defined?(m)
141
- raise ArgumentError, "variant #{m} is already defined for this enum"
142
- end
143
-
144
- @parent.const_set(m, DataType.create(@parent, args))
145
- end
146
-
147
- def respond_to_missing?(_m, _include_all = false)
148
- true
149
- end
150
- end
151
-
152
- module_function
153
-
154
- # rubocop:disable Metrics/AbcSize
155
- # rubocop:disable Metrics/CyclomaticComplexity
156
- # rubocop:disable Metrics/MethodLength
157
- # rubocop:disable Metrics/PerceivedComplexity
158
- def enum(&block)
159
- mod = Class.new do # the enum to be built
160
- private_class_method :new
161
-
162
- def self.match(obj, &block)
163
- # Binding#receiver: stesso risultato di binding.eval('self') senza eval
164
- caller_ctx = block.binding.receiver
165
-
166
- matcher = self::Matcher.new(obj)
167
- matcher.instance_eval(&block)
168
-
169
- # exhaustiveness check su classi memoizzate: niente split/sort di
170
- # stringhe per chiamata
171
- covered = matcher.matches.map { |e| e[1] }
172
- missing = variant_classes.reject { |klass| covered.include?(klass) }
173
- unless missing.empty?
174
- missing_names = missing.map { |klass| klass.name.split('::')[-1].to_sym }
175
- raise Enum::MatchError, "Match is non-exhaustive, #{missing_names} not covered"
176
- end
177
-
178
- type_matches = matcher.matches.select { |r| r[0].is_a?(r[1]) }
179
-
180
- type_matches.each do |match|
181
- obj, _type, block, args, guard = match
182
-
183
- return caller_ctx.instance_eval(&block) if args.empty?
184
-
185
- if args.count != obj.args.count
186
- msg = "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
187
- raise Enum::MatchError, msg
188
- end
189
-
190
- guard_ctx = guard_context(obj, args)
191
- return caller_ctx.instance_exec(* obj.wrapped_values, &block) unless guard
192
-
193
- if guard && guard_ctx.instance_exec(obj, &guard)
194
- return caller_ctx.instance_exec(* obj.wrapped_values, &block)
195
- end
196
- end
197
-
198
- raise Enum::MatchError, "No match could be made"
199
- end
200
-
201
- def self.variants
202
- constants - %i[Matcher MatchError]
203
- end
204
-
205
- def self.variant_classes
206
- @variant_classes ||= variants.map { |v| const_get(v) }.freeze
207
- end
208
-
209
- def self.guard_context(obj, args)
210
- # Struct.new definisce una classe: va fatto una volta per firma,
211
- # non a ogni match con guard
212
- @guard_structs ||= {}
213
- struct = @guard_structs[args] ||= Struct.new(*args)
214
-
215
- if obj.is_a?(FunctionalLightService::EnumBuilder::DataType::Binary)
216
- struct.new(*obj.value.values)
217
- else
218
- struct.new(obj.value)
219
- end
220
- end
221
- end
222
- enum = EnumBuilder.new(mod)
223
- enum.instance_eval(&block)
224
-
225
- type_variants = mod.constants
226
-
227
- matcher = Class.new do
228
- def initialize(obj)
229
- @obj = obj
230
- @matches = []
231
- @vars = []
232
- end
233
-
234
- attr_reader :matches, :vars
235
-
236
- def where(&guard)
237
- guard
238
- end
239
-
240
- type_variants.each do |m|
241
- define_method(m) do |guard = nil, &inner_block|
242
- raise ArgumentError, "No block given to `#{m}`" if inner_block.nil?
243
-
244
- params_spec = inner_block.parameters
245
- if params_spec.any? { |spec| spec.size < 2 }
246
- msg = "Unnamed param found in block parameters: #{params_spec.inspect}"
247
- raise ArgumentError, msg
248
- end
249
- if params_spec.any? { |spec| spec[0] != :req && spec[0] != :opt }
250
- msg = "Only :req & :opt params allowed; parameters=#{params_spec.inspect}"
251
- raise ArgumentError, msg
252
- end
253
-
254
- args = params_spec.map { |spec| spec[1] }
255
-
256
- type = mod.const_get(m)
257
-
258
- guard = nil if guard && !guard.is_a?(Proc)
259
-
260
- @matches << [@obj, type, inner_block, args, guard]
261
- end
262
- end
263
- end
264
-
265
- mod.const_set(:Matcher, matcher)
266
-
267
- type_variants.each do |variant|
268
- mod.singleton_class.class_exec do
269
- define_method(variant) do |*args|
270
- const_get(variant).new(*args)
271
- end
272
- end
273
- end
274
- mod
275
- end
276
- # rubocop:enable Metrics/AbcSize
277
- # rubocop:enable Metrics/CyclomaticComplexity
278
- # rubocop:enable Metrics/MethodLength
279
- # rubocop:enable Metrics/PerceivedComplexity
280
-
281
- def impl(enum_type, &block)
282
- enum_type.variants.each do |v|
283
- enum_type.const_get(v).class_eval(&block)
284
- end
285
- end
286
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Object
4
- def null?
5
- false
6
- end
7
-
8
- def some?
9
- true
10
- end
11
- end
12
-
13
- # rubocop:disable Naming/MethodName
14
- def Maybe(obj)
15
- FunctionalLightService::Deprecations.warn(
16
- "Maybe()/Null are deprecated and will be removed in a future release; " \
17
- "use FunctionalLightService::Option (Some/None) instead"
18
- )
19
- obj.nil? ? Null.instance : obj
20
- end
21
- # rubocop:enable Naming/MethodName
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunctionalLightService
4
- module Monad
5
- class NotMonadError < StandardError; end
6
-
7
- # Basicly the `pure` function
8
- def initialize(init)
9
- @value = join(init)
10
- end
11
-
12
- # If the passed value is monad already, get the value to avoid nesting
13
- # M[M[A]] is equivalent to M[A]
14
- def join(other)
15
- if other.is_a? self.class
16
- other.value
17
- else
18
- other
19
- end
20
- end
21
-
22
- # The functor: takes a function (a -> b) and applies it to the inner value of the monad (Ma),
23
- # boxes it back to the same monad (Mb)
24
- # fmap :: (a -> b) -> M a -> M b
25
- def fmap(proc = nil, &block)
26
- result = (proc || block).call(value)
27
- self.class.new(result)
28
- end
29
-
30
- # The monad: takes a function which returns a monad (of the same type), applies the function
31
- # bind :: (a -> Mb) -> M a -> M b
32
- # the self.class, i.e. the containing monad is passed as a second (optional) arg to the function
33
- def bind(proc = nil, &block)
34
- (proc || block).call(value).tap do |result|
35
- # rubocop:disable Style/CaseEquality
36
- parent = self.class.superclass === Object ? self.class : self.class.superclass
37
- # rubocop:enable Style/CaseEquality
38
- unless result.is_a? parent
39
- raise NotMonadError, "Expected #{result.inspect} to be an #{parent}"
40
- end
41
- end
42
- end
43
- # rubocop:disable Naming/MethodName
44
- alias :'>>=' :bind
45
- # rubocop:enable Naming/MethodName
46
-
47
- # Get the underlying value, return in Haskell
48
- # return :: M a -> a
49
- def value
50
- @value
51
- end
52
-
53
- def to_s
54
- value.to_s
55
- end
56
-
57
- # Two monads are equivalent if they are of the same type and when their values are equal
58
- def ==(other)
59
- return false unless other.is_a? self.class
60
-
61
- @value == other.monad_value
62
- end
63
-
64
- # Reader protetto: #value e' privato nelle varianti Nullary (es. None),
65
- # ma il confronto tra monadi dello stesso tipo deve poter leggere il valore
66
- def monad_value
67
- @value
68
- end
69
- protected :monad_value
70
-
71
- # Return the string representation of the Monad
72
- def inspect
73
- pretty_class_name = self.class.name.split('::')[-1]
74
- "#{pretty_class_name}(#{value.inspect})"
75
- end
76
- end
77
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The simplest NullObject there can be
4
- class Null
5
- class << self
6
- def method_missing(m, *args)
7
- if m == :new
8
- super
9
- else
10
- Null.instance
11
- end
12
- end
13
-
14
- def respond_to_missing?(m, _include_all = false)
15
- m != :new || super
16
- end
17
-
18
- def instance
19
- FunctionalLightService::Deprecations.warn(
20
- "Maybe()/Null are deprecated and will be removed in a future release; " \
21
- "use FunctionalLightService::Option (Some/None) instead"
22
- )
23
- @instance ||= new([])
24
- end
25
-
26
- def null?
27
- true
28
- end
29
-
30
- def some?
31
- false
32
- end
33
-
34
- def mimic(klas)
35
- FunctionalLightService::Deprecations.warn(
36
- "Maybe()/Null are deprecated and will be removed in a future release; " \
37
- "use FunctionalLightService::Option (Some/None) instead"
38
- )
39
- new(klas.instance_methods(false))
40
- end
41
-
42
- def ==(other)
43
- other.respond_to?(:null?) && other.null?
44
- end
45
- end
46
- private_class_method :new
47
-
48
- def initialize(methods)
49
- @methods = methods
50
- end
51
-
52
- # implicit conversions
53
- def to_str
54
- ''
55
- end
56
-
57
- def to_ary
58
- []
59
- end
60
-
61
- def method_missing(m, *args)
62
- return self if respond_to_missing?(m)
63
-
64
- super
65
- end
66
-
67
- def null?
68
- true
69
- end
70
-
71
- def some?
72
- false
73
- end
74
-
75
- # Convenzione Ruby: si estende respond_to_missing?, mai respond_to?
76
- # (il vecchio override aveva anche la firma sbagliata: mancava include_all)
77
- def respond_to_missing?(m, _include_all = false)
78
- @methods.empty? || @methods.include?(m) || super
79
- end
80
-
81
- def inspect
82
- 'Null'
83
- end
84
-
85
- def ==(other)
86
- other.respond_to?(:null?) && other.null?
87
- end
88
- end