params_ready 0.0.2 → 0.0.8

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/params_ready/builder.rb +21 -0
  3. data/lib/params_ready/extensions/delegation.rb +1 -0
  4. data/lib/params_ready/extensions/freezer.rb +2 -0
  5. data/lib/params_ready/extensions/undefined.rb +8 -0
  6. data/lib/params_ready/format.rb +4 -2
  7. data/lib/params_ready/helpers/arel_builder.rb +96 -35
  8. data/lib/params_ready/helpers/callable.rb +14 -0
  9. data/lib/params_ready/helpers/interface_definer.rb +48 -0
  10. data/lib/params_ready/helpers/memo.rb +0 -1
  11. data/lib/params_ready/helpers/options.rb +77 -9
  12. data/lib/params_ready/helpers/parameter_storage_class_methods.rb +27 -0
  13. data/lib/params_ready/helpers/parameter_user_class_methods.rb +18 -14
  14. data/lib/params_ready/helpers/rule.rb +30 -11
  15. data/lib/params_ready/helpers/usage_rule.rb +21 -3
  16. data/lib/params_ready/marshaller/array_marshallers.rb +4 -3
  17. data/lib/params_ready/marshaller/{hash_set_marshallers.rb → enum_set_marshallers.rb} +5 -5
  18. data/lib/params_ready/marshaller/polymorph_marshallers.rb +2 -2
  19. data/lib/params_ready/marshaller/{hash_marshallers.rb → struct_marshallers.rb} +5 -5
  20. data/lib/params_ready/marshaller/tuple_marshallers.rb +2 -2
  21. data/lib/params_ready/ordering/column.rb +1 -1
  22. data/lib/params_ready/output_parameters.rb +13 -2
  23. data/lib/params_ready/pagination/keyset_pagination.rb +5 -5
  24. data/lib/params_ready/parameter/{abstract_hash_parameter.rb → abstract_struct_parameter.rb} +6 -6
  25. data/lib/params_ready/parameter/array_parameter.rb +2 -2
  26. data/lib/params_ready/parameter/definition.rb +48 -40
  27. data/lib/params_ready/parameter/{hash_set_parameter.rb → enum_set_parameter.rb} +11 -10
  28. data/lib/params_ready/parameter/parameter.rb +48 -29
  29. data/lib/params_ready/parameter/state.rb +4 -4
  30. data/lib/params_ready/parameter/{hash_parameter.rb → struct_parameter.rb} +11 -10
  31. data/lib/params_ready/parameter/tuple_parameter.rb +1 -1
  32. data/lib/params_ready/parameter/value_parameter.rb +14 -10
  33. data/lib/params_ready/parameter_user.rb +7 -15
  34. data/lib/params_ready/query/array_grouping.rb +4 -4
  35. data/lib/params_ready/query/exists_predicate.rb +3 -3
  36. data/lib/params_ready/query/grouping.rb +8 -2
  37. data/lib/params_ready/query/join_clause.rb +91 -28
  38. data/lib/params_ready/query/predicate.rb +3 -3
  39. data/lib/params_ready/query/relation.rb +29 -14
  40. data/lib/params_ready/query/structured_grouping.rb +4 -4
  41. data/lib/params_ready/query/variable_operator_predicate.rb +12 -12
  42. data/lib/params_ready/value/coder.rb +36 -8
  43. data/lib/params_ready/version.rb +7 -0
  44. data/lib/params_ready.rb +3 -11
  45. metadata +56 -10
@@ -1,9 +1,9 @@
1
1
  require_relative 'collection'
2
- require_relative 'hash_marshallers'
2
+ require_relative 'struct_marshallers'
3
3
 
4
4
  module ParamsReady
5
5
  module Marshaller
6
- class HashSetMarshallers
6
+ class EnumSetMarshallers
7
7
  module AbstractMarshaller
8
8
  def canonicalize_collection(definition, context, validator, freeze: false)
9
9
  hash = {}
@@ -18,7 +18,7 @@ module ParamsReady
18
18
  end
19
19
  end
20
20
 
