params_ready_rails5 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/lib/arel/cte_name.rb +20 -0
  3. data/lib/params_ready/builder.rb +161 -0
  4. data/lib/params_ready/error.rb +31 -0
  5. data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
  6. data/lib/params_ready/extensions/collection.rb +43 -0
  7. data/lib/params_ready/extensions/delegation.rb +25 -0
  8. data/lib/params_ready/extensions/finalizer.rb +26 -0
  9. data/lib/params_ready/extensions/freezer.rb +49 -0
  10. data/lib/params_ready/extensions/hash.rb +46 -0
  11. data/lib/params_ready/extensions/late_init.rb +38 -0
  12. data/lib/params_ready/extensions/registry.rb +44 -0
  13. data/lib/params_ready/extensions/undefined.rb +23 -0
  14. data/lib/params_ready/format.rb +132 -0
  15. data/lib/params_ready/helpers/arel_builder.rb +68 -0
  16. data/lib/params_ready/helpers/callable.rb +14 -0
  17. data/lib/params_ready/helpers/conditional_block.rb +31 -0
  18. data/lib/params_ready/helpers/find_in_hash.rb +22 -0
  19. data/lib/params_ready/helpers/interface_definer.rb +48 -0
  20. data/lib/params_ready/helpers/key_map.rb +176 -0
  21. data/lib/params_ready/helpers/memo.rb +41 -0
  22. data/lib/params_ready/helpers/options.rb +107 -0
  23. data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
  24. data/lib/params_ready/helpers/parameter_storage_class_methods.rb +63 -0
  25. data/lib/params_ready/helpers/parameter_user_class_methods.rb +35 -0
  26. data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
  27. data/lib/params_ready/helpers/rule.rb +76 -0
  28. data/lib/params_ready/helpers/storage.rb +30 -0
  29. data/lib/params_ready/helpers/usage_rule.rb +36 -0
  30. data/lib/params_ready/input_context.rb +31 -0
  31. data/lib/params_ready/intent.rb +70 -0
  32. data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
  33. data/lib/params_ready/marshaller/builder_module.rb +9 -0
  34. data/lib/params_ready/marshaller/collection.rb +165 -0
  35. data/lib/params_ready/marshaller/definition_module.rb +63 -0
  36. data/lib/params_ready/marshaller/enum_set_marshallers.rb +96 -0
  37. data/lib/params_ready/marshaller/parameter_module.rb +11 -0
  38. data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
  39. data/lib/params_ready/marshaller/struct_marshallers.rb +100 -0
  40. data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
  41. data/lib/params_ready/ordering/column.rb +60 -0
  42. data/lib/params_ready/ordering/ordering.rb +276 -0
  43. data/lib/params_ready/output_parameters.rb +138 -0
  44. data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
  45. data/lib/params_ready/pagination/cursor.rb +171 -0
  46. data/lib/params_ready/pagination/direction.rb +148 -0
  47. data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
  48. data/lib/params_ready/pagination/keysets.rb +70 -0
  49. data/lib/params_ready/pagination/nulls.rb +31 -0
  50. data/lib/params_ready/pagination/offset_pagination.rb +130 -0
  51. data/lib/params_ready/pagination/tendency.rb +28 -0
  52. data/lib/params_ready/parameter/abstract_struct_parameter.rb +204 -0
  53. data/lib/params_ready/parameter/array_parameter.rb +197 -0
  54. data/lib/params_ready/parameter/definition.rb +272 -0
  55. data/lib/params_ready/parameter/enum_set_parameter.rb +102 -0
  56. data/lib/params_ready/parameter/parameter.rb +475 -0
  57. data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
  58. data/lib/params_ready/parameter/state.rb +132 -0
  59. data/lib/params_ready/parameter/struct_parameter.rb +64 -0
  60. data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
  61. data/lib/params_ready/parameter/value_parameter.rb +186 -0
  62. data/lib/params_ready/parameter_definer.rb +14 -0
  63. data/lib/params_ready/parameter_user.rb +35 -0
  64. data/lib/params_ready/query/array_grouping.rb +68 -0
  65. data/lib/params_ready/query/custom_predicate.rb +102 -0
  66. data/lib/params_ready/query/exists_predicate.rb +103 -0
  67. data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
  68. data/lib/params_ready/query/grouping.rb +177 -0
  69. data/lib/params_ready/query/join_clause.rb +87 -0
  70. data/lib/params_ready/query/nullness_predicate.rb +71 -0
  71. data/lib/params_ready/query/polymorph_predicate.rb +77 -0
  72. data/lib/params_ready/query/predicate.rb +203 -0
  73. data/lib/params_ready/query/predicate_operator.rb +132 -0
  74. data/lib/params_ready/query/relation.rb +337 -0
  75. data/lib/params_ready/query/structured_grouping.rb +58 -0
  76. data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
  77. data/lib/params_ready/query_context.rb +21 -0
  78. data/lib/params_ready/restriction.rb +252 -0
  79. data/lib/params_ready/result.rb +109 -0
  80. data/lib/params_ready/value/coder.rb +210 -0
  81. data/lib/params_ready/value/constraint.rb +198 -0
  82. data/lib/params_ready/value/custom.rb +56 -0
  83. data/lib/params_ready/value/validator.rb +81 -0
  84. data/lib/params_ready/version.rb +7 -0
  85. data/lib/params_ready.rb +28 -0
  86. metadata +227 -0
