psych-pure 0.1.4 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1767eb97091e21f83527221b9ef4f156d9db40998b9008422b3dc017ff4ada6
4
- data.tar.gz: b7e1d3b11886a31fda7fd7aed9937e2c26eaafa6d5e242a65bb89c8a02b00be7
3
+ metadata.gz: 33f682a9e0bba7ec024cec2e1ca54aa791aa550e066ac708d7b9cf4e765e6d5a
4
+ data.tar.gz: 5a0b04e41b5700c481658a9604be9f942f41757231f339e8382d53f2a383b431
5
5
  SHA512:
6
- metadata.gz: 7103d8ceae77bf15b2f21f9462f65fe495bf6e90206749fded264d192beb1caee6ae1a0585703ce914d7268a1e57f498f4cc1b824c21b3c5fe95edfb1dc1e320
7
- data.tar.gz: 570ba899c74d5873ba243771ae3ff71605b59f81b1567f5f0712314aa2f8efbbdfab659c4e9bde63609ce2ef5ccce46352624c0f458ef6a8ddd31826edcfa9da
6
+ metadata.gz: 164cb1c175e35e791e0206d14cfef2233817e90d2a6ebabb8f5b88ba59cad9c6fd946d651a13c7aab2437fa33e2e1a23a8d775597958e8d06d42ea21bcd6a1f5
7
+ data.tar.gz: dcab2936344a40ddd58495e6a699aebc77c0d5900c53f0692009b5e4b7383e09ae4896c9c76fa2d6b67599da72a313772613ec9440f659558a51d6598f9e4aca
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] - 2025-12-25
10
+
11
+ - Support Psych >= 5.3.0 by adding the `parse_symbols` option.
12
+
13
+ ## [0.2.0] - 2025-11-11
14
+
15
+ - Add `sequence_indent` option to `Psych::Pure.dump` to control whether sequence elements contained within mapping elements are indented.
16
+ - Properly handle mutation methods on loaded objects that mutate in place.
17
+
9
18
  ## [0.1.4] - 2025-11-10
10
19
 
11
20
  - Fix up comment handling preceding sequence elements.
@@ -37,7 +46,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
37
46
 
38
47
  - 🎉 Initial release. 🎉
39
48
 
40
- [unreleased]: https://github.com/kddnewton/psych-pure/compare/v0.1.3...HEAD
49
+ [unreleased]: https://github.com/kddnewton/psych-pure/compare/v0.3.0...HEAD
50
+ [0.3.0]: https://github.com/kddnewton/psych-pure/compare/v0.2.0...v0.3.0
51
+ [0.2.0]: https://github.com/kddnewton/psych-pure/compare/v0.1.4...v0.2.0
52
+ [0.1.4]: https://github.com/kddnewton/psych-pure/compare/v0.1.3...v0.1.4
41
53
  [0.1.3]: https://github.com/kddnewton/psych-pure/compare/v0.1.2...v0.1.3
42
54
  [0.1.2]: https://github.com/kddnewton/psych-pure/compare/v0.1.1...v0.1.2
43
55
  [0.1.1]: https://github.com/kddnewton/psych-pure/compare/v0.1.0...v0.1.1
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Psych
4
4
  module Pure
5
- VERSION = "0.1.4"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
data/lib/psych/pure.rb CHANGED
@@ -15,6 +15,17 @@ module Psych
15
15
  end
16
16
  end
17
17
 
18
+ # If Psych is older than 5.3, we need to modify the ScalarScanner to add the
19
+ # parse_symbols parameter so that we can use it consistently when we
20
+ # initialize it.
21
+ if Psych::VERSION < "5.3"
22
+ ScalarScanner.prepend(Module.new {
23
+ def initialize(class_loader, strict_integer: false, parse_symbols: true)
24
+ super(class_loader, strict_integer: strict_integer)
25
+ end
26
+ })
27
+ end
28
+
18
29
  # A YAML parser written in Ruby.
19
30
  module Pure
20
31
  # An internal exception is an exception that should not have occurred. It is
@@ -74,6 +85,10 @@ module Psych
74
85
  def column(offset)
75
86
  offset - @line_offsets[line(offset)]
76
87
  end