21
- module HashMarshaller
21
+ module StructMarshaller
22
22
  extend AbstractMarshaller
23
23
 
24
24
  def self.canonicalize(definition, hash, context, validator)
@@ -30,7 +30,7 @@ module ParamsReady
30
30
 
31
31
  def self.marshal(parameter, intent)
32
32
  if intent.marshal? parameter.name_for_formatter
33
- HashMarshallers::HashMarshaller.marshal(parameter, intent)
33
+ StructMarshallers::StructMarshaller.marshal(parameter, intent)
34
34
  else
35
35
  SetMarshaller.marshal(parameter, intent)
36
36
  end
@@ -83,7 +83,7 @@ module ParamsReady
83
83
  def self.collection
84
84
  @collection ||= begin
85
85
  c = ClassCollection.new Hash
86
- c.add_instance Hash, HashMarshaller
86
+ c.add_instance Hash, StructMarshaller
87
87
  c.add_instance Set, SetMarshaller
88
88
  c.add_instance Array, ArrayMarshaller
89
89
  c.default!(Hash)
@@ -3,7 +3,7 @@ require_relative 'collection'
3
3
  module ParamsReady
4
4
  module Marshaller
5
5
  class PolymorphMarshallers
6
- class HashMarshaller
6
+ class StructMarshaller
7
7
  attr_reader :type_identifier
8
8
 
9
9
  def self.instance(type_identifier:)
@@ -57,7 +57,7 @@ module ParamsReady
57
57
  def self.collection
58
58
  @collection ||= begin
59
59
  c = ClassCollection.new Hash
60
- c.add_factory :hash, HashMarshaller
60
+ c.add_factory :hash, StructMarshaller
61
61
  c.freeze
62
62
  c
63
63
  end
@@ -4,7 +4,7 @@ require_relative 'collection'
4
4
 
5
5
  module ParamsReady
6
6
  module Marshaller
7
- class HashMarshallers
7
+ class StructMarshallers
8
8
  module AbstractMarshaller
9
9
  def extract_bare_value(parameter, intent)
10
10
  parameter.names.keys.reduce({}) do |result, name|
@@ -27,11 +27,11 @@ module ParamsReady
27
27
  def self.canonicalize(definition, string, context, validator)
28
28
  json = Base64.urlsafe_decode64(string)
29
29
  hash = JSON.parse(json)
30
- HashMarshaller.canonicalize(definition, hash, context, validator)
30
+ StructMarshaller.canonicalize(definition, hash, context, validator)
31
31
  end
32
32
 
33
33
  def self.marshal(parameter, intent)
34
- hash = HashMarshaller.marshal(parameter, intent)
34
+ hash = StructMarshaller.marshal(parameter, intent)
35
35
  json = JSON.generate(hash)
36
36
  Base64.urlsafe_encode64(json)
37
37
  end
@@ -39,7 +39,7 @@ module ParamsReady
39
39
  freeze
40
40
  end
41
41
 
42
- module HashMarshaller
42
+ module StructMarshaller
43
43
  extend AbstractMarshaller
44
44
 
45
45
  def self.canonicalize(definition, hash, context, validator, freeze: false)
@@ -88,7 +88,7 @@ module ParamsReady
88
88
  def self.collection
89
89
  @collection ||= begin
90
90
  c = ClassCollection.new Hash
91
- c.add_instance Hash, HashMarshaller
91
+ c.add_instance Hash, StructMarshaller
92
92
  c.add_factory :base64, Base64Marshaller
93
93
  c.default!(Hash)
94
94
  c.freeze
@@ -43,7 +43,7 @@ module ParamsReady
43
43
  freeze
44
44
  end
45
45
 
46
- module HashMarshaller
46
+ module StructMarshaller
47
47
  extend AbstractMarshaller
48
48
 
49
49
  def self.canonicalize(definition, hash, context, validator)
@@ -92,7 +92,7 @@ module ParamsReady
92
92
  @collection ||= begin
93
93
  c = ClassCollection.new Array
94
94
  c.add_instance Array, ArrayMarshaller