@@ -0,0 +1,204 @@
1
+ require_relative 'parameter'
2
+ require_relative '../helpers/key_map'
3
+ require_relative '../marshaller/struct_marshallers'
4
+ require_relative '../marshaller/builder_module'
5
+ require_relative '../marshaller/definition_module'
6
+
7
+ module ParamsReady
8
+ module Parameter
9
+ using Extensions::Hash
10
+
11
+ class AbstractStructParameter < Parameter
12
+ include ComplexParameter
13
+
14
+ def_delegators :@definition, :names, :remap?
15
+ intent_for_children :restriction
16
+
17
+ EMPTY_HASH = '0'
18
+
19
+ freeze_variable :value do |value|
20
+ next if value.nil? || value == Extensions::Undefined
21
+
22
+ value.values.each do |child|
23
+ child.freeze
24
+ end
25
+ end
26
+
27
+ def []=(name, value)
28
+ init_for_write
29
+ c = child(name)
30
+ c.set_value(value)
31
+ end
32
+
33
+ def [](name)
34
+ child(name)
35
+ end
36
+
37
+ def find_in_hash(hash, context)
38
+ found, result = super hash, context
39
+
40
+ if !(default_defined? || optional?) && result == Extensions::Undefined
41
+ # nil value for non-default and non-optional hash means
42
+ # children may be able to set themselves if they have defaults
43
+ [true, {}]
44
+ elsif result == EMPTY_HASH
45
+ [true, {}]
46
+ else
47
+ [found, result]
48
+ end
49
+ end
50
+
51
+ def wrap_output(output, intent)
52
+ if (output.nil? || output.empty?) && !default_defined? && !optional?
53
+ nil
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ def for_output(format, restriction: nil, data: nil)
60
+ restriction ||= Restriction.blanket_permission
61
+
62
+ intent = Intent.new(format, restriction, data: data)
63
+ output = format(intent)
64
+ return {} if output.nil? || output == EMPTY_HASH
65
+ output
66
+ end
67
+
68
+ def for_frontend(format = :frontend, restriction: nil, data: nil)
69
+ for_output(format, restriction: restriction, data: data)
70
+ end
71
+
72
+ def for_model(format = :update, restriction: nil)
73
+ for_output(format, restriction: restriction)
74
+ end
75
+
76
+ protected
77
+
78
+ def child_for_update(path)
79
+ child_name, *path = path
80
+ [self[child_name], child_name, path]
81
+ end
82
+
83
+ def updated_clone(name, updated)
84
+ clone = definition.create
85
+ frozen = frozen? || @value&.frozen?
86
+ clone.populate_with(bare_value, frozen, name => updated)
87
+ end
88
+
89
+ def child(name)
90
+ return nil if is_nil?
91
+
92
+ value = bare_value
93
+ raise ParamsReadyError, "No such name: #{name}" unless names.key? name
94
+ if value.key? name
95
+ value[name]
96
+ else
97
+ child = definition.child_definition(name).create
98
+ raise ParamsReadyError, "Expected definite value for '#{name}' parameter" if child.nil?
99
+ place(name, child) unless frozen?
100
+ child
101
+ end
102
+ end
103
+
104
+ def populate_with(hash, freeze = false, **replacement)
105
+ @value = {}
106
+ names.each_key do |name|
107
+ incoming = replacement[name] || hash[name]
108
+
109
+ own = if freeze && incoming.frozen?
110
+ incoming
111
+ else
112
+ incoming.dup
113
+ end
114
+
115
+ own.freeze if freeze
116
+
117
+ place(name, own)
118
+ end
119
+
120
+ self.freeze if freeze
121
+ self
122
+ end
123
+
124
+ def place(name, child)
125
+ @value[name] = child
126
+ end
127
+
128
+ def init_value
129
+ @value = names.map do |name, definition|
130
+ [name, definition.create]
131
+ end.to_h
132
+ end
133
+ end
134
+
135
+ module AbstractStructParameterBuilder
136
+ include Marshaller::BuilderModule
137
+
138
+ module StructLike
139
+ def add(input, *args, **opts, &block)
140
+ definition = self.class.resolve(input, *args, **opts, &block)
141
+ @definition.add_child definition
142
+ end
143
+ end
144
+ end
145
+
146
+ class AbstractStructParameterDefinition < Definition
147
+ attr_reader :key_map
148
+
149
+ def duplicate_value(value)
150
+ value.values.map do |param|
151
+ [param.name, param.dup]
152
+ end.to_h
153
+ end
154
+
155
+ def freeze_value(value)
156
+ value.values.each(&:freeze)
157
+ value.freeze
158
+ end
159
+
160
+ attr_reader :names
161
+
162
+ def initialize(*args, **options)
163
+ super *args, **options
164
+ @key_map = nil
165
+ @names = {}
166
+ end
167
+
168
+ def child_definition(name)
169
+ raise ParamsReadyError, "No such name: #{name}" unless names.key? name
170
+ names[name]
171
+ end
172
+
173
+ def has_child?(name)
174
+ names.key? name
175
+ end
176
+
177
+ def set_default(value)
178
+ value = infer_default if value == :inferred
179
+ super(value)
180
+ end
181
+
182
+ def infer_default
183
+ names.reduce({}) do |result, pair|
184
+ child_def = pair[1]
185
+ unless child_def.default_defined?
186
+ raise ParamsReadyError, "Can't infer default, child '#{definition.name}' is not optional and has no default" unless child_def.optional?
187
+ else
188
+ result[child_def.name] = child_def.default
189
+ end
190
+ result
191
+ end
192
+ end
193
+
194
+ def add_child(child)
195
+ raise ParamsReadyError, "Not a parameter definition: '#{child.class.name}'" unless child.is_a?(AbstractDefinition)
196
+ raise ParamsReadyError, "Name already there: '#{child.name}'" if @names.key?(child.name)
197
+ raise ParamsReadyError, "Child can't be added after default has been set" if default_defined?
198
+ @names[child.name] = child
199
+ end
200
+
201
+ freeze_variables :names
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,197 @@
1
+ require_relative 'parameter'
2
+ require_relative 'definition'
3
+ require_relative 'abstract_struct_parameter'
4
+ require_relative '../builder'
5
+ require_relative '../marshaller/array_marshallers'
6
+ require_relative '../marshaller/builder_module'
7
+ require_relative '../marshaller/definition_module'
8
+ require_relative '../marshaller/parameter_module'
9
+
10
+ module ParamsReady
11
+ module Parameter
12
+ class ArrayParameter < Parameter
13
+ module ArrayLike
14
+ include ComplexParameter
15
+
16
+ def []=(index, value)
17
+ init_for_write
18
+ c = element(index)
19
+ c.set_value value
20
+ end
21
+
22
+ def [](index)
23
+ if index == :cnt || index == 'cnt'
24
+ count = ValueParameterBuilder.instance(:cnt, :integer).build.create
25
+ count.set_value(self.length)
26
+ count.freeze
27
+ count
28
+ else
29
+ element(index)
30
+ end
31
+ end
32
+
33
+ protected
34
+
35
+ def updated_clone(index, updated_child)
36
+ clone = definition.create
37
+ clone.populate_with(bare_value, frozen?, { index => updated_child })
38
+ clone
39
+ end
40
+
41
+ def child_for_update(path)
42
+ index, *child_path = path
43
+ child = element(index)
44
+ raise ParamsReadyError, "No element at index '#{index}' in '#{name}'" if child.nil?
45
+ [child, index, child_path]
46
+ end
47
+
48
+ def populate_with(array, freeze = false, replacement = {})
49
+ @value = []
50
+ array.each_with_index do |element, index|
51
+ incoming = replacement[index] || element
52
+
53
+ own = if freeze && incoming.frozen?
54
+ incoming
55
+ else
56
+ incoming.dup
57
+ end
58
+
59
+ own.freeze if freeze
60
+
61
+ @value << own
62
+ end
63
+
64
+ self.freeze if freeze
65
+ self
66
+ end
67
+ end
68
+
69
+ include ArrayLike
70
+ include Marshaller::ParameterModule
71
+
72
+ intent_for_children :delegate do
73
+ definition.prototype.name
74
+ end
75
+
76
+ def_delegators :@definition, :prototype
77
+ def_delegators :bare_value, :length, :count, :each, :each_with_index, :map, :reduce, :to_a
78
+
79
+ freeze_variable :value do |array|
80
+ array.each(&:freeze)
81
+ end
82
+
83
+ def <<(value)
84
+ init_for_write
85
+ c = element(length, for_write: true)
86
+ c.set_value value
87
+ self
88
+ end
89
+
90
+ protected
91
+
92
+ def element(index, for_write: false)
93
+ return nil if is_nil?
94
+
95
+ value = bare_value
96
+ if value.length > index
97
+ value[index]
98
+ elsif value.length == index && for_write
99
+ value << prototype.create
100
+ value[index]
101
+ else
102
+ nil
103
+ end
104
+ end
105
+
106
+ def init_value
107
+ @value = []
108
+ end
109
+ end
110
+
111
+ class ArrayParameterBuilder < Builder
112
+ module ArrayLike
113
+ def prototype(input, name = nil, *args, altn: nil, **opts, &block)
114
+ name ||= :element
115
+ altn ||= :elm
116
+ definition = self.class.resolve(input, name, *args, altn: altn, **opts, &block)
117
+ @definition.set_prototype definition
118
+ end
119
+ end
120
+
121
+ include ArrayLike
122
+ register :array
123
+ include Marshaller::BuilderModule
124
+
125
+ def self.instance(name, altn: nil)
126
+ new ArrayParameterDefinition.new(name, altn: altn)
127
+ end
128
+
129
+ def compact
130
+ @definition.set_compact true
131
+ end
132
+ end
133
+
134
+ class ArrayParameterDefinition < Definition
135
+ name_for_formatter :array
136
+ include Marshaller::DefinitionModule[Marshaller::ArrayMarshallers.collection]
137
+
138
+ module ArrayLike
139
+ def duplicate_value(value)
140
+ value.map do |param|
141
+ param.dup
142
+ end
143
+ end
144
+
145
+ def freeze_value(value)
146
+ value.each(&:freeze)
147
+ value.freeze
148
+ end
149
+ end
150
+
151
+ include ArrayLike
152
+
153
+ parameter_class ArrayParameter
154
+
155
+ def initialize(*args, prototype: nil, **options)
156
+ raise ParamsReadyError, "Not a definition: #{prototype.class.name}" unless (prototype.is_a?(Definition) || prototype.nil?)
157
+ @prototype = prototype
158
+ super *args, **options
159
+ end
160
+
161
+ def set_default(args)
162
+ raise ParamsReadyError, "Can't set default before prototype has been defined" if @prototype.nil?
163
+ super
164
+ end
165
+
166
+ late_init :prototype do |prototype|
167
+ raise ParamsReadyError, "Can't set prototype after default has been set" if default_defined?
168
+ prototype
169
+ end
170
+
171
+ late_init :compact, obligatory: false, boolean: true, getter: false
172
+
173
+ def ensure_canonical(array)
174
+ raise "Not a canonical value, array expected, got: '#{array.class.name}'" unless array.is_a? Array
175
+ context = Format.instance(:backend)
176
+ value, _validator = try_canonicalize(array, context, nil, freeze: true)
177
+ value
178
+ end
179
+
180
+ def try_canonicalize(value, context, validator = nil, freeze: false)
181
+ if freeze
182
+ marshaller = marshallers.instance(Array)
183
+ marshaller.canonicalize self, value, context, validator, freeze: freeze
184
+ else
185
+ super value, context, validator
186
+ end
187
+ end
188
+
189
+ def finish
190
+ if compact? && (prototype&.default_defined?)
191
+ raise ParamsReadyError, 'Prototype must not be optional nor have default in compact array parameter'
192
+ end
193
+ super
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,272 @@
1
+ require_relative '../extensions/freezer'
2
+ require_relative '../extensions/finalizer'
3
+ require_relative '../error'
4
+ require_relative '../extensions/class_reader_writer'
5
+ require_relative '../extensions/collection'
6
+ require_relative '../extensions/delegation'
7
+ require_relative '../extensions/late_init'
8
+ require_relative '../extensions/undefined'
9
+ require_relative '../input_context'
10
+ require_relative '../result'
11
+ require_relative '../helpers/conditional_block'
12
+ require_relative '../helpers/callable'
13
+
14
+ module ParamsReady
15
+ module Parameter
16
+ class AbstractDefinition
17
+ extend Extensions::Freezer
18
+ include Extensions::Freezer::InstanceMethods
19
+ extend Extensions::Finalizer
20
+ include Extensions::Finalizer::InstanceMethods
21
+ extend Extensions::ClassReaderWriter
22
+ extend Extensions::LateInit
23
+ extend Extensions::Collection
24
+
25
+ attr_reader :name, :altn
26
+
27
+ def parameter_class
28
+ self.class.parameter_class
29
+ end
30
+
31
+ def initialize(name, altn: nil)
32
+ @name = name.to_sym
33
+ @altn = normalize_alternative_name(altn || name)
34
+ end
35
+
36
+ def normalize_alternative_name(name)
37
+ if name.is_a? Array
38
+ name.map(&:to_sym)
39
+ else
40
+ name.to_sym
41
+ end
42
+ end
43
+
44
+ class_reader_writer :parameter_class
45
+ collection :helpers, :helper
46
+
47
+ def create
48
+ raise ParamsReadyError, "Can't create '#{name}' using unfrozen definition" unless frozen?
49
+ instance = parameter_class.new self
50
+ if @helpers
51
+ singleton = class << instance; self; end
52
+ @helpers.each do |(name, block)|
53
+ if singleton.method_defined? name
54
+ raise ParamsReadyError, "Helper '#{name}' overrides existing method"
55
+ end
56
+
57
+ singleton.send :define_method, name, &block
58
+ end
59
+ end
60
+ instance
61
+ end
62
+
63
+ def from_hash(hash, context: nil, validator: Result.new(name))
64
+ context = InputContext.resolve(context)
65
+ instance = create
66
+ result = instance.set_from_hash(hash, context: context, validator: validator)
67
+ [result, instance]
68
+ end
69
+
70
+ def from_input(input, context: nil, validator: nil)
71
+ validator ||= Result.new(name)
72
+ context = InputContext.resolve(context)
73
+ instance = create
74
+ result = instance.set_from_input(input, context, validator)
75
+ [result, instance]
76
+ end
77
+
78
+ def finish
79
+ super
80
+ freeze
81
+ end
82
+ end
83
+
84
+ class Definition < AbstractDefinition
85
+ attr_reader :default
86
+
87
+ class_reader_writer :name_for_formatter
88
+
89
+ def name_for_formatter
90
+ self.class.name_for_formatter
91
+ end
92
+
93
+ def initialize(
94
+ *args,
95
+ default: Extensions::Undefined,
96
+ optional: false,
97
+ preprocessor: nil,
98
+ populator: nil,
99
+ postprocessor: nil,
100
+ no_input: nil,
101
+ no_output: nil,
102
+ **opts
103
+ )
104
+ super *args, **opts
105
+ @default = Extensions::Undefined
106
+ @optional = optional
107
+ @preprocessor = preprocessor
108
+ @postprocessor = postprocessor
109
+ @populator = populator
110
+ @no_input = no_input
111
+ @no_output = no_output
112
+
113
+ set_default(default) unless default == Extensions::Undefined
114
+ end
115
+
116
+ def default_defined?
117
+ return false unless defined? @default
118
+ return false if @default == Extensions::Undefined
119
+ true
120
+ end
121
+
122
+ def canonical_default(value)
123
+ return value if value.nil?
124
+ ensure_canonical value
125
+ rescue => e
126
+ raise ParamsReadyError, "Invalid default: #{e.message}"
127
+ end
128
+
129
+ late_init :populator, getter: true, once: true, obligatory: false
130
+ late_init :no_input, getter: false, once: false
131
+ late_init :no_output, getter: false, once: false
132
+ late_init :memoize, getter: true, obligatory: false
133
+
134
+ def memoize?
135
+ return false if @memoize.nil?
136
+
137
+ @memoize > 0
138
+ end
139
+
140
+ def set_preprocessor(rule: nil, &block)
141
+ raise "Preprocesser already set in '#{name}'" unless @preprocessor.nil?
142
+ @preprocessor = Helpers::ConditionalBlock.new(rule: rule, &block)
143
+ end
144
+
145
+ def set_postprocessor(rule: nil, &block)
146
+ raise "Postprocessor already set in '#{name}'" unless @postprocessor.nil?
147
+ @postprocessor = Helpers::ConditionalBlock.new(rule: rule, &block)
148
+ end
149
+
150
+ def preprocess(input, context, validator)
151
+ return input if @preprocessor.nil?
152
+ return input unless @preprocessor.perform?(!context.local?, context.name)
153
+ @preprocessor.block.call input, context, self
154
+ rescue => error
155
+ preprocessor_error = PreprocessorError.new(error)
156
+ if validator.nil?
157
+ raise preprocessor_error
158
+ else
159
+ validator.error! preprocessor_error
160
+ Extensions::Undefined
161
+ end
162
+ end
163
+
164
+ def postprocess(param, context, validator)
165
+ return if @postprocessor.nil?
166
+ return unless @postprocessor.perform?(!context.local?, context.name)
167
+ @postprocessor.block.call param, context
168
+ rescue => error
169
+ postprocessor_error = PostprocessorError.new(error)
170
+ if validator.nil?
171
+ raise postprocessor_error
172
+ else
173
+ validator.error! postprocessor_error
174
+ end
175
+ validator
176
+ end
177
+
178
+ def set_no_input(*arr, rule: nil)
179
+ @no_input = Helpers::Rule(rule) || true
180
+ raise ParamsReadyError, "Default not expected: #{arr}" if rule == false
181
+
182
+ set_default *arr unless arr.empty?
183
+ end
184
+
185
+ def set_local(*arr, rule: nil)
186
+ rule = Helpers::Rule(rule)
187
+ set_no_input(*arr, rule: rule)
188
+ set_no_output(rule || true)
189
+ end
190
+
191
+ def no_input?(format)
192
+ restricted_for_format?(@no_input, format)
193
+ end
194
+
195
+ def no_output?(format)
196
+ restricted_for_format?(@no_output, format)
197
+ end
198
+
199
+ def restricted_for_format?(rule, format)
200
+ case rule
201
+ when nil, false
202
+ false
203
+ when true
204
+ !format.local?
205
+ when Helpers::Rule
206
+ rule.include?(format.name)
207
+ else
208
+ raise ParamsReadyError, "Unexpected rule: #{rule}"
209
+ end
210
+ end
211
+
212
+ late_init :optional, boolean: true, getter: false, once: false
213
+
214
+ late_init :default, once: false, definite: false do |value|
215
+ next value if value == Extensions::Undefined
216
+ next value if value.is_a? Helpers::Callable
217
+
218
+ canonical = canonical_default(value)
219
+ next canonical if canonical.nil?
220
+
221
+ freeze_value(canonical)
222
+ end
223
+
224
+ def fetch_default(duplicate: true)
225
+ return Extensions::Undefined unless default_defined?
226
+ return nil if @default.nil?
227
+
228
+ if @default.is_a?(Helpers::Callable)
229
+ fetch_callable_default
230
+ else
231
+ return @default unless duplicate
232
+ duplicate_value(@default)
233
+ end
234
+ end
235
+
236
+ def fetch_callable_default
237
+ value = @default.call
238
+ value = ensure_canonical(value)
239
+ duplicate_value(value)
240
+ rescue StandardError => e
241
+ raise ParamsReadyError, "Invalid default: #{e.message}"
242
+ end
243
+
244
+
245
+ def finish
246
+ if @populator && !@no_input
247
+ raise ParamsReadyError, "Populator set for input parameter '#{name}'"
248
+ end
249
+
250
+ if @preprocessor && @no_input == true
251
+ raise ParamsReadyError, "Preprocessor set for no-input parameter '#{name}'"
252
+ end
253
+
254
+ if @postprocessor && @no_input == true
255
+ raise ParamsReadyError, "Postprocessor set for no-input parameter '#{name}'"
256
+ end
257
+
258
+ super
259
+ end
260
+ end
261
+
262
+ module DelegatingDefinition
263
+ def self.[](delegee_name)
264
+ mod = Module.new
265
+ Extensions::Delegation.delegate(mod) do
266
+ instance_variable_get(:"@#{delegee_name}")
267
+ end
268
+ mod
269
+ end
270
+ end
271
+ end
272
+ end