params_ready 0.0.2 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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