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.
- checksums.yaml +7 -0
- data/lib/arel/cte_name.rb +20 -0
- data/lib/params_ready/builder.rb +161 -0
- data/lib/params_ready/error.rb +31 -0
- data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
- data/lib/params_ready/extensions/collection.rb +43 -0
- data/lib/params_ready/extensions/delegation.rb +25 -0
- data/lib/params_ready/extensions/finalizer.rb +26 -0
- data/lib/params_ready/extensions/freezer.rb +49 -0
- data/lib/params_ready/extensions/hash.rb +46 -0
- data/lib/params_ready/extensions/late_init.rb +38 -0
- data/lib/params_ready/extensions/registry.rb +44 -0
- data/lib/params_ready/extensions/undefined.rb +23 -0
- data/lib/params_ready/format.rb +132 -0
- data/lib/params_ready/helpers/arel_builder.rb +68 -0
- data/lib/params_ready/helpers/callable.rb +14 -0
- data/lib/params_ready/helpers/conditional_block.rb +31 -0
- data/lib/params_ready/helpers/find_in_hash.rb +22 -0
- data/lib/params_ready/helpers/interface_definer.rb +48 -0
- data/lib/params_ready/helpers/key_map.rb +176 -0
- data/lib/params_ready/helpers/memo.rb +41 -0
- data/lib/params_ready/helpers/options.rb +107 -0
- data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
- data/lib/params_ready/helpers/parameter_storage_class_methods.rb +63 -0
- data/lib/params_ready/helpers/parameter_user_class_methods.rb +35 -0
- data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
- data/lib/params_ready/helpers/rule.rb +76 -0
- data/lib/params_ready/helpers/storage.rb +30 -0
- data/lib/params_ready/helpers/usage_rule.rb +36 -0
- data/lib/params_ready/input_context.rb +31 -0
- data/lib/params_ready/intent.rb +70 -0
- data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
- data/lib/params_ready/marshaller/builder_module.rb +9 -0
- data/lib/params_ready/marshaller/collection.rb +165 -0
- data/lib/params_ready/marshaller/definition_module.rb +63 -0
- data/lib/params_ready/marshaller/enum_set_marshallers.rb +96 -0
- data/lib/params_ready/marshaller/parameter_module.rb +11 -0
- data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
- data/lib/params_ready/marshaller/struct_marshallers.rb +100 -0
- data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
- data/lib/params_ready/ordering/column.rb +60 -0
- data/lib/params_ready/ordering/ordering.rb +276 -0
- data/lib/params_ready/output_parameters.rb +138 -0
- data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
- data/lib/params_ready/pagination/cursor.rb +171 -0
- data/lib/params_ready/pagination/direction.rb +148 -0
- data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
- data/lib/params_ready/pagination/keysets.rb +70 -0
- data/lib/params_ready/pagination/nulls.rb +31 -0
- data/lib/params_ready/pagination/offset_pagination.rb +130 -0
- data/lib/params_ready/pagination/tendency.rb +28 -0
- data/lib/params_ready/parameter/abstract_struct_parameter.rb +204 -0
- data/lib/params_ready/parameter/array_parameter.rb +197 -0
- data/lib/params_ready/parameter/definition.rb +272 -0
- data/lib/params_ready/parameter/enum_set_parameter.rb +102 -0
- data/lib/params_ready/parameter/parameter.rb +475 -0
- data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
- data/lib/params_ready/parameter/state.rb +132 -0
- data/lib/params_ready/parameter/struct_parameter.rb +64 -0
- data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
- data/lib/params_ready/parameter/value_parameter.rb +186 -0
- data/lib/params_ready/parameter_definer.rb +14 -0
- data/lib/params_ready/parameter_user.rb +35 -0
- data/lib/params_ready/query/array_grouping.rb +68 -0
- data/lib/params_ready/query/custom_predicate.rb +102 -0
- data/lib/params_ready/query/exists_predicate.rb +103 -0
- data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
- data/lib/params_ready/query/grouping.rb +177 -0
- data/lib/params_ready/query/join_clause.rb +87 -0
- data/lib/params_ready/query/nullness_predicate.rb +71 -0
- data/lib/params_ready/query/polymorph_predicate.rb +77 -0
- data/lib/params_ready/query/predicate.rb +203 -0
- data/lib/params_ready/query/predicate_operator.rb +132 -0
- data/lib/params_ready/query/relation.rb +337 -0
- data/lib/params_ready/query/structured_grouping.rb +58 -0
- data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
- data/lib/params_ready/query_context.rb +21 -0
- data/lib/params_ready/restriction.rb +252 -0
- data/lib/params_ready/result.rb +109 -0
- data/lib/params_ready/value/coder.rb +210 -0
- data/lib/params_ready/value/constraint.rb +198 -0
- data/lib/params_ready/value/custom.rb +56 -0
- data/lib/params_ready/value/validator.rb +81 -0
- data/lib/params_ready/version.rb +7 -0
- data/lib/params_ready.rb +28 -0
- 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
|