95
- c.add_instance Hash, HashMarshaller
95
+ c.add_instance Hash, StructMarshaller
96
96
  c.add_factory :string, StringMarshaller
97
97
  c.freeze
98
98
  c
@@ -27,7 +27,7 @@ module ParamsReady
27
27
 
28
28
  def attribute(name, default_table, context)
29
29
  arel_table = table || default_table
30
- arel_builder = Helpers::ArelBuilder.instance(expression(name), arel_table: arel_table)
30
+ arel_builder = Helpers::ArelBuilder::Attribute.instance(expression(name), arel_table: arel_table)
31
31
  arel_builder.to_arel(arel_table, context, self)
32
32
  end
33
33
 
@@ -72,6 +72,17 @@ module ParamsReady
72
72
  end
73
73
  end
74
74
 
75
+
76
+ def to_a
77
+ if @parameter.definition.is_a? Parameter::ArrayParameterDefinition
78
+ (0...@parameter.length).map do |n|
79
+ self[n]
80
+ end
81
+ else
82
+ raise ParamsReadyError, "Unimplemented method 'to_a' for #{@parameter.definition.class.name}"
83
+ end
84
+ end
85
+
75
86
  def flat_pairs(format = @intent.format, restriction: @intent.restriction, data: @intent.data)
76
87
  self.class.flatten_hash(for_output(format, restriction: restriction, data: data), scoped_name)
77
88
  end
@@ -104,8 +115,8 @@ module ParamsReady
104
115
  @parameter.for_frontend(restriction: restriction, data: data)
105
116
  end
106
117
 
107
- def for_model(restriction: @intent.restriction)
108
- @parameter.for_model(restriction: restriction)
118
+ def for_model(format = :update, restriction: @intent.restriction)
119
+ @parameter.for_model(format, restriction: restriction)
109
120
  end
110
121
 
111
122
  def format(format = @intent)
@@ -1,4 +1,4 @@
1
- require_relative '../parameter/hash_parameter'
1
+ require_relative '../parameter/struct_parameter'
2
2
  require_relative '../value/constraint'
3
3
  require_relative '../helpers/arel_builder'
4
4
  require_relative 'abstract_pagination'
@@ -6,7 +6,7 @@ require_relative 'direction'
6
6
 
7
7
  module ParamsReady
8
8
  module Pagination
9
- class KeysetPagination < Parameter::HashParameter
9
+ class KeysetPagination < Parameter::StructParameter
10
10
  include AbstractPagination
11
11
 
12
12
  def select_keysets(query, limit, direction, keyset, ordering, arel_table, context)
@@ -177,7 +177,7 @@ module ParamsReady
177
177
  end
178
178
  end
179
179
 
180
- class KeysetPaginationDefinition < Parameter::HashParameterDefinition
180
+ class KeysetPaginationDefinition < Parameter::StructParameterDefinition
181
181
  MIN_LIMIT = 1
182
182
 
183
183
  parameter_class KeysetPagination
@@ -212,14 +212,14 @@ module ParamsReady
212
212
  class KeysetPaginationBuilder
213
213
  def initialize(ordering_builder, default_limit, max_limit = nil)
214
214
  definition = KeysetPaginationDefinition.new(default_limit, max_limit)
215
- @cursor_builder = Parameter::HashParameterBuilder.send :new, definition
215
+ @cursor_builder = Parameter::StructParameterBuilder.send :new, definition
216
216
  @default = {
217
217
  limit: default_limit,
218
218
  direction: :aft,
219
219
  keyset: {}
220
220
  }
221
221
  @ordering_builder = ordering_builder
222
- @keyset = Parameter::HashParameterBuilder.instance(:keyset, altn: :ks)
222
+ @keyset = Parameter::StructParameterBuilder.instance(:keyset, altn: :ks)
223
223
  end
224
224
 
225
225
  def key(type, name, direction, &block)
@@ -1,6 +1,6 @@
1
1
  require_relative 'parameter'
2
2
  require_relative '../helpers/key_map'