88
+
89
+ def point(offset)
90
+ "line #{line(offset) + 1} column #{column(offset)}"
91
+ end
77
92
  end
78
93
 
79
94
  # A location represents a range of bytes in the input string.
@@ -201,15 +216,37 @@ module Psych
201
216
  # rely on the source formatting, and need to format it ourselves.
202
217
  attr_reader :dirty
203
218
 
204
- def initialize(object, psych_node, dirty = false)
219
+ def initialize(object, psych_node)
205
220
  super(object)
206
221
  @psych_node = psych_node
207
- @dirty = dirty
222
+ @dirty = false
208
223
  end
209
224
 
210
- def replace(psych_node)
211
- @psych_node = psych_node
212
- @dirty = true
225
+ def initialize_clone(obj, freeze: nil)
226
+ super
227
+ @psych_node = obj.psych_node.dup
228
+ end
229
+
230
+ def initialize_dup(obj)
231
+ super
232
+ @psych_node = obj.psych_node.dup
233
+ end
234
+
235
+ # Effectively implement the same method_missing as SimpleDelegator, but
236
+ # additionally track whether or not the object has been mutated.
237
+ ruby2_keywords def method_missing(name, *args, &block)
238
+ takes_block = false
239
+ target = self.__getobj__ { takes_block = true }
240
+
241
+ if !takes_block && target_respond_to?(target, name, false)
242
+ previous = target.dup
243
+ result = target.__send__(name, *args, &block)
244
+
245
+ @dirty = true unless previous.eql?(target)
246
+ result
247
+ else
248
+ super(name, *args, &block)
249
+ end
213
250
  end
214
251
  end
215
252
 
@@ -623,8 +660,8 @@ module Psych
623
660
  defined?(@comments)
624
661
  end
625
662
 
626
- def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, comments: false)
627
- Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, comments: comments).accept(self)
663
+ def to_ruby(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true, comments: false)
664
+ Visitors::ToRuby.create(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols, comments: comments).accept(self)
628
665
  end
629
666
  end
630
667
 
@@ -710,9 +747,9 @@ module Psych
710
747
 
711
748
  # Extend the ToRuby singleton to be able to pass the comments option.
712
749
  module ToRubySingleton
713
- def create(symbolize_names: false, freeze: false, strict_integer: false, comments: false)
750
+ def create(symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true, comments: false)
714
751
  class_loader = ClassLoader.new
715
- scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer)
752
+ scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols)
716
753
  new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze, comments: comments)
717
754
  end
718
755
  end
@@ -909,6 +946,123 @@ module Psych
909
946
  # The parser is responsible for taking a YAML string and converting it into
910
947
  # a series of events that can be used by the consumer.
911
948
  class Parser
