structure 3.6.1 → 3.6.2
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 +4 -4
- data/lib/structure/builder.rb +1 -1
- data/lib/structure/types.rb +9 -2
- data/lib/structure/version.rb +1 -1
- data/lib/structure.rb +55 -48
- data/sig/structure.rbs +5 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49afeef61e56c6e3f65ad2eff0855af54552ee4dbcab2282a8d0194e30156362
|
4
|
+
data.tar.gz: 1b7889d683dd5a129d35446773729212ea0f1348958688e4520f98fab8d90e8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c6ab2be186a6a1eec57cc1bdad3627cccbcd91c20ff803a69d789cd694568c128853431ccb14b7694980700ef95675555919e5e944bf5df54c13c11497c2a19
|
7
|
+
data.tar.gz: 6d942ea5cc6bb8d012c8c7245480f7bb66857fb30ec20d8afd948183c7c29220baa9129abca1f17037c4db62c159e6860fe0a82c6c837639741c5c9ccec43c7a
|
data/lib/structure/builder.rb
CHANGED
data/lib/structure/types.rb
CHANGED
@@ -75,7 +75,7 @@ module Structure
|
|
75
75
|
private
|
76
76
|
|
77
77
|
def boolean
|
78
|
-
|
78
|
+
->(val) { BOOLEAN_TRUTHY.include?(val) }
|
79
79
|
end
|
80
80
|
|
81
81
|
def self_referential
|
@@ -92,8 +92,15 @@ module Structure
|
|
92
92
|
|
93
93
|
def string_class(class_name, context_class)
|
94
94
|
resolved_class = nil
|
95
|
+
mutex = Mutex.new
|
96
|
+
|
95
97
|
proc do |value|
|
96
|
-
resolved_class
|
98
|
+
unless resolved_class
|
99
|
+
mutex.synchronize do
|
100
|
+
resolved_class ||= Structure::Types.resolve_class(class_name, context_class)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
97
104
|
if resolved_class.respond_to?(:parse)
|
98
105
|
resolved_class.parse(value) # steep:ignore
|
99
106
|
else
|
data/lib/structure/version.rb
CHANGED
data/lib/structure.rb
CHANGED
@@ -26,21 +26,24 @@ module Structure
|
|
26
26
|
# @type var klass: untyped
|
27
27
|
klass = Data.define(*builder.attributes)
|
28
28
|
|
29
|
-
# capture metadata and attach to class
|
29
|
+
# capture all metadata and attach to class - no closure capture needed
|
30
|
+
mappings = builder.mappings
|
31
|
+
coercions = builder.coercions(klass)
|
32
|
+
predicates = builder.predicate_methods
|
33
|
+
after = builder.after_parse_callback
|
34
|
+
|
30
35
|
meta = {
|
31
36
|
attributes: builder.attributes.freeze,
|
32
37
|
types: builder.types.freeze,
|
33
38
|
defaults: builder.defaults.freeze,
|
39
|
+
mappings: mappings.freeze,
|
40
|
+
coercions: coercions.freeze,
|
41
|
+
predicates: predicates.freeze,
|
42
|
+
after: after,
|
34
43
|
}.freeze
|
35
44
|
klass.instance_variable_set(:@__structure_meta__, meta)
|
36
45
|
klass.singleton_class.attr_reader(:__structure_meta__)
|
37
46
|
|
38
|
-
# capture locals for method generation
|
39
|
-
mappings = builder.mappings
|
40
|
-
coercions = builder.coercions(klass)
|
41
|
-
predicates = builder.predicate_methods
|
42
|
-
after = builder.after_parse_callback
|
43
|
-
|
44
47
|
# Define predicate methods
|
45
48
|
predicates.each do |pred, attr|
|
46
49
|
klass.define_method(pred) { !!public_send(attr) }
|
@@ -62,55 +65,59 @@ module Structure
|
|
62
65
|
h
|
63
66
|
end
|
64
67
|
|
65
|
-
# parse accepts JSON-ish hashes + kwargs override
|
66
|
-
klass.
|
67
|
-
|
68
|
+
# parse accepts JSON-ish hashes + kwargs override - using string eval to avoid closure capture
|
69
|
+
klass.singleton_class.class_eval(<<~RUBY)
|
70
|
+
def parse(data = {}, **kwargs)
|
71
|
+
return data if data.is_a?(self)
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
unless data.respond_to?(:merge!)
|
74
|
+
raise TypeError, "can't convert \#{data.class} into \#{self}"
|
75
|
+
end
|
72
76
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
attributes.each do |attr|
|
89
|
-
source = mappings[attr] || attr.to_s
|
90
|
-
value =
|
91
|
-
if data.key?(source) then data[source]
|
92
|
-
elsif data.key?(source.to_sym) then data[source.to_sym]
|
93
|
-
elsif defaults.key?(attr) then defaults[attr]
|
94
|
-
end
|
77
|
+
# @type var kwargs: Hash[Symbol, untyped]
|
78
|
+
string_kwargs = kwargs.transform_keys(&:to_s)
|
79
|
+
data.merge!(string_kwargs)
|
80
|
+
# @type self: singleton(Data) & _StructuredDataClass
|
81
|
+
# @type var final: Hash[Symbol, untyped]
|
82
|
+
final = {}
|
83
|
+
|
84
|
+
# @type var meta: untyped
|
85
|
+
meta = __structure_meta__
|
86
|
+
|
87
|
+
attributes = meta.fetch(:attributes)
|
88
|
+
defaults = meta.fetch(:defaults)
|
89
|
+
mappings = meta.fetch(:mappings)
|
90
|
+
coercions = meta.fetch(:coercions)
|
91
|
+
after = meta.fetch(:after)
|
95
92
|
|
96
|
-
|
97
|
-
|
98
|
-
# self-referential types need class context to call parse
|
93
|
+
attributes.each do |attr|
|
94
|
+
source = mappings[attr] || attr.to_s
|
99
95
|
value =
|
100
|
-
if
|
101
|
-
|
102
|
-
|
103
|
-
coercion.call(value)
|
96
|
+
if data.key?(source) then data[source]
|
97
|
+
elsif data.key?(source.to_sym) then data[source.to_sym]
|
98
|
+
elsif defaults.key?(attr) then defaults[attr]
|
104
99
|
end
|
100
|
+
|
101
|
+
coercion = coercions[attr]
|
102
|
+
if coercion && !value.nil?
|
103
|
+
# Procs (not lambdas) need class context for self-referential parsing
|
104
|
+
# Lambdas and other callables use direct invocation
|
105
|
+
value =
|
106
|
+
if coercion.is_a?(Proc) && !coercion.lambda?
|
107
|
+
instance_exec(value, &coercion) # steep:ignore
|
108
|
+
else
|
109
|
+
coercion.call(value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
final[attr] = value
|
105
114
|
end
|
106
115
|
|
107
|
-
|
116
|
+
obj = new(**final)
|
117
|
+
after&.call(obj) if after
|
118
|
+
obj
|
108
119
|
end
|
109
|
-
|
110
|
-
obj = new(**final)
|
111
|
-
after&.call(obj)
|
112
|
-
obj
|
113
|
-
end
|
120
|
+
RUBY
|
114
121
|
|
115
122
|
klass
|
116
123
|
end
|
data/sig/structure.rbs
CHANGED
@@ -3,9 +3,12 @@ module Structure
|
|
3
3
|
def __structure_meta__: () -> {
|
4
4
|
attributes: Array[Symbol],
|
5
5
|
types: Hash[Symbol, untyped],
|
6
|
-
defaults: Hash[Symbol, untyped]
|
6
|
+
defaults: Hash[Symbol, untyped],
|
7
|
+
mappings: Hash[Symbol, String],
|
8
|
+
coercions: Hash[Symbol, untyped],
|
9
|
+
predicates: Hash[Symbol, Symbol],
|
10
|
+
after: untyped
|
7
11
|
}
|
8
|
-
def parse: (?Hash[String | Symbol, untyped] data, **untyped kwargs) -> instance
|
9
12
|
end
|
10
13
|
|
11
14
|
def self.new: () ?{ (Structure::Builder) [self: Structure::Builder] -> void } -> untyped
|