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,76 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ParamsReady
|
4
|
+
module Helpers
|
5
|
+
def self.Rule(input)
|
6
|
+
return input if input.nil?
|
7
|
+
return input if input.is_a? Rule
|
8
|
+
|
9
|
+
Rule.instance(input).freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
class Rule
|
13
|
+
attr_reader :hash, :mode, :values
|
14
|
+
|
15
|
+
def self.instance(input)
|
16
|
+
mode, values = case input
|
17
|
+
when :none, :all then [input, nil]
|
18
|
+
when Hash
|
19
|
+
if input.length > 1 || input.length < 1
|
20
|
+
raise ParamsReadyError, "Unexpected hash for rule: '#{input}'"
|
21
|
+
end
|
22
|
+
key, values = input.first
|
23
|
+
case key
|
24
|
+
when :except, :only then [key, values.to_set.freeze]
|
25
|
+
else
|
26
|
+
raise ParamsReadyError, "Unexpected mode for rule: '#{key}'"
|
27
|
+
end
|
28
|
+
else
|
29
|
+
raise ParamsReadyError, "Unexpected input for rule: '#{input}'"
|
30
|
+
end
|
31
|
+
new(mode, values)
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(mode, values)
|
35
|
+
@mode = mode
|
36
|
+
@values = values.freeze
|
37
|
+
@hash = [@mode, @values].hash
|
38
|
+
freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def merge(other)
|
42
|
+
return self if other.nil?
|
43
|
+
raise ParamsReadyError, "Can't merge with #{other.class.name}" unless other.is_a? Rule
|
44
|
+
raise ParamsReadyError, "Can't merge incompatible rules: #{mode}/#{other.mode}" if other.mode != mode
|
45
|
+
|
46
|
+
case mode
|
47
|
+
when :all, :none
|
48
|
+
self
|
49
|
+
when :only, :except
|
50
|
+
values = self.values + other.values
|
51
|
+
Rule.new(mode, values)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def include?(name)
|
56
|
+
case @mode
|
57
|
+
when :none then false
|
58
|
+
when :all then true
|
59
|
+
when :only then @values.member? name
|
60
|
+
when :except
|
61
|
+
!@values.member? name
|
62
|
+
else
|
63
|
+
raise ParamsReadyError, "Unexpected mode for rule: '#{@mode}'"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ==(other)
|
68
|
+
return false unless other.is_a? Rule
|
69
|
+
return true if object_id == other.object_id
|
70
|
+
return false unless @mode == other.instance_variable_get(:@mode)
|
71
|
+
|
72
|
+
@values == other.instance_variable_get(:@values)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ParamsReady
|
2
|
+
module Helpers
|
3
|
+
class Storage
|
4
|
+
attr_reader :parameters, :relations
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@parameters = Hash.new
|
8
|
+
@relations = Hash.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_relation?(name)
|
12
|
+
relations.key? name
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_parameter?(name)
|
16
|
+
parameters.key? name
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_relation(relation)
|
20
|
+
raise ParamsReadyError, "Relation already exists: #{relation.name}" if self.has_relation?(relation.name)
|
21
|
+
@relations[relation.name] = relation
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_parameter(param)
|
25
|
+
raise ParamsReadyError, "Parameter already exists: #{param.name}" if self.has_parameter?(param.name)
|
26
|
+
@parameters[param.name] = param
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'rule'
|
2
|
+
|
3
|
+
module ParamsReady
|
4
|
+
module Helpers
|
5
|
+
class UsageRule
|
6
|
+
attr_reader :parameter_definition, :rule
|
7
|
+
|
8
|
+
def initialize(parameter_definition, rule = :all)
|
9
|
+
@parameter_definition = parameter_definition
|
10
|
+
@rule = ParamsReady::Helpers::Rule(rule)
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_for?(method)
|
15
|
+
@rule.include? method
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
parameter_definition.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def merge(other)
|
23
|
+
return self if other.nil?
|
24
|
+
raise ParamsReadyError, "Can't merge into #{other.class.name}" unless other.is_a? UsageRule
|
25
|
+
|
26
|
+
unless parameter_definition == other.parameter_definition
|
27
|
+
message = "Can't merge incompatible rules: #{parameter_definition.name}/#{other.parameter_definition.name}"
|
28
|
+
raise ParamsReadyError, message
|
29
|
+
end
|
30
|
+
|
31
|
+
rule = self.rule.merge(other.rule)
|
32
|
+
UsageRule.new(parameter_definition, rule)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require_relative 'format'
|
3
|
+
|
4
|
+
module ParamsReady
|
5
|
+
class InputContext
|
6
|
+
include Format::Wrapper
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :data
|
10
|
+
|
11
|
+
def_delegator :data, :[]
|
12
|
+
|
13
|
+
def initialize(format, data = {})
|
14
|
+
@format = Format.resolve(format).freeze
|
15
|
+
@data = data.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.resolve(unknown)
|
19
|
+
case unknown
|
20
|
+
when nil
|
21
|
+
Format.instance(:frontend)
|
22
|
+
when InputContext, Format
|
23
|
+
unknown
|
24
|
+
when Symbol
|
25
|
+
Format.instance(unknown)
|
26
|
+
else
|
27
|
+
raise ParamsReadyError, "Unexpected type for InputContext: #{unknown.class.name}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require_relative 'restriction'
|
3
|
+
require_relative 'format'
|
4
|
+
require_relative 'parameter/parameter'
|
5
|
+
|
6
|
+
module ParamsReady
|
7
|
+
class Intent
|
8
|
+
extend Forwardable
|
9
|
+
include Restriction::Wrapper
|
10
|
+
include Format::Wrapper
|
11
|
+
|
12
|
+
def clone(restriction:)
|
13
|
+
Intent.new @format, restriction, data: @data
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :data, :hash
|
17
|
+
|
18
|
+
def initialize(format, restriction = Restriction.blanket_permission, data: nil)
|
19
|
+
@format = Format.resolve(format).freeze
|
20
|
+
raise ParamsReadyError, "Restriction expected, got: #{restriction.inspect}" unless restriction.is_a? Restriction
|
21
|
+
@restriction = restriction
|
22
|
+
@data = check_data(data)
|
23
|
+
@hash = [@format, @restriction, @data].hash
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_data(data)
|
28
|
+
return if data.nil?
|
29
|
+
# The reason we require data object to be
|
30
|
+
# a Parameter is that it must be deep frozen
|
31
|
+
# for the output memoizing feature to work properly.
|
32
|
+
raise 'Data object must be a parameter' unless data.is_a? Parameter::Parameter
|
33
|
+
raise 'Data object must be frozen' unless data.frozen?
|
34
|
+
|
35
|
+
data
|
36
|
+
end
|
37
|
+
|
38
|
+
def omit?(parameter)
|
39
|
+
return true unless permitted?(parameter)
|
40
|
+
@format.omit?(parameter)
|
41
|
+
end
|
42
|
+
|
43
|
+
def preserve?(parameter)
|
44
|
+
!omit?(parameter)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.instance(name)
|
48
|
+
format = Format.instance(name)
|
49
|
+
Intent.new(format)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.resolve(intent_or_name)
|
53
|
+
if intent_or_name.is_a? Intent
|
54
|
+
intent_or_name
|
55
|
+
else
|
56
|
+
instance(intent_or_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def ==(other)
|
61
|
+
return false unless other.is_a?(Intent)
|
62
|
+
return true if object_id == other.object_id
|
63
|
+
restriction == other.restriction && format == other.format && data == other.data
|
64
|
+
end
|
65
|
+
|
66
|
+
def eql?(other)
|
67
|
+
self == other
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'collection'
|
2
|
+
require_relative '../extensions/undefined'
|
3
|
+
require_relative '../extensions/hash'
|
4
|
+
require_relative '../helpers/find_in_hash'
|
5
|
+
|
6
|
+
module ParamsReady
|
7
|
+
module Marshaller
|
8
|
+
class ArrayMarshallers
|
9
|
+
module AbstractMarshaller
|
10
|
+
def marshal(parameter, intent)
|
11
|
+
array = parameter.send(:bare_value)
|
12
|
+
definition = parameter.definition
|
13
|
+
compact = definition.compact?
|
14
|
+
|
15
|
+
elements = array.map do |element|
|
16
|
+
if element.eligible_for_output?(intent)
|
17
|
+
element.format_self_permitted(intent)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
elements = elements.compact if compact
|
21
|
+
do_marshal(elements, intent, compact)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ArrayMarshaller
|
26
|
+
extend AbstractMarshaller
|
27
|
+
|
28
|
+
def self.canonicalize(definition, array, context, validator, freeze: false)
|
29
|
+
canonical = array.map do |value|
|
30
|
+
next if definition.compact? && value.nil?
|
31
|
+
|
32
|
+
element = definition.prototype.create
|
33
|
+
element.set_from_input(value, context, validator)
|
34
|
+
next if definition.compact? && element.is_nil?
|
35
|
+
|
36
|
+
element.freeze if freeze
|
37
|
+
element
|
38
|
+
end.compact
|
39
|
+
|
40
|
+
[canonical, validator]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.do_marshal(array, _, _)
|
44
|
+
array
|
45
|
+
end
|
46
|
+
|
47
|
+
freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
module StructMarshaller
|
51
|
+
extend AbstractMarshaller
|
52
|
+
|
53
|
+
def self.canonicalize(definition, hash, context, validator)
|
54
|
+
if definition.compact?
|
55
|
+
ArrayMarshaller.canonicalize(definition, hash.values, context, validator)
|
56
|
+
else
|
57
|
+
count_key = :cnt
|
58
|
+
found, count = Helpers::FindInHash.find_in_hash hash, count_key
|
59
|
+
raise ParamsReadyError, "Count not found" unless found
|
60
|
+
|
61
|
+
count = Integer(count)
|
62
|
+
array = (0...count).map do |index|
|
63
|
+
found, value = Helpers::FindInHash.find_in_hash hash, index
|
64
|
+
element = definition.prototype.create
|
65
|
+
element.set_from_input(value, context, validator) if found
|
66
|
+
element
|
67
|
+
end
|
68
|
+
[array, validator]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.do_marshal(array, _, compact)
|
73
|
+
return array if compact
|
74
|
+
|
75
|
+
result = array.each_with_index.reduce({}) do |result, (element, index)|
|
76
|
+
index = index.to_s
|
77
|
+
result[index] = element
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
result['cnt'] = array.length.to_s
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
freeze
|
86
|
+
end
|
87
|
+
|
88
|
+
class StringMarshaller
|
89
|
+
include AbstractMarshaller
|
90
|
+
|
91
|
+
attr_reader :separator
|
92
|
+
|
93
|
+
def self.instance(separator:, split_pattern: nil)
|
94
|
+
instance = new separator, split_pattern
|
95
|
+
[String, instance.freeze]
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialize(separator, split_pattern)
|
99
|
+
@separator = separator.to_s.freeze
|
100
|
+
@split_pattern = split_pattern.freeze
|
101
|
+
end
|
102
|
+
|
103
|
+
def split_pattern
|
104
|
+
@split_pattern || @separator
|
105
|
+
end
|
106
|
+
|
107
|
+
def canonicalize(definition, string, context, validator)
|
108
|
+
array = string.split(split_pattern).map(&:strip).reject(&:empty?)
|
109
|
+
ArrayMarshaller.canonicalize(definition, array, context, validator)
|
110
|
+
end
|
111
|
+
|
112
|
+
def do_marshal(array, _, _)
|
113
|
+
array.join(separator)
|
114
|
+
end
|
115
|
+
|
116
|
+
freeze
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.collection
|
120
|
+
@collection ||= begin
|
121
|
+
c = ClassCollection.new Array
|
122
|
+
c.add_instance Array, ArrayMarshaller
|
123
|
+
c.add_instance Hash, StructMarshaller
|
124
|
+
c.add_factory :string, StringMarshaller
|
125
|
+
c.default!(Hash)
|
126
|
+
c.freeze
|
127
|
+
c
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require_relative '../error'
|
2
|
+
require_relative '../extensions/hash'
|
3
|
+
|
4
|
+
module ParamsReady
|
5
|
+
module Marshaller
|
6
|
+
class InstanceCollection
|
7
|
+
attr_reader :default, :instances
|
8
|
+
|
9
|
+
def initialize(canonical, default = nil, instances = {})
|
10
|
+
@canonical = canonical
|
11
|
+
@default = default
|
12
|
+
@instances = instances
|
13
|
+
end
|
14
|
+
|
15
|
+
def canonicalize(definition, input, context, validator, **opts)
|
16
|
+
value_class = infer_class(input)
|
17
|
+
marshaller = instance(value_class)
|
18
|
+
raise ParamsReadyError, "Unexpected type for #{definition.name}: #{value_class.name}" if marshaller.nil?
|
19
|
+
|
20
|
+
marshaller.canonicalize(definition, input, context, validator, **opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def marshal_canonical(parameter, format, **opts)
|
24
|
+
marshaller = instance @canonical
|
25
|
+
if marshaller.nil?
|
26
|
+
value = parameter.send(:bare_value)
|
27
|
+
raise ParamsReadyError, "Value is not canonical" unless value.is_a? @canonical
|
28
|
+
value
|
29
|
+
else
|
30
|
+
marshaller.marshal(parameter, format, **opts)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def marshal(parameter, format, **opts)
|
35
|
+
default.marshal(parameter, format, **opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def infer_class(value)
|
39
|
+
if instances.key? value.class
|
40
|
+
value.class
|
41
|
+
elsif value.is_a?(Hash) || Extensions::Hash.acts_as_hash?(value)
|
42
|
+
Hash
|
43
|
+
else
|
44
|
+
value.class
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_instance(value_class, instance)
|
49
|
+
raise ParamsReadyError, "Marshaller must be frozen" unless instance.frozen?
|
50
|
+
|
51
|
+
@instances[value_class] = instance
|
52
|
+
end
|
53
|
+
|
54
|
+
def instance(value_class)
|
55
|
+
@instances[value_class]
|
56
|
+
end
|
57
|
+
|
58
|
+
def instance?(value_class)
|
59
|
+
@instances.key?(value_class)
|
60
|
+
end
|
61
|
+
|
62
|
+
def default=(instance)
|
63
|
+
raise ParamsReadyError, "Default already defined" if default?
|
64
|
+
raise ParamsReadyError, "Marshaller must be frozen" unless instance.frozen?
|
65
|
+
|
66
|
+
@default = instance
|
67
|
+
end
|
68
|
+
|
69
|
+
def default!(value_class)
|
70
|
+
instance = instance(value_class)
|
71
|
+
raise ParamsReadyError, "No marshaller for class '#{value_class.name}'" if instance.nil?
|
72
|
+
self.default = instance
|
73
|
+
end
|
74
|
+
|
75
|
+
def default?
|
76
|
+
!@default.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
def reverse_merge(other)
|
80
|
+
clone = self.class.new(@canonical, @default, @instances.dup)
|
81
|
+
populate_clone(clone, other)
|
82
|
+
end
|
83
|
+
|
84
|
+
def populate_clone(clone, other)
|
85
|
+
if other.default? && !clone.default?
|
86
|
+
clone.default = other.default
|
87
|
+
end
|
88
|
+
|
89
|
+
other.instances.each do |value_class, i|
|
90
|
+
next if clone.instance?(value_class)
|
91
|
+
|
92
|
+
clone.add_instance value_class, i
|
93
|
+
end
|
94
|
+
|
95
|
+
clone
|
96
|
+
end
|
97
|
+
|
98
|
+
def freeze
|
99
|
+
@instance.freeze
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class ClassCollection < InstanceCollection
|
105
|
+
attr_reader :factories
|
106
|
+
|
107
|
+
def initialize(canonical, default = nil, instances = {}, factories = {})
|
108
|
+
@factories = factories
|
109
|
+
super canonical, default, instances
|
110
|
+
end
|
111
|
+
|
112
|
+
def instance_collection
|
113
|
+
InstanceCollection.new(@canonical, nil, @instances.dup)
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_factory(name, factory)
|
117
|
+
name = name.to_sym
|
118
|
+
raise ParamsReadyError, "Name '#{name}' already taken" if factory?(name)
|
119
|
+
raise ParamsReadyError, "Factory must be frozen" unless factory.frozen?
|
120
|
+
|
121
|
+
@factories[name] = factory
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_instance(value_class, instance)
|
125
|
+
raise ParamsReadyError, "Marshaller for '#{value_class.name}' already exists" if instance?(value_class)
|
126
|
+
|
127
|
+
super
|
128
|
+
end
|
129
|
+
|
130
|
+
def build_instance(name, **opts)
|
131
|
+
factory(name).instance(**opts)
|
132
|
+
end
|
133
|
+
|
134
|
+
def factory(name)
|
135
|
+
@factories[name]
|
136
|
+
end
|
137
|
+
|
138
|
+
def factory?(name)
|
139
|
+
@factories.key? name
|
140
|
+
end
|
141
|
+
|
142
|
+
def freeze
|
143
|
+
@factories.freeze
|
144
|
+
super
|
145
|
+
end
|
146
|
+
|
147
|
+
def reverse_merge(other)
|
148
|
+
clone = self.class.new(@canonical, @default, @instances.dup, @factories.dup)
|
149
|
+
populate_clone(clone, other)
|
150
|
+
end
|
151
|
+
|
152
|
+
def populate_clone(clone, other)
|
153
|
+
merged = super
|
154
|
+
|
155
|
+
other.factories.each do |value_class, f|
|
156
|
+
next if merged.factory?(value_class)
|
157
|
+
|
158
|
+
clone.add_factory value_class, f
|
159
|
+
end
|
160
|
+
|
161
|
+
merged
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ParamsReady
|
2
|
+
module Marshaller
|
3
|
+
module DefinitionModule
|
4
|
+
def self.[](collection)
|
5
|
+
mod = Module.new
|
6
|
+
mod.include self
|
7
|
+
mod.define_method :class_marshallers do
|
8
|
+
collection
|
9
|
+
end
|
10
|
+
mod
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :marshallers
|
14
|
+
|
15
|
+
def initialize(*args, marshaller: nil, **options)
|
16
|
+
@marshallers = class_marshallers.instance_collection
|
17
|
+
set_marshaller(**marshaller) unless marshaller.nil?
|
18
|
+
|
19
|
+
super *args, **options
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_marshaller(to: nil, using: nil, **opts)
|
23
|
+
if using.is_a? Symbol
|
24
|
+
raise ParamsReadyError, "Expected ':to' argument to be nil, got #{to.class.name}" unless to.nil?
|
25
|
+
default_class, instance = class_marshallers.build_instance(using, **opts)
|
26
|
+
@marshallers.add_instance(default_class, instance)
|
27
|
+
@marshallers.default!(default_class)
|
28
|
+
elsif using.nil?
|
29
|
+
@marshallers.default!(to)
|
30
|
+
else
|
31
|
+
@marshallers.add_instance(to, using)
|
32
|
+
@marshallers.default!(to)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def marshal(parameter, intent, **opts)
|
37
|
+
if intent.marshal?(name_for_formatter)
|
38
|
+
@marshallers.marshal(parameter, intent, **opts)
|
39
|
+
else
|
40
|
+
@marshallers.marshal_canonical(parameter, intent, **opts)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def try_canonicalize(input, context, validator = nil, **opts)
|
45
|
+
@marshallers.canonicalize(self, input, context, validator, **opts)
|
46
|
+
end
|
47
|
+
|
48
|
+
def finish
|
49
|
+
unless @marshallers.default?
|
50
|
+
if class_marshallers.default?
|
51
|
+
@marshallers.default = class_marshallers.default
|
52
|
+
end
|
53
|
+
end
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
def freeze
|
58
|
+
@marshallers.freeze
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|