949
+ # A stack of contexts that the parser is currently within. We use this to
950
+ # decorate error messages with the context in which they occurred.
951
+ class Context
952
+ class BlockMapping
953
+ attr_reader :pos, :indent
954
+
955
+ def initialize(pos, indent)
956
+ @pos = pos
957
+ @indent = indent
958
+ end
959
+
960
+ def format(source)
961
+ "block mapping at #{source.point(pos)}#{indent == -1 ? "" : " (indent=#{indent})"}"
962
+ end
963
+ end
964
+
965
+ class BlockSequence
966
+ attr_reader :pos, :indent
967
+
968
+ def initialize(pos, indent)
969
+ @pos = pos
970
+ @indent = indent
971
+ end
972
+
973
+ def format(source)
974
+ "block sequence at #{source.point(pos)}#{indent == -1 ? "" : " (indent=#{indent})"}"
975
+ end
976
+ end
977
+
978
+ class DoubleQuotedScalar
979
+ attr_reader :pos
980
+
981
+ def initialize(pos)
982
+ @pos = pos
983
+ end
984
+
985
+ def format(source)
986
+ "double quoted scalar at #{source.point(pos)}"
987
+ end
988
+ end
989
+
990
+ class FlowMapping
991
+ attr_reader :pos, :context
992
+
993
+ def initialize(pos, context)
994
+ @pos = pos
995
+ @context = context
996
+ end
997
+
998
+ def format(source)
999
+ "flow mapping at #{source.point(pos)} (context=#{context})"
1000
+ end
1001
+ end
1002
+
1003
+ class FlowSequence
1004
+ attr_reader :pos, :context
1005
+
1006
+ def initialize(pos, context)
1007
+ @pos = pos
1008
+ @context = context
1009
+ end
1010
+
1011
+ def format(source)
1012
+ "flow sequence at #{source.point(pos)} (context=#{context})"
1013
+ end
1014
+ end
1015
+
1016
+ private_constant :BlockMapping, :BlockSequence, :DoubleQuotedScalar, :FlowMapping, :FlowSequence
1017
+
1018
+ def initialize
1019
+ @contexts = []
1020
+ @deepest = []
1021
+ end
1022
+
1023
+ def syntax_error(source, filename, pos, message)
1024
+ unless (stack = (@contexts.empty? ? @deepest : @contexts)).empty?
1025
+ pos = stack.last.pos
1026
+ message = "#{message}\nwithin:\n#{stack.map { |element| " #{element.format(source)}\n" }.join}"
1027
+ end
1028
+
1029
+ SyntaxError.new(filename, source.line(pos), source.column(pos), pos, message, nil)
1030
+ end
1031
+
1032
+ def within_block_mapping(pos, indent, &blk)
1033
+ within(BlockMapping.new(pos, indent), &blk)
1034
+ end
1035
+
1036
+ def within_block_sequence(pos, indent, &blk)
1037
+ within(BlockSequence.new(pos, indent), &blk)
1038
+ end
1039
+
1040
+ def within_double_quoted_scalar(pos, &blk)
1041
+ within(DoubleQuotedScalar.new(pos), &blk)
1042
+ end
1043
+
1044
+ def within_flow_mapping(pos, context, &blk)
1045
+ within(FlowMapping.new(pos, context), &blk)
1046
+ end
1047
+
1048
+ def within_flow_sequence(pos, context, &blk)
1049
+ within(FlowSequence.new(pos, context), &blk)
1050
+ end
1051
+
1052
+ private
1053
+
1054
+ def within(object)
1055
+ @contexts.push(object)
1056
+ @deepest = @contexts.dup if @contexts.length > @deepest.length
1057
+
1058
+ begin
1059
+ yield
1060
+ ensure
1061
+ @contexts.pop
1062
+ end
1063
+ end
1064
+ end
1065
+
912
1066
  # Initialize a new parser with the given source string.
913
1067
  def initialize(handler)
914
1068
  # These are used to track the current state of the parser.
@@ -952,6 +1106,11 @@ module Psych
952
1106
  # This parser can optionally parse comments and attach them to the
953
1107
  # resulting tree, if the option is passed.
954
1108
  @comments = nil
1109
+
1110
+ # The context of the parser at any given time, which is used to decorate
1111
+ # error messages to make it easier to find the specific location where
1112
+ # they occurred.
1113
+ @context = Context.new
955
1114
  end
956
1115
 
957
1116
  # Top-level parse function that starts the parsing process.
@@ -985,9 +1144,7 @@ module Psych
985
1144
 
986
1145
  # Raise a syntax error with the given message.
987
1146
  def raise_syntax_error(message)
988
- line = @source.line(@scanner.pos)
989
- column = @source.column(@scanner.pos)
990
- raise SyntaxError.new(@filename, line, column, @scanner.pos, message, nil)
1147
+ raise @context.syntax_error(@source, @filename, @scanner.pos, message)
991
1148
  end
992
1149
 
993
1150
  # ------------------------------------------------------------------------
@@ -1856,30 +2013,32 @@ module Psych
1856
2013
  def parse_c_double_quoted(n, c)
1857
2014
  pos_start = @scanner.pos
1858
2015
 
1859
- if try { match("\"") && parse_nb_double_text(n, c) && match("\"") }
1860
- end1 = "(?:\\\\\\r?\\n[ \\t]*)"
1861
- end2 = "(?:[ \\t]*\\r?\\n[ \\t]*)"
1862
- hex = "[0-9a-fA-F]"
1863
- hex2 = "(?:\\\\x(#{hex}{2}))"
1864
- hex4 = "(?:\\\\u(#{hex}{4}))"
1865
- hex8 = "(?:\\\\U(#{hex}{8}))"
1866
-
1867
- value = from(pos_start).byteslice(1...-1)
1868
- value.gsub!(%r{(?:\r\n|#{end1}|#{end2}+|#{hex2}|#{hex4}|#{hex8}|\\[\\ "/_0abefnrt\tvLNP])}) do |m|
1869
- case m
1870
- when /\A(?:#{hex2}|#{hex4}|#{hex8})\z/o
1871
- m[2..].to_i(16).chr(Encoding::UTF_8)
1872
- when /\A#{end1}\z/o
1873
- ""
1874
- when /\A#{end2}+\z/o
1875
- m.sub(/#{end2}/, "").gsub(/#{end2}/, "\n").then { |r| r.empty? ? " " : r }
1876
- else
1877
- C_DOUBLE_QUOTED_UNESCAPES.fetch(m, m)
2016
+ @context.within_double_quoted_scalar(@scanner.pos) do
2017
+ if try { match("\"") && parse_nb_double_text(n, c) && match("\"") }
2018
+ end1 = "(?:\\\\\\r?\\n[ \\t]*)"
2019
+ end2 = "(?:[ \\t]*\\r?\\n[ \\t]*)"
2020
+ hex = "[0-9a-fA-F]"
2021
+ hex2 = "(?:\\\\x(#{hex}{2}))"
2022
+ hex4 = "(?:\\\\u(#{hex}{4}))"
2023
+ hex8 = "(?:\\\\U(#{hex}{8}))"
2024
+
2025
+ value = from(pos_start).byteslice(1...-1)
2026
+ value.gsub!(%r{(?:\r\n|#{end1}|#{end2}+|#{hex2}|#{hex4}|#{hex8}|\\[\\ "/_0abefnrt\tvLNP])}) do |m|
2027
+ case m
2028
+ when /\A(?:#{hex2}|#{hex4}|#{hex8})\z/o
2029
+ m[2..].to_i(16).chr(Encoding::UTF_8)
2030
+ when /\A#{end1}\z/o
2031
+ ""
2032
+ when /\A#{end2}+\z/o
2033
+ m.sub(/#{end2}/, "").gsub(/#{end2}/, "\n").then { |r| r.empty? ? " " : r }
2034
+ else
2035
+ C_DOUBLE_QUOTED_UNESCAPES.fetch(m, m)
2036
+ end
1878
2037
  end
1879
- end
1880
2038
 
1881
- events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), from(pos_start), value, Nodes::Scalar::DOUBLE_QUOTED))
1882
- true
2039
+ events_push_flush_properties(Scalar.new(Location.new(@source, pos_start, @scanner.pos), from(pos_start), value, Nodes::Scalar::DOUBLE_QUOTED))
2040
+ true
2041
+ end
1883
2042
  end
1884
2043
  end
1885
2044
 
@@ -2249,14 +2408,16 @@ module Psych
2249
2408
  def parse_c_flow_sequence(n, c)
2250
2409
  try do
2251
2410
  if match("[")
2252
- events_push_flush_properties(SequenceStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Sequence::FLOW))
2411
+ @context.within_flow_sequence(@scanner.pos, c) do
2412
+ events_push_flush_properties(SequenceStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Sequence::FLOW))
2253
2413
 
2254
- parse_s_separate(n, c)
2255
- parse_ns_s_flow_seq_entries(n, parse_in_flow(c))
2414
+ parse_s_separate(n, c)
2415
+ parse_ns_s_flow_seq_entries(n, parse_in_flow(c))
2256
2416
 
2257
- if match("]")
2258
- events_push_flush_properties(SequenceEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
2259
- true
2417
+ if match("]")
2418
+ events_push_flush_properties(SequenceEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
2419
+ true
2420
+ end
2260
2421
  end
2261
2422
  end
2262
2423
  end
@@ -2297,14 +2458,16 @@ module Psych
2297
2458
  def parse_c_flow_mapping(n, c)
2298
2459
  try do
2299
2460
  if match("{")
2300
- events_push_flush_properties(MappingStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Mapping::FLOW))
2461
+ @context.within_flow_mapping(@scanner.pos, c) do
2462
+ events_push_flush_properties(MappingStart.new(Location.new(@source, @scanner.pos - 1, @scanner.pos), Nodes::Mapping::FLOW))
2301
2463
 
2302
- parse_s_separate(n, c)
2303
- parse_ns_s_flow_map_entries(n, parse_in_flow(c))
2464
+ parse_s_separate(n, c)
2465
+ parse_ns_s_flow_map_entries(n, parse_in_flow(c))
2304
2466
 
2305
- if match("}")
2306
- events_push_flush_properties(MappingEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
2307
- true
2467
+ if match("}")
2468
+ events_push_flush_properties(MappingEnd.new(Location.new(@source, @scanner.pos - 1, @scanner.pos)))
2469
+ true
2470
+ end
2308
2471
  end
2309
2472
  end
2310
2473
  end
@@ -3017,18 +3180,20 @@ module Psych
3017
3180
  def parse_l_block_sequence(n)
3018
3181
  return false if (m = detect_indent(n)) == 0
3019
3182
 
3020
- events_cache_push
3021
- events_push_flush_properties(SequenceStart.new(Location.point(@source, @scanner.pos), Nodes::Sequence::BLOCK))
3183
+ @context.within_block_sequence(@scanner.pos, n) do
3184
+ events_cache_push
3185
+ events_push_flush_properties(SequenceStart.new(Location.point(@source, @scanner.pos), Nodes::Sequence::BLOCK))
3022
3186
 
3023
- if try { plus { try { parse_s_indent(n + m) && parse_c_l_block_seq_entry(n + m) } } }
3024
- events_cache_flush
3025
- events_push_flush_properties(SequenceEnd.new(Location.point(@source, @scanner.pos)))
3026
- true
3027
- else
3028
- event = events_cache_pop[0]
3029
- @anchor = event.anchor
3030
- @tag = event.tag
3031
- false
3187
+ if try { plus { try { parse_s_indent(n + m) && parse_c_l_block_seq_entry(n + m) } } }
3188
+ events_cache_flush
3189
+ events_push_flush_properties(SequenceEnd.new(Location.point(@source, @scanner.pos)))
3190
+ true
3191
+ else
3192
+ event = events_cache_pop[0]
3193
+ @anchor = event.anchor
3194
+ @tag = event.tag
3195
+ false
3196
+ end
3032
3197
  end
3033
3198
  end
3034
3199
 
@@ -3093,16 +3258,18 @@ module Psych
3093
3258
  def parse_l_block_mapping(n)
3094
3259
  return false if (m = detect_indent(n)) == 0
3095
3260
 
3096
- events_cache_push
3097
- events_push_flush_properties(MappingStart.new(Location.point(@source, @scanner.pos), Nodes::Mapping::BLOCK))
3261
+ @context.within_block_mapping(@scanner.pos, n) do
3262
+ events_cache_push
3263
+ events_push_flush_properties(MappingStart.new(Location.point(@source, @scanner.pos), Nodes::Mapping::BLOCK))
3098
3264
 
3099
- if try { plus { try { parse_s_indent(n + m) && parse_ns_l_block_map_entry(n + m) } } }
3100
- events_cache_flush
3101
- events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
3102
- true
3103
- else
3104
- events_cache_pop
3105
- false
3265
+ if try { plus { try { parse_s_indent(n + m) && parse_ns_l_block_map_entry(n + m) } } }
3266
+ events_cache_flush
3267
+ events_push_flush_properties(MappingEnd.new(Location.point(@source, @scanner.pos)))
3268
+ true
3269
+ else
3270
+ events_cache_pop
3271
+ false
3272
+ end
3106
3273
  end
3107
3274
  end
3108
3275
 
@@ -3559,8 +3726,9 @@ module Psych
3559
3726
  # The visitor is responsible for walking the tree and generating the YAML
3560
3727
  # output.
3561
3728
  class Visitor
3562
- def initialize(q)
3729
+ def initialize(q, sequence_indent: false)
3563
3730
  @q = q
3731
+ @sequence_indent = sequence_indent
3564
3732
  end
3565
3733
 
3566
3734
  # Visit an AliasNode.
@@ -3777,6 +3945,11 @@ module Psych
3777
3945
  elsif inlined || value.anchor || value.tag || value.value.empty?
3778
3946
  @q.text(" ")
3779
3947
  @q.nest(2) { visit(value) }
3948
+ elsif @sequence_indent
3949
+ @q.nest(2) do
3950
+ @q.breakable
3951
+ visit(value)
3952
+ end
3780
3953
  else
3781
3954
  @q.breakable
3782
3955
  visit(value)
@@ -4006,7 +4179,7 @@ module Psych
4006
4179
  q.text(" ")
4007
4180
  end
4008
4181
 
4009
- node.accept(Visitor.new(q))
4182
+ node.accept(Visitor.new(q, sequence_indent: @options.fetch(:sequence_indent, false)))
4010
4183
  q.breakable
4011
4184
  q.current_group.break
4012
4185
  q.flush
@@ -4193,19 +4366,19 @@ module Psych
4193
4366
  end
4194
4367
  end
4195
4368
 
4196
- def self.unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
4369
+ def self.unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true, comments: false)
4197
4370
  result = parse(yaml, filename: filename, comments: comments)
4198
4371
  return fallback unless result
4199
4372
 
4200
- result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, comments: comments)
4373
+ result.to_ruby(symbolize_names: symbolize_names, freeze: freeze, strict_integer: strict_integer, parse_symbols: parse_symbols, comments: comments)
4201
4374
  end
4202
4375
 
4203
- def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
4376
+ def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true, comments: false)
4204
4377
  result = parse(yaml, filename: filename, comments: comments)
4205
4378
  return fallback unless result
4206
4379
 
4207
4380
  class_loader = ClassLoader::Restricted.new(permitted_classes.map(&:to_s), permitted_symbols.map(&:to_s))
4208
- scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer)
4381
+ scanner = ScalarScanner.new(class_loader, strict_integer: strict_integer, parse_symbols: parse_symbols)
4209
4382
  visitor =
4210
4383
  if aliases
4211
4384
  Visitors::ToRuby.new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze, comments: comments)
@@ -4216,7 +4389,7 @@ module Psych
4216
4389
  visitor.accept(result)
4217
4390
  end
4218
4391
 
4219
- def self.load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, comments: false)
4392
+ def self.load(yaml, permitted_classes: [Symbol], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false, strict_integer: false, parse_symbols: true, comments: false)
4220
4393
  safe_load(
4221
4394
  yaml,
4222
4395
  permitted_classes: permitted_classes,
@@ -4227,6 +4400,7 @@ module Psych
4227
4400
  symbolize_names: symbolize_names,
4228
4401
  freeze: freeze,
4229
4402
  strict_integer: strict_integer,
4403
+ parse_symbols: parse_symbols,
4230
4404
  comments: comments
4231
4405
  )
4232
4406
  end
@@ -4245,6 +4419,21 @@ module Psych
4245
4419
  result
4246
4420
  end
4247
4421
 
4422
+ def self.safe_load_stream(yaml, filename: nil, permitted_classes: [], aliases: false, comments: false)
4423
+ documents = parse_stream(yaml, filename: filename).children.map do |child|
4424
+ stream = Psych::Nodes::Stream.new
4425
+ stream.children << child
4426
+ safe_load(stream.to_yaml, permitted_classes: permitted_classes, aliases: aliases, comments: comments)
4427
+ end
4428
+
4429
+ if block_given?
4430
+ documents.each { |doc| yield doc }
4431
+ nil
4432
+ else
4433
+ documents
4434
+ end
4435
+ end
4436
+
4248
4437
  def self.unsafe_load_file(filename, **kwargs)
4249
4438
  File.open(filename, "r:bom|utf-8") do |f|
4250
4439
  self.unsafe_load(f, filename: filename, **kwargs)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: psych-pure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-11 00:00:00.000000000 Z
11
+ date: 2025-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: psych
@@ -113,7 +113,7 @@ licenses:
113
113
  - MIT
114
114
  metadata:
115
115
  bug_tracker_uri: https://github.com/kddnewton/psych-pure/issues
116
- changelog_uri: https://github.com/kddnewton/psych-pure/blob/v0.1.4/CHANGELOG.md
116
+ changelog_uri: https://github.com/kddnewton/psych-pure/blob/v0.3.0/CHANGELOG.md
117
117
  source_code_uri: https://github.com/kddnewton/psych-pure
118
118
  rubygems_mfa_required: 'true'
119
119
  post_install_message: