structure 3.6.3 → 4.0.0
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 +36 -7
- data/lib/structure/rbs.rb +14 -5
- data/lib/structure/types.rb +72 -75
- data/lib/structure/version.rb +1 -1
- data/lib/structure.rb +63 -62
- data/sig/structure/builder.rbs +7 -1
- data/sig/structure/rbs.rbs +1 -1
- data/sig/structure/types.rbs +12 -5
- data/sig/structure.rbs +2 -3
- 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: 2306964e18c5a78a2241c8c42071fd0a1921fa8c7c54cf210b0825b20607cfca
|
4
|
+
data.tar.gz: c43cf42996e715fe5c1ca1b57bf91ee411405f188382b2d10c0b593a66e90a6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 314242bf300d7ec09e1755647d4a8fef224eb2c64fc0b6d945be386b7af008c22146e80993f1e753f1df9cc5b37841242b7ab4a14d71cde42732952e288fc196
|
7
|
+
data.tar.gz: 72dcc21770b4f57b67fd64b8ad69695f02d0a55b2622717f63795b8da32aa8b45cd61db146b2e2c4dc02683588ca74dba831437652afaed9bb25fc159195d0b5
|
data/lib/structure/builder.rb
CHANGED
@@ -5,12 +5,15 @@ require "structure/types"
|
|
5
5
|
module Structure
|
6
6
|
# Builder class for accumulating attribute definitions
|
7
7
|
class Builder
|
8
|
-
|
8
|
+
# @api private
|
9
|
+
attr_reader :mappings, :defaults, :types, :after_parse_callback
|
9
10
|
|
11
|
+
# @api private
|
10
12
|
def initialize
|
11
13
|
@mappings = {}
|
12
|
-
@types = {}
|
13
14
|
@defaults = {}
|
15
|
+
@types = {}
|
16
|
+
@optional = Set.new
|
14
17
|
end
|
15
18
|
|
16
19
|
# DSL method for defining attributes with optional type coercion
|
@@ -43,6 +46,25 @@ module Structure
|
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
49
|
+
# DSL method for defining optional attributes (key can be missing from input hash)
|
50
|
+
#
|
51
|
+
# @param name [Symbol] The attribute name
|
52
|
+
# @param type [Class, Symbol, Array, nil] Type for coercion (e.g., String, :boolean, [String])
|
53
|
+
# @param from [String, nil] Source key in the data hash (defaults to name.to_s)
|
54
|
+
# @param default [Object, nil] Default value if attribute is missing
|
55
|
+
# @yield [value] Block for custom transformation
|
56
|
+
# @raise [ArgumentError] If both type and block are provided
|
57
|
+
#
|
58
|
+
# @example Optional attribute
|
59
|
+
# attribute? :age, Integer
|
60
|
+
#
|
61
|
+
# @example Optional with default
|
62
|
+
# attribute? :status, String, default: "pending"
|
63
|
+
def attribute?(name, type = nil, from: nil, default: nil, &block)
|
64
|
+
attribute(name, type, from: from, default: default, &block)
|
65
|
+
@optional.add(name)
|
66
|
+
end
|
67
|
+
|
46
68
|
# Defines a callback to run after parsing
|
47
69
|
#
|
48
70
|
# @yield [instance] Block that receives the parsed instance
|
@@ -56,14 +78,21 @@ module Structure
|
|
56
78
|
@after_parse_callback = block
|
57
79
|
end
|
58
80
|
|
59
|
-
|
60
|
-
|
61
|
-
|
81
|
+
# @api private
|
82
|
+
def attributes = @mappings.keys
|
83
|
+
|
84
|
+
# @api private
|
85
|
+
def optional = @optional.to_a
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
def required = attributes - optional
|
62
89
|
|
63
|
-
|
64
|
-
|
90
|
+
# @api private
|
91
|
+
def coercions(context = nil)
|
92
|
+
@types.transform_values { |type| Types.coerce(type, context) }
|
65
93
|
end
|
66
94
|
|
95
|
+
# @api private
|
67
96
|
def predicate_methods
|
68
97
|
@types.filter_map do |name, type|
|
69
98
|
if type == :boolean
|
data/lib/structure/rbs.rb
CHANGED
@@ -15,10 +15,15 @@ module Structure
|
|
15
15
|
# @type var meta: Hash[Symbol, untyped]
|
16
16
|
meta = klass.respond_to?(:__structure_meta__) ? klass.__structure_meta__ : {}
|
17
17
|
|
18
|
+
attributes = meta[:mappings] ? meta[:mappings].keys : klass.members
|
19
|
+
types = meta.fetch(:types, {}) # steep:ignore
|
20
|
+
required = meta.fetch(:required, attributes) # steep:ignore
|
21
|
+
|
18
22
|
emit_rbs_content(
|
19
|
-
class_name
|
20
|
-
attributes
|
21
|
-
types
|
23
|
+
class_name:,
|
24
|
+
attributes:,
|
25
|
+
types:,
|
26
|
+
required:,
|
22
27
|
has_structure_modules: meta.any?,
|
23
28
|
)
|
24
29
|
end
|
@@ -44,7 +49,7 @@ module Structure
|
|
44
49
|
|
45
50
|
private
|
46
51
|
|
47
|
-
def emit_rbs_content(class_name:, attributes:, types:, has_structure_modules:)
|
52
|
+
def emit_rbs_content(class_name:, attributes:, types:, required:, has_structure_modules:)
|
48
53
|
# @type var lines: Array[String]
|
49
54
|
lines = []
|
50
55
|
lines << "class #{class_name} < Data"
|
@@ -58,7 +63,11 @@ module Structure
|
|
58
63
|
[attr, rbs_type != "untyped" ? "#{rbs_type}?" : rbs_type]
|
59
64
|
end.to_h
|
60
65
|
|
61
|
-
|
66
|
+
# Mark optional attributes with ? prefix in keyword params
|
67
|
+
keyword_params = attributes.map do |attr|
|
68
|
+
prefix = required.include?(attr) ? "" : "?"
|
69
|
+
"#{prefix}#{attr}: #{rbs_types[attr]}"
|
70
|
+
end.join(", ")
|
62
71
|
positional_params = attributes.map { |attr| rbs_types[attr] }.join(", ")
|
63
72
|
|
64
73
|
lines << " def self.new: (#{keyword_params}) -> #{class_name}"
|
data/lib/structure/types.rb
CHANGED
@@ -11,130 +11,98 @@ module Structure
|
|
11
11
|
|
12
12
|
# Main factory method for creating type coercers
|
13
13
|
#
|
14
|
-
# @param type [Class, Symbol, Array, String] Type specification
|
15
|
-
# @
|
14
|
+
# @param type [Class, Symbol, Array, String, nil] Type specification
|
15
|
+
# @param context [Class, nil] Context class for lazy-loading and self-referential types
|
16
|
+
# @return [Proc, nil] Coercion proc or nil if no coercion needed
|
17
|
+
# @raise [ArgumentError] If type is a Hash instance (typed hashes not yet supported) or unsupported type
|
16
18
|
#
|
17
19
|
# @example Boolean type
|
18
20
|
# coerce(:boolean) # => boolean proc
|
19
21
|
#
|
22
|
+
# @example Self-referential types
|
23
|
+
# coerce(:self) # => proc that calls context.parse
|
24
|
+
#
|
20
25
|
# @example Kernel types
|
21
26
|
# coerce(Integer) # => proc that calls Kernel.Integer
|
22
27
|
#
|
28
|
+
# @example Array types
|
29
|
+
# coerce([String]) # => proc that coerces array elements to String
|
30
|
+
#
|
23
31
|
# @example Parseable types
|
24
32
|
# coerce(Date) # => proc that calls Date.parse
|
25
33
|
#
|
26
|
-
# @example
|
27
|
-
# coerce(
|
34
|
+
# @example No coercion
|
35
|
+
# coerce(nil) # => nil
|
36
|
+
#
|
37
|
+
# @example Custom lambdas
|
38
|
+
# coerce(->(val) { val.upcase }) # => returns the lambda itself
|
28
39
|
#
|
29
|
-
# @example
|
40
|
+
# @example Lazy-resolved classes
|
30
41
|
# coerce("MyClass") # => proc that resolves and coerces to MyClass
|
31
|
-
def coerce(type,
|
42
|
+
def coerce(type, context = nil)
|
32
43
|
case type
|
33
44
|
when :boolean
|
34
45
|
boolean
|
35
46
|
when :self
|
36
|
-
self_referential
|
37
|
-
when
|
38
|
-
|
47
|
+
self_referential(context)
|
48
|
+
when ->(t) { t.respond_to?(:name) && t.name && Kernel.respond_to?(t.name) }
|
49
|
+
kernel(type)
|
39
50
|
when ->(t) { t.is_a?(Array) && t.length == 1 }
|
40
|
-
array(type.first,
|
41
|
-
when Hash
|
42
|
-
raise ArgumentError, "Cannot specify #{type.inspect} as type"
|
51
|
+
array(type.first, context)
|
43
52
|
when ->(t) { t.respond_to?(:parse) }
|
44
53
|
parseable(type)
|
45
|
-
when ->(t) { t.respond_to?(:name) && t.name && Kernel.respond_to?(t.name) }
|
46
|
-
kernel(type)
|
47
|
-
when ->(t) { t.respond_to?(:call) }
|
48
|
-
type
|
49
54
|
when nil
|
50
55
|
type
|
56
|
+
when ->(t) { t.respond_to?(:call) }
|
57
|
+
type
|
58
|
+
when String
|
59
|
+
lazy_class(type, context)
|
51
60
|
else
|
52
61
|
raise ArgumentError, "Cannot specify #{type.inspect} as type"
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
56
|
-
def resolve_class(class_name, context_class)
|
57
|
-
if context_class && defined?(context_class.name)
|
58
|
-
namespace = context_class.name.to_s.split("::")[0...-1]
|
59
|
-
if namespace.any?
|
60
|
-
begin
|
61
|
-
namespace.reduce(Object) { |mod, name| mod.const_get(name) }.const_get(class_name)
|
62
|
-
rescue NameError
|
63
|
-
Object.const_get(class_name)
|
64
|
-
end
|
65
|
-
else
|
66
|
-
Object.const_get(class_name)
|
67
|
-
end
|
68
|
-
else
|
69
|
-
Object.const_get(class_name)
|
70
|
-
end
|
71
|
-
rescue NameError => e
|
72
|
-
raise NameError, "Unable to resolve class '#{class_name}': #{e.message}"
|
73
|
-
end
|
74
|
-
|
75
65
|
private
|
76
66
|
|
77
67
|
def boolean
|
78
|
-
->(val) { BOOLEAN_TRUTHY.include?(val) }
|
79
|
-
end
|
80
|
-
|
81
|
-
def self_referential
|
82
|
-
proc { |val| parse(val) }
|
68
|
+
@boolean ||= ->(val) { BOOLEAN_TRUTHY.include?(val) }
|
83
69
|
end
|
84
70
|
|
85
|
-
def
|
86
|
-
->(val) {
|
71
|
+
def self_referential(context)
|
72
|
+
->(val) { context.parse(val) }
|
87
73
|
end
|
88
74
|
|
89
|
-
def
|
90
|
-
->(val) { type.parse(val) }
|
91
|
-
end
|
92
|
-
|
93
|
-
def string_class(class_name, context_class)
|
75
|
+
def lazy_class(class_name, context)
|
94
76
|
resolved_class = nil
|
95
|
-
mutex = Mutex.new
|
96
77
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
if resolved_class.respond_to?(:parse)
|
105
|
-
resolved_class.parse(value) # steep:ignore
|
106
|
-
else
|
107
|
-
value
|
108
|
-
end
|
78
|
+
->(value) do
|
79
|
+
resolved_class ||= resolve_class(class_name, context)
|
80
|
+
# @type var resolved_class: untyped
|
81
|
+
resolved_class.parse(value)
|
109
82
|
end
|
110
83
|
end
|
111
84
|
|
112
|
-
def array(element_type,
|
113
|
-
|
114
|
-
|
85
|
+
def array(element_type, context = nil)
|
86
|
+
case element_type
|
87
|
+
when :self
|
88
|
+
lambda do |value|
|
115
89
|
unless value.respond_to?(:map)
|
116
90
|
raise TypeError, "can't convert #{value.class} into Array"
|
117
91
|
end
|
118
92
|
|
119
|
-
value.map { |element| parse(element) }
|
93
|
+
value.map { |element| context.parse(element) }
|
120
94
|
end
|
121
|
-
|
122
|
-
|
95
|
+
when String
|
96
|
+
lambda do |value|
|
123
97
|
unless value.respond_to?(:map)
|
124
98
|
raise TypeError, "can't convert #{value.class} into Array"
|
125
99
|
end
|
126
100
|
|
127
|
-
resolved_class =
|
128
|
-
value.map
|
129
|
-
if resolved_class.respond_to?(:parse)
|
130
|
-
resolved_class.parse(element)
|
131
|
-
else
|
132
|
-
element
|
133
|
-
end
|
134
|
-
end
|
101
|
+
resolved_class = resolve_class(element_type, context)
|
102
|
+
value.map { |element| resolved_class.parse(element) }
|
135
103
|
end
|
136
104
|
else
|
137
|
-
element_coercer = coerce(element_type,
|
105
|
+
element_coercer = coerce(element_type, context)
|
138
106
|
lambda do |value|
|
139
107
|
unless value.respond_to?(:map)
|
140
108
|
raise TypeError, "can't convert #{value.class} into Array"
|
@@ -144,6 +112,35 @@ module Structure
|
|
144
112
|
end
|
145
113
|
end
|
146
114
|
end
|
115
|
+
|
116
|
+
def parseable(type)
|
117
|
+
@parseable_cache ||= {} # : Hash[untyped, Proc]
|
118
|
+
@parseable_cache[type] ||= ->(val) { type.parse(val) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def kernel(type)
|
122
|
+
@kernel_cache ||= {} # : Hash[untyped, Proc]
|
123
|
+
@kernel_cache[type] ||= ->(val) { Kernel.send(type.name, val) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def resolve_class(class_name, context)
|
127
|
+
if context && defined?(context.name)
|
128
|
+
namespace = context.name.to_s.split("::")[0...-1]
|
129
|
+
if namespace.any?
|
130
|
+
begin
|
131
|
+
namespace.reduce(Object) { |mod, name| mod.const_get(name) }.const_get(class_name)
|
132
|
+
rescue NameError
|
133
|
+
Object.const_get(class_name)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
Object.const_get(class_name)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
Object.const_get(class_name)
|
140
|
+
end
|
141
|
+
rescue NameError => e
|
142
|
+
raise NameError, "Unable to resolve class '#{class_name}': #{e.message}"
|
143
|
+
end
|
147
144
|
end
|
148
145
|
end
|
149
146
|
end
|
data/lib/structure/version.rb
CHANGED
data/lib/structure.rb
CHANGED
@@ -26,94 +26,95 @@ module Structure
|
|
26
26
|
# @type var klass: untyped
|
27
27
|
klass = Data.define(*builder.attributes)
|
28
28
|
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
# Override initialize to make optional attributes truly optional
|
30
|
+
optional_attrs = builder.optional
|
31
|
+
unless optional_attrs.empty?
|
32
|
+
klass.class_eval do
|
33
|
+
alias_method(:__data_initialize__, :initialize)
|
34
|
+
|
35
|
+
define_method(:initialize) do |**kwargs| # steep:ignore
|
36
|
+
optional_attrs.each do |attr|
|
37
|
+
kwargs[attr] = nil unless kwargs.key?(attr)
|
38
|
+
end
|
39
|
+
__data_initialize__(**kwargs) # steep:ignore
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
34
43
|
|
44
|
+
builder.predicate_methods.each do |pred, attr|
|
45
|
+
klass.define_method(pred) { !!public_send(attr) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Store metadata on class to avoid closure capture (memory optimization)
|
35
49
|
meta = {
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
after: after,
|
50
|
+
types: builder.types,
|
51
|
+
defaults: builder.defaults,
|
52
|
+
mappings: builder.mappings,
|
53
|
+
coercions: builder.coercions(klass),
|
54
|
+
after_parse: builder.after_parse_callback,
|
55
|
+
required: builder.required,
|
43
56
|
}.freeze
|
44
57
|
klass.instance_variable_set(:@__structure_meta__, meta)
|
45
58
|
klass.singleton_class.attr_reader(:__structure_meta__)
|
46
59
|
|
47
|
-
# Define predicate methods
|
48
|
-
predicates.each do |pred, attr|
|
49
|
-
klass.define_method(pred) { !!public_send(attr) }
|
50
|
-
end
|
51
|
-
|
52
60
|
# recursive to_h
|
53
61
|
klass.define_method(:to_h) do
|
54
|
-
|
55
|
-
h = {}
|
56
|
-
klass.members.each do |m|
|
62
|
+
klass.members.to_h do |m|
|
57
63
|
v = public_send(m)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
+
value = case v
|
65
|
+
when Array then v.map { |x| x.respond_to?(:to_h) && x ? x.to_h : x }
|
66
|
+
when ->(x) { x.respond_to?(:to_h) && x } then v.to_h
|
67
|
+
else v
|
68
|
+
end
|
69
|
+
[m, value]
|
64
70
|
end
|
65
|
-
h
|
66
71
|
end
|
67
72
|
|
68
|
-
# parse accepts JSON-ish hashes +
|
69
|
-
|
73
|
+
# parse accepts JSON-ish hashes + optional overrides hash
|
74
|
+
# overrides is a positional arg (not **kwargs) to avoid hash allocation when unused
|
75
|
+
#
|
76
|
+
# @type self: singleton(Data) & _StructuredDataClass
|
77
|
+
# @type var final: Hash[Symbol, untyped]
|
78
|
+
klass.singleton_class.define_method(:parse) do |data = {}, overrides = nil|
|
70
79
|
return data if data.is_a?(self)
|
71
80
|
|
72
81
|
unless data.respond_to?(:merge!)
|
73
82
|
raise TypeError, "can't convert #{data.class} into #{self}"
|
74
83
|
end
|
75
84
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
elsif data.key?(source.to_sym) then data[source.to_sym]
|
97
|
-
elsif defaults.key?(attr) then defaults[attr]
|
85
|
+
overrides&.each { |k, v| data[k.to_s] = v }
|
86
|
+
|
87
|
+
final = {}
|
88
|
+
mappings = __structure_meta__[:mappings]
|
89
|
+
defaults = __structure_meta__[:defaults]
|
90
|
+
after_parse = __structure_meta__[:after_parse]
|
91
|
+
required = __structure_meta__[:required]
|
92
|
+
|
93
|
+
# Check for missing required attributes
|
94
|
+
required.each do |attr|
|
95
|
+
from = mappings[attr]
|
96
|
+
next if data.key?(from) || data.key?(from.to_sym) || defaults.key?(attr)
|
97
|
+
|
98
|
+
raise ArgumentError, "missing keyword: :#{attr}"
|
99
|
+
end
|
100
|
+
|
101
|
+
mappings.each do |attr, from|
|
102
|
+
value = data.fetch(from) do
|
103
|
+
data.fetch(from.to_sym) do
|
104
|
+
defaults[attr]
|
98
105
|
end
|
106
|
+
end
|
99
107
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
# Lambdas and other callables use direct invocation
|
104
|
-
value =
|
105
|
-
if coercion.is_a?(Proc) && !coercion.lambda?
|
106
|
-
instance_exec(value, &coercion) # steep:ignore
|
107
|
-
else
|
108
|
-
coercion.call(value)
|
109
|
-
end
|
108
|
+
if value
|
109
|
+
coercion = __structure_meta__[:coercions][attr]
|
110
|
+
value = coercion.call(value) if coercion
|
110
111
|
end
|
111
112
|
|
112
113
|
final[attr] = value
|
113
114
|
end
|
114
115
|
|
115
116
|
obj = new(**final)
|
116
|
-
|
117
|
+
after_parse&.call(obj)
|
117
118
|
obj
|
118
119
|
end
|
119
120
|
|
data/sig/structure/builder.rbs
CHANGED
@@ -4,18 +4,24 @@ module Structure
|
|
4
4
|
@types: Hash[Symbol, untyped]
|
5
5
|
@defaults: Hash[Symbol, untyped]
|
6
6
|
@after_parse_callback: Proc?
|
7
|
+
@optional: Set[Symbol]
|
7
8
|
|
8
9
|
def attribute: (Symbol name, untyped type, ?from: String?, ?default: untyped) ?{ (untyped) -> untyped } -> void
|
9
10
|
| (Symbol name, ?from: String, ?default: untyped) ?{ (untyped) -> untyped } -> void
|
10
11
|
|
12
|
+
def attribute?: (Symbol name, untyped type, ?from: String?, ?default: untyped) ?{ (untyped) -> untyped } -> void
|
13
|
+
| (Symbol name, ?from: String, ?default: untyped) ?{ (untyped) -> untyped } -> void
|
14
|
+
|
11
15
|
def after_parse: () { (Data) -> void } -> void
|
12
16
|
|
13
17
|
def attributes: () -> Array[Symbol]
|
14
18
|
def mappings: () -> Hash[Symbol, String]
|
15
19
|
def types: () -> Hash[Symbol, untyped]
|
16
20
|
def defaults: () -> Hash[Symbol, untyped]
|
17
|
-
def coercions: (?untyped?
|
21
|
+
def coercions: (?untyped? context) -> Hash[Symbol, Proc]
|
18
22
|
def predicate_methods: () -> Hash[Symbol, Symbol]
|
23
|
+
def optional: () -> Array[Symbol]
|
24
|
+
def required: () -> Array[Symbol]
|
19
25
|
def after_parse_callback: () -> (Proc | nil)
|
20
26
|
end
|
21
27
|
end
|
data/sig/structure/rbs.rbs
CHANGED
@@ -3,7 +3,7 @@ module Structure
|
|
3
3
|
def self.emit: (untyped klass) -> String?
|
4
4
|
def self.write: (untyped klass, ?dir: String) -> String?
|
5
5
|
|
6
|
-
private def self.emit_rbs_content: (class_name: String, attributes: Array[Symbol], types: Hash[Symbol, untyped], has_structure_modules: bool) -> String
|
6
|
+
private def self.emit_rbs_content: (class_name: String, attributes: Array[Symbol], types: Hash[Symbol, untyped], required: Array[Symbol], has_structure_modules: bool) -> String
|
7
7
|
private def self.parse_data_type: (untyped type, String class_name) -> String
|
8
8
|
private def self.map_type_to_rbs: (untyped type, String class_name) -> String
|
9
9
|
end
|
data/sig/structure/types.rbs
CHANGED
@@ -1,18 +1,25 @@
|
|
1
1
|
module Structure
|
2
2
|
module Types
|
3
|
+
interface _ParseableClass
|
4
|
+
def parse: (untyped) -> untyped
|
5
|
+
end
|
6
|
+
|
3
7
|
BOOLEAN_TRUTHY: Array[untyped]
|
4
8
|
|
5
9
|
self.@boolean: Proc
|
10
|
+
self.@kernel_cache: Hash[untyped, Proc]
|
11
|
+
self.@parseable_cache: Hash[untyped, Proc]
|
12
|
+
self.@string_class_cache: Hash[Array[untyped], Proc]
|
6
13
|
|
7
|
-
def self.coerce: (untyped type, ?untyped?
|
8
|
-
def self.resolve_class: (String class_name, untyped? context_class) -> untyped
|
14
|
+
def self.coerce: (untyped type, ?untyped? context) -> untyped
|
9
15
|
|
10
16
|
private def self.boolean: () -> Proc
|
11
|
-
private def self.self_referential: () -> Proc
|
12
|
-
private def self.
|
13
|
-
private def self.array: (untyped element_type, ?untyped?
|
17
|
+
private def self.self_referential: (_ParseableClass context) -> Proc
|
18
|
+
private def self.lazy_class: (String class_name, untyped? context) -> Proc
|
19
|
+
private def self.array: (untyped element_type, ?untyped? context) -> Proc
|
14
20
|
private def self.parseable: (untyped type) -> Proc
|
15
21
|
private def self.kernel: (Class type) -> Proc
|
16
22
|
private def self.parse: (untyped val) -> untyped
|
23
|
+
private def self.resolve_class: (String class_name, untyped? context) -> _ParseableClass
|
17
24
|
end
|
18
25
|
end
|
data/sig/structure.rbs
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
module Structure
|
2
2
|
interface _StructuredDataClass
|
3
3
|
def __structure_meta__: () -> {
|
4
|
-
attributes: Array[Symbol],
|
5
4
|
types: Hash[Symbol, untyped],
|
6
5
|
defaults: Hash[Symbol, untyped],
|
7
6
|
mappings: Hash[Symbol, String],
|
8
7
|
coercions: Hash[Symbol, untyped],
|
9
|
-
|
10
|
-
|
8
|
+
after_parse: untyped,
|
9
|
+
required: Array[Symbol]
|
11
10
|
}
|
12
11
|
end
|
13
12
|
|