3
- require_relative '../marshaller/hash_marshallers'
3
+ require_relative '../marshaller/struct_marshallers'
4
4
  require_relative '../marshaller/builder_module'
5
5
  require_relative '../marshaller/definition_module'
6
6
 
@@ -8,7 +8,7 @@ module ParamsReady
8
8
  module Parameter
9
9
  using Extensions::Hash
10
10
 
11
- class AbstractHashParameter < Parameter
11
+ class AbstractStructParameter < Parameter
12
12
  include ComplexParameter
13
13
 
14
14
  def_delegators :@definition, :names, :remap?
@@ -69,7 +69,7 @@ module ParamsReady
69
69
  for_output(format, restriction: restriction, data: data)
70
70
  end
71
71
 
72
- def for_model(format = :attributes, restriction: nil)
72
+ def for_model(format = :update, restriction: nil)
73
73
  for_output(format, restriction: restriction)
74
74
  end
75
75
 
@@ -132,10 +132,10 @@ module ParamsReady
132
132
  end
133
133
  end
134
134
 
135
- module AbstractHashParameterBuilder
135
+ module AbstractStructParameterBuilder
136
136
  include Marshaller::BuilderModule
137
137
 
138
- module HashLike
138
+ module StructLike
139
139
  def add(input, *args, **opts, &block)
140
140
  definition = self.class.resolve(input, *args, **opts, &block)
141
141
  @definition.add_child definition
@@ -143,7 +143,7 @@ module ParamsReady
143
143
  end
144
144
  end
145
145
 
146
- class AbstractHashParameterDefinition < Definition
146
+ class AbstractStructParameterDefinition < Definition
147
147
  attr_reader :key_map
148
148
 
149
149
  def duplicate_value(value)
@@ -1,6 +1,6 @@
1
1
  require_relative 'parameter'
2
2
  require_relative 'definition'
3
- require_relative 'abstract_hash_parameter'
3
+ require_relative 'abstract_struct_parameter'
4
4
  require_relative '../builder'
5
5
  require_relative '../marshaller/array_marshallers'
6
6
  require_relative '../marshaller/builder_module'
@@ -74,7 +74,7 @@ module ParamsReady
74
74
  end
75
75
 
76
76
  def_delegators :@definition, :prototype
77
- def_delegators :bare_value, :length, :each, :each_with_index, :map, :reduce, :to_a
77
+ def_delegators :bare_value, :length, :count, :each, :each_with_index, :map, :reduce, :to_a
78
78
 
79
79
  freeze_variable :value do |array|
80
80
  array.each(&:freeze)
@@ -9,6 +9,7 @@ require_relative '../extensions/undefined'
9
9
  require_relative '../input_context'
10
10
  require_relative '../result'
11
11
  require_relative '../helpers/conditional_block'
12
+ require_relative '../helpers/callable'
12
13
 
13
14
  module ParamsReady
14
15
  module Parameter
@@ -96,7 +97,7 @@ module ParamsReady
96
97
  preprocessor: nil,
97
98
  populator: nil,
98
99
  postprocessor: nil,
99
- local: false,
100
+ no_input: nil,
100
101
  no_output: nil,
101
102
  **opts
102
103
  )
@@ -106,7 +107,7 @@ module ParamsReady
106
107
  @preprocessor = preprocessor
107
108
  @postprocessor = postprocessor
108
109
  @populator = populator
109
- @local = local
110
+ @no_input = no_input
110
111
  @no_output = no_output
111
112
 
112
113
  set_default(default) unless default == Extensions::Undefined
@@ -126,6 +127,7 @@ module ParamsReady
126
127
  end
127
128
 
128
129
  late_init :populator, getter: true, once: true, obligatory: false
130
+ late_init :no_input, getter: false, once: false
129
131
  late_init :no_output, getter: false, once: false
130
132
  late_init :memoize, getter: true, obligatory: false
131
133
 
@@ -173,52 +175,45 @@ module ParamsReady
173
175
  validator
174
176
  end
175
177
 
