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