yeptris 0.6.18.2-arm-linux-eabihf
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/README.adoc +141 -0
- data/ext/build_windows_native.rb +41 -0
- data/ext/libyeptris/extconf.rb +57 -0
- data/ext/yeptris_native/cbor_ruby.c +193 -0
- data/ext/yeptris_native/extconf.rb +47 -0
- data/ext/yeptris_native/json_ruby.c +354 -0
- data/ext/yeptris_native/yeptris_native.c +497 -0
- data/lib/yeptris/cbor.rb +130 -0
- data/lib/yeptris/document.rb +255 -0
- data/lib/yeptris/ffi.rb +426 -0
- data/lib/yeptris/json/descriptor.rb +266 -0
- data/lib/yeptris/json.rb +394 -0
- data/lib/yeptris/materializer.rb +354 -0
- data/lib/yeptris/native-3.4.so +0 -0
- data/lib/yeptris/node.rb +453 -0
- data/lib/yeptris/psych/class_loader.rb +24 -0
- data/lib/yeptris/psych/coder_shim.rb +55 -0
- data/lib/yeptris/psych/drop_in.rb +63 -0
- data/lib/yeptris/psych/encodable.rb +14 -0
- data/lib/yeptris/psych/handler.rb +90 -0
- data/lib/yeptris/psych/parser.rb +105 -0
- data/lib/yeptris/psych/scalar_scanner.rb +149 -0
- data/lib/yeptris/psych/visitors.rb +545 -0
- data/lib/yeptris/psych.rb +622 -0
- data/lib/yeptris/schema.rb +253 -0
- data/lib/yeptris/valueml.rb +455 -0
- data/lib/yeptris/yaml/descriptor.rb +56 -0
- data/lib/yeptris/yaml.rb +422 -0
- data/lib/yeptris.rb +113 -0
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +254 -0
- data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
- data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
- data/vendor/libyeptris/src/CMakeLists.txt +167 -0
- data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
- data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
- data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
- data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
- data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
- data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
- data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
- data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
- data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
- data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
- data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
- data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
- data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
- data/vendor/libyeptris/src/include/yeptris.h +23 -0
- data/vendor/libyeptris/src/yeptris/build.c +376 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
- data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
- data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
- data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
- data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
- data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
- data/vendor/libyeptris/src/yeptris/doc.h +56 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
- data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
- data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
- data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
- data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
- data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
- data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
- data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
- data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
- data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
- data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
- data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
- data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
- data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
- data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
- data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
- data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
- data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
- data/vendor/libyeptris/src/yeptris/parse.c +737 -0
- data/vendor/libyeptris/src/yeptris/plan.c +824 -0
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
- data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
- data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
- data/vendor/libyeptris/src/yeptris/schema.c +308 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
- data/vendor/libyeptris/src/yeptris/version.c +7 -0
- data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
- data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
- data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
- metadata +199 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "time"
|
|
5
|
+
require "set"
|
|
6
|
+
|
|
7
|
+
module Yeptris
|
|
8
|
+
module Psych
|
|
9
|
+
module Visitors
|
|
10
|
+
# The dump-side visitor (Psych's YAMLTree core): an arbitrary
|
|
11
|
+
# Ruby object graph becomes DOM nodes — anchors for shared
|
|
12
|
+
# objects, aliases for repeats, the Encodable protocol, and the
|
|
13
|
+
# !ruby/... tags Psych's own emitter produces.
|
|
14
|
+
class YAMLTree
|
|
15
|
+
# stdlib's factory (relaton's db cache calls it): the options
|
|
16
|
+
# ride our visitor defaults
|
|
17
|
+
def self.create(options = {}, emitter = nil)
|
|
18
|
+
new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@tree = ::Yeptris::Document.create
|
|
23
|
+
@names = {} # object_id -> anchor name
|
|
24
|
+
@counter = 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def push(obj)
|
|
28
|
+
@refs = Hash.new(0)
|
|
29
|
+
@refs[obj.object_id] += 1 # the root counts as a reference
|
|
30
|
+
count_refs(obj, {})
|
|
31
|
+
root = visit(obj)
|
|
32
|
+
@tree.set_root(root) if root
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
alias_method :<<, :push # stdlib's dump body pushes via <<
|
|
36
|
+
|
|
37
|
+
# stdlib Psych.dump's tail: `visitor.tree.yaml io, options` —
|
|
38
|
+
# the tree wraps the built document; yaml serializes it
|
|
39
|
+
# (string return, or writes + returns the io)
|
|
40
|
+
def tree
|
|
41
|
+
BuiltDocument.new(@tree)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The raw document (the CBOR encoder consumes the tree
|
|
45
|
+
# directly; #tree wraps it for stdlib's yaml face)
|
|
46
|
+
def document
|
|
47
|
+
@tree
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def finish
|
|
51
|
+
@tree.serialize(explicit_doc_start: true)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The built-document handle stdlib's dump tail serializes
|
|
55
|
+
class BuiltDocument
|
|
56
|
+
def initialize(doc)
|
|
57
|
+
@doc = doc
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def yaml(io = nil, _options = {})
|
|
61
|
+
out = @doc.serialize(explicit_doc_start: true)
|
|
62
|
+
return out unless io
|
|
63
|
+
|
|
64
|
+
io.write(out)
|
|
65
|
+
io
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# stdlib register()s every object — scalars included — so a
|
|
70
|
+
# repeated scalar dumps &name/*name (test_float_references,
|
|
71
|
+
# test_alias_with_time). Containers already route through
|
|
72
|
+
# anchor_for; this is the scalar arm of the same machinery.
|
|
73
|
+
def anchored_scalar(obj, text: nil, tag: nil)
|
|
74
|
+
state, name = anchor_for(obj)
|
|
75
|
+
return alias_of(obj, name) if state == :alias
|
|
76
|
+
|
|
77
|
+
n = scalar(text || obj, tag: tag)
|
|
78
|
+
n.set_anchor(name) if name
|
|
79
|
+
remember(obj, n)
|
|
80
|
+
n
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def visit(obj)
|
|
84
|
+
case obj
|
|
85
|
+
# strings/floats/ints: stdlib yaml_tree does NOT anchor them
|
|
86
|
+
# (its float test is load-side only) — and CBOR encode
|
|
87
|
+
# rejects the anchors the cbor profile feeds it
|
|
88
|
+
when nil, true, false, ::Integer, ::Float, ::String then scalar(obj)
|
|
89
|
+
when ::Symbol then @tree.new_scalar(":#{obj}", :plain) # psych emits symbols bare, never through visit_String's quoting rules
|
|
90
|
+
# stdlib visit_DateTime: the class tag carries the type (a
|
|
91
|
+
# plain timestamp re-loads as Time) — "!ruby/object:DateTime
|
|
92
|
+
# 2017-04-13 12:00:00.500000000 +09:00", to_s text, one line
|
|
93
|
+
when ::DateTime
|
|
94
|
+
anchored_scalar(obj, text: obj.strftime("%F %H:%M:%S.%9N %:z"),
|
|
95
|
+
tag: "!ruby/object:DateTime")
|
|
96
|
+
when ::Date, ::Time then anchored_scalar(obj) # timestamp text, never ivars
|
|
97
|
+
when ::Hash then visit_hash(obj)
|
|
98
|
+
when ::Array then visit_array(obj)
|
|
99
|
+
when ::Struct then visit_struct(obj)
|
|
100
|
+
when ::Set then visit_set(obj)
|
|
101
|
+
when Encodable then visit_encode_with(obj)
|
|
102
|
+
else
|
|
103
|
+
if obj.instance_of?(::Object)
|
|
104
|
+
# A BARE Object has no declared state to lose — the
|
|
105
|
+
# empty !ruby/object form is correct by construction
|
|
106
|
+
# (an Object with ivars set from outside its class is
|
|
107
|
+
# outside the encapsulation contract; use a real class
|
|
108
|
+
# with Encodable for stateful objects).
|
|
109
|
+
return visit_bare_object(obj)
|
|
110
|
+
end
|
|
111
|
+
refuse_dump(obj)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# Psych anchors only objects that appear more than once;
|
|
118
|
+
# single-occurrence containers dump clean (no &o1 noise)
|
|
119
|
+
def anchor_for(obj)
|
|
120
|
+
oid = obj.object_id
|
|
121
|
+
name = @names[oid]
|
|
122
|
+
return [:alias, name] if name
|
|
123
|
+
|
|
124
|
+
if @refs[oid] < 2
|
|
125
|
+
return [:none, nil]
|
|
126
|
+
end
|
|
127
|
+
name = (@counter += 1).to_s # stdlib: &1, *1
|
|
128
|
+
@names[oid] = name
|
|
129
|
+
[:new, name]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def count_refs(obj, seen)
|
|
133
|
+
return if seen.key?(obj.object_id)
|
|
134
|
+
|
|
135
|
+
seen[obj.object_id] = true
|
|
136
|
+
case obj
|
|
137
|
+
when Hash
|
|
138
|
+
obj.each_value { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
139
|
+
when Array
|
|
140
|
+
obj.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
141
|
+
when Struct
|
|
142
|
+
obj.each_pair { |_k, v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
143
|
+
when Set
|
|
144
|
+
obj.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
145
|
+
when ::String, ::Integer, ::Float, ::Symbol, ::Date, ::Time, true, false, nil, ::Numeric
|
|
146
|
+
# immutables: identity anchors are meaningless
|
|
147
|
+
else
|
|
148
|
+
unless obj.instance_of?(::Object)
|
|
149
|
+
refuse_dump(obj) unless obj.is_a?(Encodable)
|
|
150
|
+
# Encodable: descend through the PUBLIC protocol so
|
|
151
|
+
# shared objects inside coder contents still get
|
|
152
|
+
# anchors — encode_with must be pure (it runs once more
|
|
153
|
+
# at dump).
|
|
154
|
+
coder = ::Yeptris::Psych::CoderShim.new(nil)
|
|
155
|
+
obj.encode_with(coder)
|
|
156
|
+
case coder.type
|
|
157
|
+
when :seq
|
|
158
|
+
coder.seq.each { |v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
159
|
+
when :map
|
|
160
|
+
coder.each { |_k, v| @refs[v.object_id] += 1; count_refs(v, seen) }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def refuse_dump(obj)
|
|
167
|
+
raise ::Yeptris::DumpError,
|
|
168
|
+
"cannot dump #{obj.class}: include Yeptris::Psych::Encodable and implement encode_with(coder)"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def scalar(obj, tag: nil)
|
|
172
|
+
# strings route through the builder's plain-safety helper
|
|
173
|
+
# (one quoting rule, DRY); other scalars are their own text
|
|
174
|
+
text =
|
|
175
|
+
if obj.is_a?(::Time)
|
|
176
|
+
# psych-5's format_time (#300 family 1) — the FIRST cut
|
|
177
|
+
# put this branch after the Date||Time iso8601 one, dead
|
|
178
|
+
# code: Psych.dump of a Hash still emitted iso8601 (the
|
|
179
|
+
# neutral surface alone was pinned)
|
|
180
|
+
::Yeptris::YAML::BulkBuilder.time_text(obj)
|
|
181
|
+
elsif obj.is_a?(::Date)
|
|
182
|
+
obj.iso8601 # canonical calendar form
|
|
183
|
+
elsif obj.nil?
|
|
184
|
+
"" # libyaml's null rendering rides bare (issue #290)
|
|
185
|
+
elsif obj.is_a?(::Float)
|
|
186
|
+
::Yeptris::YAML::BulkBuilder.float_text(obj)
|
|
187
|
+
else
|
|
188
|
+
obj.to_s
|
|
189
|
+
end
|
|
190
|
+
n =
|
|
191
|
+
if obj.is_a?(::String) && !tag
|
|
192
|
+
::Yeptris::YAML::Builder.build_string(@tree, obj)
|
|
193
|
+
else
|
|
194
|
+
@tree.new_scalar(text, :plain)
|
|
195
|
+
end
|
|
196
|
+
n.set_tag(tag) if tag
|
|
197
|
+
n
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def visit_hash(hash)
|
|
201
|
+
state, name = anchor_for(hash)
|
|
202
|
+
return alias_of(hash, name) if state == :alias
|
|
203
|
+
|
|
204
|
+
m = @tree.new_mapping
|
|
205
|
+
remember(hash, m)
|
|
206
|
+
m.set_anchor(name) if name
|
|
207
|
+
hash.each do |k, v|
|
|
208
|
+
if k.nil?
|
|
209
|
+
# Psych's nil-key form: `! ''` — the explicit tag on an
|
|
210
|
+
# empty single-quoted key (#300 family 2)
|
|
211
|
+
key_node = @tree.new_scalar("", :single_quoted)
|
|
212
|
+
key_node.set_tag("!")
|
|
213
|
+
m.map_add_node(key_node, visit(v))
|
|
214
|
+
elsif k.is_a?(::String) && k == "<<"
|
|
215
|
+
# stdlib yaml_tree: a literal chevron key carries the
|
|
216
|
+
# explicit !!str tag (single-quoted) so it does not
|
|
217
|
+
# re-load as a merge
|
|
218
|
+
key_node = @tree.new_scalar("<<", :single_quoted)
|
|
219
|
+
key_node.set_tag("!!str")
|
|
220
|
+
m.map_add_node(key_node, visit(v))
|
|
221
|
+
else
|
|
222
|
+
m.map_add(key_text(k), visit(v))
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
m
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def visit_array(ary)
|
|
229
|
+
state, name = anchor_for(ary)
|
|
230
|
+
return alias_of(ary, name) if state == :alias
|
|
231
|
+
|
|
232
|
+
s = @tree.new_sequence
|
|
233
|
+
remember(ary, s)
|
|
234
|
+
s.set_anchor(name) if name
|
|
235
|
+
ary.each { |e| s.seq_add(visit(e)) }
|
|
236
|
+
s
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def visit_struct(strct)
|
|
240
|
+
state, name = anchor_for(strct)
|
|
241
|
+
return alias_of(strct, name) if state == :alias
|
|
242
|
+
|
|
243
|
+
m = @tree.new_mapping
|
|
244
|
+
remember(strct, m)
|
|
245
|
+
m.set_anchor(name) if name
|
|
246
|
+
m.set_tag(strct.class.name.to_s.empty? ? "!ruby/struct" : "!ruby/struct:#{strct.class.name}")
|
|
247
|
+
strct.each_pair { |k, v| m.map_add(key_text(k), visit(v)) }
|
|
248
|
+
m
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def visit_set(set)
|
|
252
|
+
state, name = anchor_for(set)
|
|
253
|
+
return alias_of(set, name) if state == :alias
|
|
254
|
+
|
|
255
|
+
s = @tree.new_sequence
|
|
256
|
+
remember(set, s)
|
|
257
|
+
s.set_anchor(name) if name
|
|
258
|
+
s.set_tag("!ruby/set")
|
|
259
|
+
set.each { |e| s.seq_add(visit(e)) }
|
|
260
|
+
s
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def visit_encode_with(obj)
|
|
264
|
+
state, name = anchor_for(obj)
|
|
265
|
+
return alias_of(obj, name) if state == :alias
|
|
266
|
+
|
|
267
|
+
coder = ::Yeptris::Psych::CoderShim.new(obj.class.name)
|
|
268
|
+
obj.encode_with(coder) # the typed public surface
|
|
269
|
+
# Wrap coder contents in a __init__ sub-mapping so the load
|
|
270
|
+
# side can dispatch back into obj.init_with(coder) without
|
|
271
|
+
# library-side ivar reflection (the encapsulation law).
|
|
272
|
+
init = @tree.new_mapping
|
|
273
|
+
case coder.type
|
|
274
|
+
when :scalar
|
|
275
|
+
init.map_add("__scalar__", scalar(coder.scalar))
|
|
276
|
+
init.map_add("__tag__", scalar(coder.tag)) if coder.tag
|
|
277
|
+
when :seq
|
|
278
|
+
inner = @tree.new_sequence
|
|
279
|
+
inner.set_tag(coder.tag) if coder.tag
|
|
280
|
+
coder.seq.each { |e| inner.seq_add(visit(e)) }
|
|
281
|
+
init.seq_add(inner)
|
|
282
|
+
else
|
|
283
|
+
coder.each { |k, v| init.map_add(key_text(k), visit(v)) }
|
|
284
|
+
init.set_tag(coder.tag) if coder.tag
|
|
285
|
+
end
|
|
286
|
+
m = @tree.new_mapping
|
|
287
|
+
m.set_anchor(name) if name
|
|
288
|
+
custom = ::Yeptris::Psych.dump_tags[obj.class]
|
|
289
|
+
m.set_tag(custom || "!ruby/object:#{obj.class.name}")
|
|
290
|
+
m.map_add("__init__", init)
|
|
291
|
+
remember(obj, m)
|
|
292
|
+
m
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def visit_bare_object(obj)
|
|
296
|
+
state, name = anchor_for(obj)
|
|
297
|
+
return alias_of(obj, name) if state == :alias
|
|
298
|
+
|
|
299
|
+
m = @tree.new_mapping
|
|
300
|
+
remember(obj, m)
|
|
301
|
+
m.set_anchor(name) if name
|
|
302
|
+
m.set_tag("!ruby/object")
|
|
303
|
+
m
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def alias_of(obj, name)
|
|
307
|
+
@tree.new_alias(@nodes.fetch(obj.object_id), name)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def remember(obj, node)
|
|
311
|
+
(@nodes ||= {})[obj.object_id] = node
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def key_text(k)
|
|
315
|
+
k.is_a?(Symbol) ? ":#{k}" : k.to_s
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Load side: revival of !ruby/... tags over the parsed Nodes
|
|
321
|
+
# tree. The Materializer stays the plain-data fast path.
|
|
322
|
+
class ToRuby
|
|
323
|
+
def self.visit(node)
|
|
324
|
+
new.visit(node)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# stdlib ToRuby#accept's domain post-pass: normalize the tag
|
|
328
|
+
# (strip leading !//, ensure tag: unless x-private) and run the
|
|
329
|
+
# registered block over the revived result (#95 thread)
|
|
330
|
+
def apply_domain_type(result, node)
|
|
331
|
+
return result if node.tag.nil? || node.tag.empty?
|
|
332
|
+
|
|
333
|
+
key = node.tag.sub(/\A[!\/]*/, "").sub(/(,\d+)\//, '\1:')
|
|
334
|
+
key = "tag:#{key}" unless key.match?(/\A(?:tag:|x-private)/)
|
|
335
|
+
entry = ::Yeptris::Psych.domain_types[key]
|
|
336
|
+
return result unless entry
|
|
337
|
+
|
|
338
|
+
_value, block = entry
|
|
339
|
+
block.call(key, result)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def visit(node)
|
|
343
|
+
@root ||= node
|
|
344
|
+
result =
|
|
345
|
+
case node
|
|
346
|
+
when ::Yeptris::Psych::Nodes::Alias then visit_alias(node)
|
|
347
|
+
when ::Yeptris::Psych::Nodes::Scalar then visit_scalar(node)
|
|
348
|
+
when ::Yeptris::Psych::Nodes::Sequence then visit_sequence(node)
|
|
349
|
+
when ::Yeptris::Psych::Nodes::Mapping then visit_mapping(node)
|
|
350
|
+
else node&.to_ruby
|
|
351
|
+
end
|
|
352
|
+
# stdlib register(): an anchored node's result is THE object
|
|
353
|
+
# for every alias to that anchor (scalars included)
|
|
354
|
+
anchors[node.anchor] = result if node.respond_to?(:anchor) && node.anchor
|
|
355
|
+
# the domain-types post-pass (stdlib ToRuby#accept's tail):
|
|
356
|
+
# registered blocks transform the revived result per node
|
|
357
|
+
if node && !::Yeptris::Psych.domain_types.empty?
|
|
358
|
+
result = apply_domain_type(result, node)
|
|
359
|
+
end
|
|
360
|
+
result
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
private
|
|
364
|
+
|
|
365
|
+
def anchors
|
|
366
|
+
@anchors ||= {}
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def visit_alias(node)
|
|
370
|
+
name = node.anchor
|
|
371
|
+
unless anchors.key?(name)
|
|
372
|
+
target = find_anchored(root_container(node), name)
|
|
373
|
+
anchors[name] = target ? visit(target) : nil
|
|
374
|
+
end
|
|
375
|
+
anchors[name]
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# the walk for alias targets starts at the first node this
|
|
379
|
+
# visitor was handed (the document's root)
|
|
380
|
+
def root_container(_node)
|
|
381
|
+
@root
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def find_anchored(node, name)
|
|
385
|
+
return node if node.anchor == name
|
|
386
|
+
|
|
387
|
+
node.children.each do |c|
|
|
388
|
+
found = find_anchored(c, name)
|
|
389
|
+
return found if found
|
|
390
|
+
end
|
|
391
|
+
nil
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def visit_scalar(node)
|
|
395
|
+
node.to_ruby
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def visit_sequence(node)
|
|
399
|
+
if node.tag == "!ruby/set"
|
|
400
|
+
set = ::Set.new
|
|
401
|
+
anchors[node.anchor] = set if node.anchor
|
|
402
|
+
node.children.each { |c| set << visit(c) }
|
|
403
|
+
return set
|
|
404
|
+
end
|
|
405
|
+
ary = []
|
|
406
|
+
anchors[node.anchor] = ary if node.anchor
|
|
407
|
+
node.children.each { |c| ary << visit(c) }
|
|
408
|
+
ary
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def visit_mapping(node)
|
|
412
|
+
tag = node.tag
|
|
413
|
+
return hash_into({}, node) if tag.nil? || tag.empty?
|
|
414
|
+
|
|
415
|
+
case tag
|
|
416
|
+
when "!ruby/set"
|
|
417
|
+
set = Set.new
|
|
418
|
+
anchors[node.anchor] = set if node.anchor
|
|
419
|
+
node.children.each_slice(2) { |_k, v| set << visit(v) }
|
|
420
|
+
set
|
|
421
|
+
when /\A!ruby\/struct(?::(.+))?\z/
|
|
422
|
+
klass = resolve_class(Regexp.last_match(1).to_s)
|
|
423
|
+
pairs = {}
|
|
424
|
+
anchors[node.anchor] = nil # placeholder: filled below
|
|
425
|
+
node.children.each_slice(2) do |k, v|
|
|
426
|
+
pairs[visit(k).to_s.sub(/\A:/, "")] = visit(v)
|
|
427
|
+
end
|
|
428
|
+
keys = pairs.keys.map(&:to_sym)
|
|
429
|
+
built =
|
|
430
|
+
if klass && klass <= Struct
|
|
431
|
+
klass.new(*pairs.values)
|
|
432
|
+
else
|
|
433
|
+
Struct.new(*keys).new(*pairs.values)
|
|
434
|
+
end
|
|
435
|
+
anchors[node.anchor] = built if node.anchor
|
|
436
|
+
built
|
|
437
|
+
when /\A!ruby\/object(?::(.+))?\z/
|
|
438
|
+
klass = resolve_class(Regexp.last_match(1).to_s)
|
|
439
|
+
revive_object(klass, node)
|
|
440
|
+
else
|
|
441
|
+
# Psych.load_tags first (the class registry, #95 bug 4);
|
|
442
|
+
# anything else stays the plain mapping
|
|
443
|
+
if (klass = ::Yeptris::Psych.load_tags[tag])
|
|
444
|
+
revive_tagged(klass, node)
|
|
445
|
+
else
|
|
446
|
+
hash_into({}, node)
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# stdlib visit_hash's merge branch, verbatim in structure: the
|
|
452
|
+
# key's SHAPE decides (an explicit !!str '<<' is a literal key,
|
|
453
|
+
# not a merge), the VALUE's node kind picks the arm, and every
|
|
454
|
+
# merge is TypeError-guarded (a bad element keeps the whole
|
|
455
|
+
# '<<' pair literal — merges are all-or-nothing).
|
|
456
|
+
def hash_into(h, node)
|
|
457
|
+
anchors[node.anchor] = h if node.anchor
|
|
458
|
+
node.children.each_slice(2) do |k, v|
|
|
459
|
+
key = visit(k)
|
|
460
|
+
val = visit(v)
|
|
461
|
+
if key == "<<" && k.tag != "tag:yaml.org,2002:str"
|
|
462
|
+
case v
|
|
463
|
+
when ::Yeptris::Psych::Nodes::Alias, ::Yeptris::Psych::Nodes::Mapping
|
|
464
|
+
begin
|
|
465
|
+
h.merge!(val)
|
|
466
|
+
rescue ::TypeError
|
|
467
|
+
h[key] = val
|
|
468
|
+
end
|
|
469
|
+
when ::Yeptris::Psych::Nodes::Sequence
|
|
470
|
+
begin
|
|
471
|
+
merged = {}
|
|
472
|
+
val.reverse_each { |value| merged.merge!(value) }
|
|
473
|
+
h.merge!(merged)
|
|
474
|
+
rescue ::TypeError
|
|
475
|
+
h[key] = val
|
|
476
|
+
end
|
|
477
|
+
else
|
|
478
|
+
h[key] = val
|
|
479
|
+
end
|
|
480
|
+
else
|
|
481
|
+
h[key] = val
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
h
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# A load_tags-registered class revives from the mapping's own
|
|
488
|
+
# top-level pairs (Psych's coder semantics — no __init__
|
|
489
|
+
# wrapper, which is OUR encoder's internal convention)
|
|
490
|
+
def revive_tagged(klass, node)
|
|
491
|
+
raise ::Yeptris::DumpError,
|
|
492
|
+
"#{klass} is not Yeptris::Psych::Encodable: implement init_with(coder)" unless klass <= ::Yeptris::Psych::Encodable
|
|
493
|
+
|
|
494
|
+
obj = klass.allocate
|
|
495
|
+
anchors[node.anchor] = obj if node.anchor
|
|
496
|
+
coder = ::Yeptris::Psych::CoderShim.new
|
|
497
|
+
node.children.each_slice(2) do |k, v|
|
|
498
|
+
coder[k.to_ruby.to_s] = visit(v)
|
|
499
|
+
end
|
|
500
|
+
obj.init_with(coder)
|
|
501
|
+
obj
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def revive_object(klass, node)
|
|
505
|
+
# bare !ruby/object (no class): Psych.dump(Object.new)'s form.
|
|
506
|
+
# An Object has no declared state — allocation is the whole
|
|
507
|
+
# revival (no reflection, no protocol needed).
|
|
508
|
+
return ::Object.allocate if klass.nil?
|
|
509
|
+
|
|
510
|
+
raise ::Yeptris::DumpError, "#{klass} is not Yeptris::Psych::Encodable: implement init_with(coder)" unless klass <= ::Yeptris::Psych::Encodable
|
|
511
|
+
|
|
512
|
+
obj = klass.allocate
|
|
513
|
+
anchors[node.anchor] = obj if node.anchor
|
|
514
|
+
# The class opts in via init_with(coder); the encoder wrote
|
|
515
|
+
# `coder["__init__"] = the mapping` so we can pass it back
|
|
516
|
+
# without library-side ivar reflection.
|
|
517
|
+
node.children.each_slice(2) do |k, v|
|
|
518
|
+
key = k.to_ruby.to_s
|
|
519
|
+
next unless key == "__init__"
|
|
520
|
+
|
|
521
|
+
coder = ::Yeptris::Psych::CoderShim.new
|
|
522
|
+
if v.is_a?(::Yeptris::Psych::Nodes::Mapping)
|
|
523
|
+
v.children.each_slice(2) do |ck, cv|
|
|
524
|
+
coder[ck.to_ruby.to_s] = visit(cv)
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
obj.init_with(coder)
|
|
528
|
+
end
|
|
529
|
+
obj
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def resolve_class(name)
|
|
533
|
+
return nil if name.nil? || name.empty?
|
|
534
|
+
|
|
535
|
+
klass = ::Object.const_get(name)
|
|
536
|
+
raise DisallowedClass, name unless klass.is_a?(Class) || klass.is_a?(Module)
|
|
537
|
+
|
|
538
|
+
klass
|
|
539
|
+
rescue ::NameError
|
|
540
|
+
nil
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
end
|