176
- def set_local(*arr, rule: nil)
177
- if rule.nil?
178
- @local = true
179
- else
180
- @local = Helpers::Rule(rule)
181
- end
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
182
  set_default *arr unless arr.empty?
183
183
  end
184
184
 
185
- def local?(format)
186
- case @local
187
- when nil, false
188
- false
189
- when true
190
- !format.local?
191
- when Helpers::Rule
192
- @local.include? format.name
193
- else
194
- raise ParamsReadyError, "Unexpected rule: #{@local}"
195
- end
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)
196
193
  end
197
194
 
198
195
  def no_output?(format)
199
- case @no_output
196
+ restricted_for_format?(@no_output, format)
197
+ end
198
+
199
+ def restricted_for_format?(rule, format)
200
+ case rule
200
201
  when nil, false
201
- return local?(format)
202
+ false
202
203
  when true
203
- return !format.local?
204
+ !format.local?
204
205
  when Helpers::Rule
205
- @no_output.include? format.name
206
+ rule.include?(format.name)
206
207
  else
207
- raise ParamsReadyError, "Unexpected option: '#{@no_output}'"
208
+ raise ParamsReadyError, "Unexpected rule: #{rule}"
208
209
  end
209
210
  end
210
211
 
211
- late_init :optional, boolean: true, getter: false, once: false do |value|
212
- next value if value == false
213
- raise ParamsReadyError, "Optional parameter can't have default" if default_defined?
214
-
215
- value
216
- end
212
+ late_init :optional, boolean: true, getter: false, once: false
217
213
 
218
214
  late_init :default, once: false, definite: false do |value|
219
215
  next value if value == Extensions::Undefined
220
-
221
- raise ParamsReadyError, "Optional parameter can't have default" if optional?
216
+ next value if value.is_a? Helpers::Callable
222
217
 
223
218
  canonical = canonical_default(value)
224
219
  next canonical if canonical.nil?
@@ -226,25 +221,38 @@ module ParamsReady
226
221
  freeze_value(canonical)
227
222
  end
228
223
 
229
- def duplicate_default
224
+ def fetch_default(duplicate: true)
230
225
  return Extensions::Undefined unless default_defined?
231
226
  return nil if @default.nil?
232
227
 
233
- duplicate_value(@default)
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}"
234
242
  end
235
243
 
236
244
 
237
245
  def finish
238
- if @populator && !@local
239
- raise ParamsReadyError, "Populator set for non-local parameter '#{name}'"
246
+ if @populator && !@no_input
247
+ raise ParamsReadyError, "Populator set for input parameter '#{name}'"
240
248
  end
241
249
 
242
- if @preprocessor && @local
243
- raise ParamsReadyError, "Preprocessor set for local parameter '#{name}'"
250
+ if @preprocessor && @no_input == true
251
+ raise ParamsReadyError, "Preprocessor set for no-input parameter '#{name}'"
244
252
  end
245
253
 
246
- if @postprocessor && @local
247
- raise ParamsReadyError, "Postprocessor set for local parameter '#{name}'"
254
+ if @postprocessor && @no_input == true
255
+ raise ParamsReadyError, "Postprocessor set for no-input parameter '#{name}'"
248
256
  end
249
257
 
250
258
  super
@@ -1,13 +1,13 @@
1
1
  require 'set'
2
- require_relative 'hash_parameter'
2
+ require_relative 'struct_parameter'
3
3
  require_relative 'value_parameter'
4
4
  require_relative '../intent'
5
- require_relative '../marshaller/hash_set_marshallers'
5
+ require_relative '../marshaller/enum_set_marshallers'
6
6
  require_relative '../marshaller/parameter_module'
7
7
 
8
8
  module ParamsReady
9
9
  module Parameter
10
- class HashSetParameter < AbstractHashParameter
10
+ class EnumSetParameter < AbstractStructParameter
11
11
  include Marshaller::ParameterModule
12
12
 
13
13
  def self.intent_for_set(intent)
@@ -31,13 +31,14 @@ module ParamsReady
31
31
  end
32
32
  end
33
33
 
