params_ready_rails5 0.0.7

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 (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,172 @@
1
+ require_relative 'parameter'
2
+ require_relative 'polymorph_parameter'
3
+ require_relative 'value_parameter'
4
+ require_relative '../marshaller/parameter_module'
5
+ require_relative '../marshaller/definition_module'
6
+ require_relative '../marshaller/builder_module'
7
+ require_relative '../marshaller/polymorph_marshallers'
8
+
9
+ module ParamsReady
10
+ module Parameter
11
+ class PolymorphParameter < Parameter
12
+ include ComplexParameter
13
+ include Marshaller::ParameterModule
14
+
15
+ def_delegators :@definition, :identifier, :types
16
+ intent_for_children :restriction
17
+
18
+ def permission_depends_on
19
+ type = to_type
20
+ return [] if type.nil?
21
+ [type]
22
+ end
23
+
24
+ def set_value_as(value, type, context = Format.instance(:backend), validator = nil)
25
+ parameter = types[type].create
26
+ parameter.set_value value, context, validator
27
+ @value = parameter
28
+ end
29
+
30
+ def type
31
+ return nil unless is_definite?
32
+ bare_value.name
33
+ end
34
+
35
+ def to_type
36
+ bare_value
37
+ end
38
+
39
+ def [](key)
40
+ raise ParamsReadyError, "Type '#{key}' is not set, current type: nil" if is_nil?
41
+
42
+ param = bare_value
43
+ if param.name != key
44
+ raise ParamsReadyError, "Type '#{key}' is not set, current type: '#{param.name}'"
45
+ else
46
+ param
47
+ end
48
+ end
49
+
50
+ protected
51
+
52
+ def child_for_update(path)
53
+ type, *path = path
54
+ [self[type], type, path]
55
+ end
56
+
57
+ def updated_clone(_child_name, updated)
58
+ clone = definition.create
59
+ clone.populate_with(updated, frozen?)
60
+ clone
61
+ end
62
+
63
+ def populate_with(value, freeze = false)
64
+ @value = if freeze && value.frozen?
65
+ value
66
+ else
67
+ value.dup
68
+ end
69
+
70
+ self.freeze if freeze
71
+ self
72
+ end
73
+ end
74
+
75
+ class PolymorphParameterBuilder < Builder
76
+ include Marshaller::BuilderModule
77
+
78
+ register :polymorph
79
+
80
+ def self.instance(name, altn: nil)
81
+ new PolymorphParameterDefinition.new(name, altn: altn)
82
+ end
83
+
84
+ def type(input, *args, **opts, &block)
85
+ definition = self.class.resolve(input, *args, **opts, &block)
86
+ @definition.add_type definition
87
+ end
88
+
89
+ def identifier(identifier)
90
+ @definition.set_identifier identifier
91
+ end
92
+ end
93
+
94
+ class PolymorphParameterDefinition < Definition
95
+ include ValueParameterDefinition::ValueLike
96
+ include Marshaller::DefinitionModule[Marshaller::PolymorphMarshallers.collection]
97
+
98
+ obligatory! :types
99
+ attr_reader :types
100
+ # late_init :identifier, obligatory: true
101
+
102
+ parameter_class PolymorphParameter
103
+ name_for_formatter :polymorph
104
+
105
+ def initialize(*args, identifier: nil, types: [], default_name: nil, **options)
106
+ @types = {}
107
+ @alt_names = {}
108
+ add_types types
109
+ set_default(default_name)
110
+ super *args, **options
111
+ set_identifier identifier unless identifier.nil?
112
+ end
113
+
114
+ def add_type(definition)
115
+ check_type_names(definition)
116
+ raise ParamsReadyError, "Reused name: #{definition.name}" if @types.key? definition.name
117
+ raise ParamsReadyError, "Reused alternative: #{definition.altn}" if @alt_names.key? definition.altn
118
+ @types[definition.name] = definition
119
+ @alt_names[definition.altn] = definition
120
+ end
121
+
122
+ def check_type_names(definition)
123
+ return if @marshallers.nil?
124
+ return unless @marshallers.default?
125
+ default = @marshallers.default
126
+ return unless default.respond_to? :reserved?
127
+
128
+ raise ParamsReadyError, "Reserved name: #{definition.name}" if default.reserved?(definition.name)
129
+ raise ParamsReadyError, "Reserved alternative: #{definition.altn}" if default.reserved?(definition.altn)
130
+ end
131
+
132
+ def add_types(types)
133
+ return if types.nil?
134
+ types.each do |definition|
135
+ add_type definition
136
+ end
137
+ end
138
+
139
+ def set_identifier(type_identifier)
140
+ raise ParamsReadyError, "Identifier already taken: #{type_identifier}" if @alt_names&.key?(type_identifier)
141
+ raise ParamsReadyError, "Identifier already taken: #{type_identifier}" if @types&.key?(type_identifier)
142
+ set_marshaller using: :hash, type_identifier: type_identifier
143
+ end
144
+
145
+ def type(name, context)
146
+ name = if context.alternative?
147
+ @alt_names[name.to_sym].name
148
+ else
149
+ name
150
+ end
151
+ @types[name.to_sym]
152
+ end
153
+
154
+ def set_default(default_name)
155
+ return if default_name.nil?
156
+
157
+ type_def = types[default_name]
158
+ raise ParamsReadyError, "Unknown type '#{default_name}'" if type_def.nil?
159
+ raise ParamsReadyError, "Default type must have default" unless type_def.default_defined?
160
+ frozen = freeze_value(type_def.create)
161
+ @default = frozen
162
+ end
163
+
164
+ def finish
165
+ set_identifier :ppt unless marshallers.default?
166
+ super
167
+ end
168
+
169
+ freeze_variables :types
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,132 @@
1
+ require_relative '../error'
2
+ require_relative '../../params_ready/parameter/struct_parameter'
3
+ require_relative '../query/relation'
4
+
5
+ module ParamsReady
6
+ module Parameter
7
+ class State < StructParameter
8
+ extend Query::Relation::PageAccessors
9
+ extend Forwardable
10
+ def_delegators :definition, :relations
11
+
12
+ def relation(name)
13
+ raise ParamsReadyError, "Relation not defined: '#{name}'" unless relations.include? name
14
+ child(name)
15
+ end
16
+
17
+ def self.relation_delegator(name)
18
+ define_method name do |relation_name, *args|
19
+ relation(relation_name).send(name, *args)
20
+ end
21
+ ruby2_keywords name
22
+ end
23
+
24
+ relation_delegator :ordering
25
+ relation_delegator :num_pages
26
+ relation_delegator :page_no
27
+ relation_delegator :offset
28
+ relation_delegator :limit
29
+ relation_delegator :has_previous?
30
+ relation_delegator :has_previous?
31
+ relation_delegator :has_next?
32
+ relation_delegator :has_page?
33
+
34
+ def page(relation_name = nil, delta = 0, count: nil)
35
+ if delta == 0
36
+ clone
37
+ else
38
+ raise ParamsReadyError, "Relation must be specified when delta is not 0" if relation_name.nil?
39
+
40
+ return nil unless relation(relation_name).pagination.can_yield_page? delta, count: count
41
+ new_offset = relation(relation_name).new_offset(delta)
42
+ update_in(new_offset, [relation_name, :pagination, 0])
43
+ end
44
+ end
45
+
46
+ def current_page
47
+ page
48
+ end
49
+
50
+ def first_page(relation_name)
51
+ value = relation(relation_name).pagination.first_page_value
52
+ update_in(value, [relation_name, :pagination])
53
+ end
54
+
55
+ def last_page(relation_name, *args, **opts)
56
+ value = relation(relation_name).pagination.last_page_value(*args, **opts)
57
+ return if value.nil?
58
+ update_in(value, [relation_name, :pagination])
59
+ end
60
+
61
+ def previous_page(relation_name, delta = 1)
62
+ value = relation(relation_name).pagination.previous_page_value(delta)
63
+ return if value.nil?
64
+ update_in(value, [relation_name, :pagination])
65
+ end
66
+
67
+ def next_page(relation_name, delta = 1, count: nil)
68
+ value = relation(relation_name).pagination.next_page_value(delta, count: count)
69
+ return if value.nil?
70
+ update_in(value, [relation_name, :pagination])
71
+ end
72
+
73
+ def before_page(relation_name, keyset)
74
+ value = relation(relation_name).pagination.before_page_value(keyset)
75
+ update_in(value, [relation_name, :pagination])
76
+ end
77
+
78
+ def after_page(relation_name, keyset)
79
+ value = relation(relation_name).pagination.after_page_value(keyset)
80
+ update_in(value, [relation_name, :pagination])
81
+ end
82
+
83
+ def limited_at(relation_name, limit)
84
+ limit_key = relation(relation_name).pagination.limit_key
85
+ update_in(limit, [relation_name, :pagination, limit_key])
86
+ end
87
+
88
+ def toggled_order(relation_name, column)
89
+ new_order = relation(relation_name).ordering.toggled_order_value(column)
90
+ toggled = update_in(new_order, [relation_name, :ordering])
91
+ toggled.first_page(relation_name)
92
+ end
93
+
94
+ def reordered(relation_name, column, direction)
95
+ new_order = relation(relation_name).ordering.reordered_value(column, direction)
96
+ reordered = update_in(new_order, [relation_name, :ordering])
97
+ reordered.first_page(relation_name)
98
+ end
99
+ end
100
+
101
+ class StateBuilder < StructParameterBuilder
102
+ def relation(relation)
103
+ @definition.add_relation relation
104
+ end
105
+
106
+ def self.instance
107
+ definition = StateDefinition.new(:'', altn: :'')
108
+ new definition
109
+ end
110
+ end
111
+
112
+ class StateDefinition < StructParameterDefinition
113
+ parameter_class State
114
+ attr_reader :relations
115
+
116
+ def initialize(*args, **opts)
117
+ super *args, **opts
118
+ @relations = Set.new
119
+ end
120
+
121
+ def add_relation(relation)
122
+ if @relations.include? relation.name
123
+ raise ParamsReadyError, "Relation already there '#{relation.name}'"
124
+ end
125
+ @relations << relation.name
126
+ add_child relation
127
+ end
128
+
129
+ freeze_variable :relations
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,64 @@
1
+ require_relative 'parameter'
2
+ require_relative '../builder'
3
+ require_relative 'abstract_struct_parameter'
4
+ require_relative '../marshaller/parameter_module'
5
+
6
+
7
+ module ParamsReady
8
+ module Parameter
9
+ class StructParameter < AbstractStructParameter
10
+ include Marshaller::ParameterModule
11
+ end
12
+
13
+ class StructParameterBuilder < Builder
14
+ include AbstractStructParameterBuilder::StructLike
15
+ include Marshaller::BuilderModule
16
+ register :struct
17
+ register_deprecated :hash, use: :struct
18
+
19
+ def self.instance(name, altn: nil)
20
+ new StructParameterDefinition.new(name, altn: altn)
21
+ end
22
+
23
+ def map(hash)
24
+ @definition.add_map(hash, **{})
25
+ end
26
+ end
27
+
28
+ class StructParameterDefinition < AbstractStructParameterDefinition
29
+ include Marshaller::DefinitionModule[Marshaller::StructMarshallers.collection]
30
+
31
+ name_for_formatter :struct
32
+ parameter_class StructParameter
33
+ freeze_variables :key_map
34
+
35
+ def ensure_canonical(hash)
36
+ context = Format.instance(:backend)
37
+
38
+ value, _validator = try_canonicalize hash, context, nil, freeze: true
39
+ return value if value.length == hash.length
40
+
41
+ extra_keys = hash.keys.select do |key|
42
+ !value.key?(key)
43
+ end.map do |key|
44
+ "'#{key.to_s}'"
45
+ end.join(", ")
46
+ raise ParamsReadyError, "extra keys found -- #{extra_keys}" if extra_keys.length > 0
47
+ value
48
+ end
49
+
50
+
51
+ def add_map(hash)
52
+ @key_map ||= Helpers::KeyMap.new
53
+ hash.each do |key, value|
54
+ @key_map.map(key, to: value)
55
+ end
56
+ end
57
+
58
+ def remap?(context)
59
+ return false if key_map.nil?
60
+ context.remap?
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,152 @@
1
+ require 'set'
2
+ require_relative 'parameter'
3
+ require_relative '../builder'
4
+ require_relative 'definition'
5
+ require_relative 'array_parameter'
6
+ require_relative '../marshaller/tuple_marshallers'
7
+ require_relative '../marshaller/definition_module'
8
+ require_relative '../marshaller/builder_module'
9
+ require_relative '../marshaller/parameter_module'
10
+
11
+ module ParamsReady
12
+ module Parameter
13
+ class TupleParameter < Parameter
14
+ include ArrayParameter::ArrayLike
15
+ include Marshaller::ParameterModule
16
+
17
+ def_delegators :@definition, :names, :fields, :separator, :marshaller
18
+
19
+ freeze_variable :value do |array|
20
+ next if Extensions::Undefined.value_indefinite?(array)
21
+ array.each(&:freeze)
22
+ end
23
+
24
+ def method_missing(name, *args)
25
+ integer = ordinal_to_integer(name)
26
+ if integer.nil?
27
+ super
28
+ else
29
+ self[integer - 1]
30
+ end
31
+ end
32
+
33
+ def respond_to_missing?(name, include_private = false)
34
+ return true unless ordinal_to_integer(name).nil?
35
+ super
36
+ end
37
+
38
+ protected
39
+
40
+ def element(index)
41
+ return nil if is_nil?
42
+
43
+ value = bare_value
44
+ if index < 0 || index >= value.length
45
+ raise ParamsReadyError, "Index out of bounds: #{index}"
46
+ else
47
+ value[index]
48
+ end
49
+ end
50
+
51
+ ORDINALS = %i{
52
+ first second third fourth fifth sixth seventh eighth nineth tenth
53
+ }.each_with_index.map do |ordinal, index|
54
+ [ordinal, index + 1]
55
+ end.to_h.freeze
56
+
57
+ def ordinal_to_integer(name)
58
+ integer = ORDINALS[name]
59
+ return nil if integer.nil?
60
+ return nil if definition.arity < integer
61
+
62
+ integer
63
+ end
64
+
65
+ def init_value
66
+ @value = []
67
+ fields.each do |definition|
68
+ @value << definition.create
69
+ end
70
+ end
71
+ end
72
+
73
+ class TupleParameterBuilder < Builder
74
+ include Marshaller::BuilderModule
75
+ register :tuple
76
+
77
+ def self.instance(name, altn: nil)
78
+ new TupleParameterDefinition.new(name, altn: altn)
79
+ end
80
+
81
+ def field(input, *args, **opts, &block)
82
+ definition = self.class.resolve(input, *args, **opts, &block)
83
+ @definition.add_field definition
84
+ end
85
+ end
86
+
87
+ class TupleParameterDefinition < Definition
88
+ include ArrayParameterDefinition::ArrayLike
89
+ include Marshaller::DefinitionModule[Marshaller::TupleMarshallers.collection]
90
+
91
+ class StringMarshaller
92
+ def initialize(separator:)
93
+ @separator = separator
94
+ end
95
+
96
+ def marshal(fields, _format)
97
+ fields.join(@separator)
98
+ end
99
+ end
100
+
101
+ class StructMarshaller
102
+ def marshal(fields, _format)
103
+ fields.each_with_index.map do |field, index|
104
+ [index.to_s, field]
105
+ end.to_h
106
+ end
107
+ end
108
+
109
+ name_for_formatter :tuple
110
+
111
+ parameter_class TupleParameter
112
+
113
+ def initialize(*args, separator: nil, fields: nil, **options)
114
+ @fields = []
115
+ add_fields fields unless fields.nil?
116
+ super *args, **options
117
+ end
118
+
119
+ def add_fields(fields)
120
+ fields.each do |field|
121
+ add_field(field)
122
+ end
123
+ end
124
+
125
+ collection :fields, :field do |field|
126
+ raise ParamsReadyError, "Can't add field if default is present" if default_defined?
127
+ raise ParamsReadyError, "Not a field definition #{field.class.name}" unless field.is_a? AbstractDefinition
128
+ raise ParamsReadyError, "Field is not a value #{field.class.name}" unless field.is_a? ValueParameterDefinition
129
+ raise ParamsReadyError, "Field can't be optional" if field.optional?
130
+ raise ParamsReadyError, "Field can't have default" if field.default_defined?
131
+ field
132
+ end
133
+
134
+ def arity
135
+ @fields.length
136
+ end
137
+
138
+ def ensure_canonical(array)
139
+ raise ParamsReadyError, "Not an array" unless array.is_a? Array
140
+ context = Format.instance(:backend)
141
+ marshaller = marshallers.instance(Array)
142
+ value, _validator = marshaller.canonicalize(self, array, context, nil, freeze: true)
143
+ value
144
+ end
145
+
146
+ def freeze
147
+ @fields.freeze
148
+ super
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,186 @@
1
+ require_relative 'parameter'
2
+ require_relative '../value/validator'
3
+ require_relative '../value/coder'
4
+ require_relative 'definition'
5
+ require_relative '../builder'
6
+
7
+ module ParamsReady
8
+ module Parameter
9
+ class ValueParameter < Parameter
10
+ def_delegators :@definition, :coder
11
+
12
+ def marshal(intent)
13
+ return nil if is_nil?
14
+
15
+ value = bare_value
16
+ return value unless intent.marshal?(name_for_formatter)
17
+
18
+ coder.format(value, intent)
19
+ end
20
+
21
+ protected
22
+
23
+ def update_self(value)
24
+ clone = dup
25
+ clone.set_value value
26
+
27
+ if frozen?
28
+ if clone == self
29
+ return false, self
30
+ else
31
+ [true, clone.freeze]
32
+ end
33
+ else
34
+ [true, clone]
35
+ end
36
+ end
37
+
38
+ def populate_with(value)
39
+ @value = value.dup
40
+ end
41
+ end
42
+
43
+ class ValueParameterBuilder < Builder
44
+ module ValueLike
45
+ def constrain(name_or_constraint, *args, strategy: :raise, **opts, &block)
46
+ validator = Value::Validator.instance(name_or_constraint, *args, strategy: strategy, **opts, &block)
47
+ @definition.add_constraint validator
48
+ end
49
+
50
+ def coerce(&block)
51
+ @definition.set_coerce(block)
52
+ end
53
+
54
+ def format(&block)
55
+ @definition.set_format(block)
56
+ end
57
+
58
+ def type_identifier(name)
59
+ @definition.set_type_identifier(name)
60
+ end
61
+ end
62
+
63
+ include ValueLike
64
+ extend Extensions::Registry
65
+
66
+ register :value
67
+
68
+ registry :coders, as: :coder, getter: true do |name, _|
69
+ builder_class = ValueParameterBuilder[name]
70
+ builder_class.register name
71
+ end
72
+
73
+ def self.instance(name, coder_or_name = nil, altn: nil, **opts)
74
+ coder = if coder_or_name.is_a? Symbol
75
+ coder_class = self.coder(coder_or_name)
76
+ coder_class.instance(**opts)
77
+ else
78
+ raise ParamsReadyError, 'Expected option hash to be empty' unless opts.empty?
79
+ if coder_or_name.nil?
80
+ Value::GenericCoder.new(name)
81
+ else
82
+ coder_or_name
83
+ end
84
+ end
85
+ new ValueParameterDefinition.new(name, coder, altn: altn)
86
+ end
87
+
88
+ def self.[](coder_name)
89
+ builder = Class.new(self)
90
+ capitalized = coder_name.to_s.split('_').map(&:capitalize).join
91
+ qualified = "#{self.name}::#{capitalized}Builder".freeze
92
+
93
+ builder.define_singleton_method :name do
94
+ qualified
95
+ end
96
+
97
+ builder.define_singleton_method :instance do |name, altn: nil, **opts|
98
+ superclass.instance(name, coder_name, altn: altn, **opts)
99
+ end
100
+
101
+ builder
102
+ end
103
+
104
+ register_coder :integer, Value::IntegerCoder
105
+ register_coder :decimal, Value::DecimalCoder
106
+ register_coder :string, Value::StringCoder
107
+ register_coder :symbol, Value::SymbolCoder
108
+ register_coder :boolean, Value::BooleanCoder
109
+ register_coder :date, Value::DateCoder
110
+ register_coder :datetime, Value::DateTimeCoder
111
+ end
112
+
113
+ class ValueParameterDefinition < Definition
114
+ extend Forwardable
115
+ def_delegators :@coder, :set_coerce, :set_format, :set_type_identifier
116
+
117
+ name_for_formatter :value
118
+
119
+ def name_for_formatter
120
+ coder_name = @coder.type_identifier if @coder.respond_to? :type_identifier
121
+ return coder_name unless coder_name.nil?
122
+
123
+ super
124
+ end
125
+
126
+ parameter_class ValueParameter
127
+
128
+ module ValueLike
129
+ def duplicate_value(value)
130
+ value.dup
131
+ end
132
+
133
+ def freeze_value(value)
134
+ value.freeze
135
+ end
136
+ end
137
+
138
+ include ValueLike
139
+
140
+ attr_reader :coder
141
+
142
+ collection :constraints, :constraint do |constraint|
143
+ raise ParamsReadyError, "Can't constrain after default has been set" if default_defined?
144
+ constraint
145
+ end
146
+
147
+ def initialize(name, coder, *args, constraints: [], **options)
148
+ @coder = coder
149
+ @constraints = constraints
150
+ super name, *args, **options
151
+ end
152
+
153
+ def try_canonicalize(input, context, validator = nil)
154
+ value = coder.try_coerce input, context
155
+ return value if Extensions::Undefined.value_indefinite?(value)
156
+
157
+ value, validator = validate value, validator
158
+ if validator.nil? || validator.ok?
159
+ [value.freeze, validator]
160
+ else
161
+ [nil, validator]
162
+ end
163
+ end
164
+
165
+ def ensure_canonical(value)
166
+ coerced = coder.try_coerce value, Format.instance(:backend)
167
+ if coder.strict_default? && value != coerced
168
+ raise ParamsReadyError, "input '#{value}'/#{value.class.name} (expected '#{coerced}'/#{coerced.class.name})"
169
+ end
170
+ validate coerced
171
+ coerced
172
+ end
173
+
174
+ def validate(value, validator = nil)
175
+ constraints.reduce([value, validator]) do |(value, validator), constraint|
176
+ constraint.validate value, validator
177
+ end
178
+ end
179
+
180
+ def finish
181
+ @coder.finish if @coder.respond_to?(:finish)
182
+ super
183
+ end
184
+ end
185
+ end
186
+ end