34
- class HashSetParameterBuilder < Builder
34
+ class EnumSetParameterBuilder < Builder
35
35
  include Marshaller::BuilderModule
36
36
 
37
- register :hash_set
37
+ register :enum_set
38
+ register_deprecated :hash_set, use: :enum_set
38
39
 
39
40
  def self.instance(name, altn: nil, type: :boolean)
40
- new HashSetParameterDefinition.new(name, altn: altn, type: type)
41
+ new EnumSetParameterDefinition.new(name, altn: altn, type: type)
41
42
  end
42
43
 
43
44
  def add(input, *args, val: nil, **opts, &block)
@@ -56,12 +57,12 @@ module ParamsReady
56
57
  end
57
58
  end
58
59
 
59
- class HashSetParameterDefinition < AbstractHashParameterDefinition
60
+ class EnumSetParameterDefinition < AbstractStructParameterDefinition
60
61
  attr_reader :type, :values
61
62
  freeze_variable :values
62
- name_for_formatter :hash_set
63
- parameter_class HashSetParameter
64
- include Marshaller::DefinitionModule[Marshaller::HashSetMarshallers.collection]
63
+ name_for_formatter :enum_set
64
+ parameter_class EnumSetParameter
65
+ include Marshaller::DefinitionModule[Marshaller::EnumSetMarshallers.collection]
65
66
 
66
67
  def initialize(*args, type: :boolean, **opts)
67
68
  @type = type
@@ -8,8 +8,12 @@ module ParamsReady
8
8
  module Parameter
9
9
  module FromHash
10
10
  def set_from_hash(hash, context: nil, validator: Result.new(name))
11
- _, input = find_in_hash hash, context
12
- set_from_input(input, context, validator)
11
+ if no_input?(context)
12
+ populate(context, validator)
13
+ else
14
+ _, input = find_in_hash hash, context
15
+ set_from_input(input, context, validator)
16
+ end
13
17
  end
14
18
  end
15
19
 
@@ -184,7 +188,9 @@ module ParamsReady
184
188
  end
185
189
 
186
190
  def inspect
187
- "#{self.class.name.split("::").last} #{self.name}: { #{inspect_content} }"
191
+ preserve = Format.instance(:inspect).preserve?(self)
192
+ content = preserve ? inspect_content : '[FILTERED]'
193
+ "#{self.class.name.split("::").last} #{self.name}: { #{content} }"
188
194
  end
189
195
 
190
196
  def dup
@@ -205,7 +211,7 @@ module ParamsReady
205
211
 
206
212
  class Parameter < AbstractParameter
207
213
  def_delegators :@definition,
208
- :default, :optional?, :default_defined?, :constraints, :no_output?, :local?
214
+ :default, :optional?, :default_defined?, :constraints, :no_output?, :no_input?
209
215
 
210
216
  def initialize(definition)
211
217
  @value = Extensions::Undefined
@@ -248,6 +254,7 @@ module ParamsReady
248
254
 
249
255
  def is_definite?
250
256
  return true if @value != Extensions::Undefined && !@value.nil?
257
+ return false if optional? && @value.nil?
251
258
 
252
259
  definite_default?
253
260
  end
@@ -267,7 +274,13 @@ module ParamsReady
267
274
  end
268
275
 
269
276
  def is_undefined?
270
- @value == Extensions::Undefined && !default_defined?
277
+ @value == Extensions::Undefined && allows_undefined?
278
+ end
279
+
280
+ def allows_undefined?
281
+ return true if optional?
282
+
283
+ !default_defined?
271
284
  end
272
285
 
273
286
  def eligible_for_output?(intent)
@@ -278,14 +291,6 @@ module ParamsReady
278
291
  format.hash_key(self)
279
292
  end
280
293
 
281
- def set_from_hash(hash, context: nil, validator: Result.new(name))
282
- if local?(context)
283
- populate(context, validator)
284
- else
285
- super
286
- end
287
- end
288
-
289
294
  def set_from_input(input, context, validator)
290
295
  preprocessed = definition.preprocess(input, context, validator)
291
296
  set_value preprocessed, context, validator
@@ -342,14 +347,18 @@ module ParamsReady
342
347
  format(Intent.instance(:backend))
343
348
  end
344
349
 
345
- def unwrap_or(default)
350
+ def unwrap_or(*args, &block)
351
+ ensure_default_present!(*args, &block)
352
+
346
353
  if is_definite?
347
- unwrap
354
+ begin
355
+ unwrap
356
+ rescue StandardError => _
357
+ supply_default(*args, &block)
358
+ end
348
359
  else
349
- default
360
+ supply_default(*args, &block)
350
361
  end
351
- rescue StandardError => _
352
- default
353
362
  end
354
363
 
355
364
  def find_in_hash(hash, context)
@@ -389,17 +398,17 @@ module ParamsReady
389
398
 
390
399
  def handle_indefinite_input(input, validator)
391
400
  value_missing validator
392
- if optional?
401
+ if default_defined?
393
402
  # if value_missing doesn't crash,
394
403
  # and the parameter is optional
395
404
  # it's safe to set to nil or Extensions::Undefined
396
- @value = input
397
- elsif default_defined?
405
+ @value = Extensions::Undefined
406
+ elsif optional?
398
407
  # Clear possible previous state,
399
408
  # will be set to default on read
400
- @value = Extensions::Undefined
409
+ @value = input
401
410
  else
402
- raise ParamsReadyError, "Unexpected state in #handle_indefinite_input" if validator.ok?
411
+ raise ParamsReadyError, "Unexpected state for '#{name}' in #handle_indefinite_input" if validator.ok?
403
412
  end
404
413
  end
405
414
 
@@ -427,22 +436,32 @@ module ParamsReady
427
436
  nil
428
437
  end
429
438
 
439
+ def ensure_default_present!(*args, &block)
440
+ raise ParamsReadyError, 'Single default value expected' if args.length > 1
441
+ raise ParamsReadyError, 'Supply either default or a block' if args.length == 0 && block.nil?
442
+ warn 'WARNING: block supersedes default value' if args.length > 0 && block
443
+ end
444
+
445
+ def supply_default(*args, &block)
446
+ if block.nil?
447
+ args[0]
448
+ else
449
+ block.call
450
+ end
451
+ end
452
+
430
453
  def init_for_read(to_be_frozen = false)
431
454
  return unless @value == Extensions::Undefined
432
455
  return unless default_defined?
433
456
 
434
- @value = if to_be_frozen
435
- definition.default
436
- else
437
- definition.duplicate_default
438
- end
457
+ @value = definition.fetch_default(duplicate: !to_be_frozen)
439
458
  end
440
459
 
441
460
  def init_for_write
442
461
  return if is_definite?
443
462
 
444
463
  if default_defined? && !default.nil?
445
- @value = definition.duplicate_default
464
+ @value = definition.fetch_default
446
465
  else
447
466
  init_value
448
467
  end
@@ -1,10 +1,10 @@
1
1
  require_relative '../error'
2
- require_relative '../../params_ready/parameter/hash_parameter'
2
+ require_relative '../../params_ready/parameter/struct_parameter'
3
3
  require_relative '../query/relation'
4
4
 
5
5
  module ParamsReady
6
6
  module Parameter
7
- class State < HashParameter
7
+ class State < StructParameter
8
8
  extend Query::Relation::PageAccessors
9
9
  extend Forwardable
10
10
  def_delegators :definition, :relations
@@ -98,7 +98,7 @@ module ParamsReady
98
98
  end
99
99
  end
100
100
 
101
- class StateBuilder < HashParameterBuilder
101
+ class StateBuilder < StructParameterBuilder
102
102
  def relation(relation)
103
103
  @definition.add_relation relation
104
104
  end
@@ -109,7 +109,7 @@ module ParamsReady
109
109
  end
110
110
  end
111
111
 
112
- class StateDefinition < HashParameterDefinition
112
+ class StateDefinition < StructParameterDefinition
113
113
  parameter_class State
114
114
  attr_reader :relations
115
115