class Gem::YAMLSerializer::Parser - Documentation for Ruby 4.1
Description: Documentation for the Gem::YAMLSerializer::Parser class
Keywords: ruby,class,Gem::YAMLSerializer::Parser

🔍

No recent searches

# class Gem::YAMLSerializer::Parser

### Constants

MAPPING_KEY_RE
MAX_NESTING_DEPTH

### Public Class Methods

new (source)
Source

    # File lib/rubygems/yaml_serializer.rb, line 35
    def initialize(source)
      @lines = source.split("\n")
      @anchors = {}
      @depth = 0
      strip_document_prefix
    end

### Public Instance Methods

parse ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 42
    def parse
      return nil if @lines.empty?

      root = nil
      while @lines.any?
        before = @lines.size
        node = parse_node(-1)
        @lines.shift if @lines.size == before && @lines.any?

        if root.is_a?(Mapping) && node.is_a?(Mapping)
          root.pairs.concat(node.pairs)
        elsif root.nil?
          root = node
        end
      end
      root
    end

### Private Instance Methods

apply_tag (node, tag, anchor)
Source

    # File lib/rubygems/yaml_serializer.rb, line 215
    def apply_tag(node, tag, anchor)
      if node.is_a?(Mapping)
        node.tag = tag
        node.anchor = anchor
        node
      else
        Mapping.new(pairs: [[Scalar.new(value: "value"), node]], tag: tag, anchor: anchor)
      end
    end

coerce (val)
Source

    # File lib/rubygems/yaml_serializer.rb, line 269
    def coerce(val)
      val = val.sub(/^! /, "") if val.start_with?("! ")

      if val =~ /^"(.*)"$/
        $1.gsub(/\\["nrt\\]/) do |m|
          case m
          when '\\"' then '"'
          when "\\n" then "\n"
          when "\\r" then "\r"
          when "\\t" then "\t"
          when "\\\\" then "\\"
          end
        end
      elsif val =~ /^'(.*)'$/
        $1.gsub(/''/, "'")
      elsif val == "true"
        true
      elsif val == "false"
        false
      elsif ["~", "null"].include?(val)
        nil
      elsif val == "{}"
        Mapping.new
      elsif val =~ /^\[(.*)\]$/
        inner = $1.strip
        return Sequence.new if inner.empty?
        items = inner.split(/\s*,\s*/).reject(&:empty?).map {|e| Scalar.new(value: coerce(e)) }
        Sequence.new(items: items)
      elsif /\A\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}:\d{2})?/.match?(val)
        begin
          Time.new(val)
        rescue ArgumentError
          # date-only format like "2024-06-15" is not supported by Time.new
          if /\A(\d{4})-(\d{2})-(\d{2})\z/.match(val)
            Time.utc($1.to_i, $2.to_i, $3.to_i)
          else
            val
          end
        end
      elsif /^-?\d+$/.match?(val)
        val.to_i
      else
        val
      end
    end

consume_anchor ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 344
    def consume_anchor
      line = @lines[0]
      stripped = line.lstrip
      return nil unless stripped.start_with?("&") && stripped =~ /^&(\S+)\s+/

      anchor = $1
      @lines[0] = line.sub(/&#{Regexp.escape(anchor)}\s+/, "")
      anchor
    end

consume_value_anchor (val)
Source

    # File lib/rubygems/yaml_serializer.rb, line 361
    def consume_value_anchor(val)
      return [nil, val] unless val =~ /^&(\S+)\s+/

      anchor = $1
      [anchor, val.sub(/^&#{Regexp.escape(anchor)}\s+/, "")]
    end

current_indent ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 339
    def current_indent
      line = @lines[0]
      line.size - line.lstrip.size
    end

decode_binary_tag (str)
Source

    # File lib/rubygems/yaml_serializer.rb, line 315
    def decode_binary_tag(str)
      content = str.sub(/\A!binary\s+/, "")
      content = $1 if content =~ /\A"(.*)"\z/ || content =~ /\A'(.*)'\z/
      content.unpack1("m")
    end

extract_item_anchor (content)
Source

    # File lib/rubygems/yaml_serializer.rb, line 354
    def extract_item_anchor(content)
      return [nil, content] unless content =~ /^&(\S+)/

      anchor = $1
      [anchor, content.sub(/^&#{Regexp.escape(anchor)}\s*/, "")]
    end

parse_alias_ref ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 331
    def parse_alias_ref
      AliasRef.new(name: @lines.shift.lstrip[1..].strip)
    end

parse_binary_value (val, indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 321
    def parse_binary_value(val, indent)
      rest = val.sub(/\A!binary\s+/, "")
      if rest.start_with?("|")
        content = parse_block_scalar(indent, rest[1..].to_s.strip)
        Scalar.new(value: content.unpack1("m"))
      else
        Scalar.new(value: decode_binary_tag(val))
      end
    end

parse_block_scalar (base_indent, modifier)
Source

    # File lib/rubygems/yaml_serializer.rb, line 225
    def parse_block_scalar(base_indent, modifier)
      parts = []
      block_indent = nil

      while @lines.any?
        line = @lines[0]
        if line.strip.empty?
          parts << "\n"
          @lines.shift
        else
          line_indent = line.size - line.lstrip.size
          break if line_indent <= base_indent
          block_indent ||= line_indent
          parts << @lines.shift[block_indent..].to_s << "\n"
        end
      end

      res = parts.join
      res.chomp! if modifier == "-" && res.end_with?("\n")
      res
    end

parse_inline_alias (content)
Source

    # File lib/rubygems/yaml_serializer.rb, line 335
    def parse_inline_alias(content)
      AliasRef.new(name: content[1..].strip)
    end

parse_inline_scalar (val, indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 258
    def parse_inline_scalar(val, indent)
      result = coerce(val)
      return result if result.is_a?(Mapping) || result.is_a?(Sequence)

      while result.is_a?(String) && @lines.any? &&
            !@lines[0].strip.empty? && current_indent > indent
        result << " " << @lines.shift.strip
      end
      Scalar.new(value: result)
    end

parse_mapping (indent, anchor)
Source

    # File lib/rubygems/yaml_serializer.rb, line 150
    def parse_mapping(indent, anchor)
      pairs = []
      while @lines.any?
        line = @lines[0]
        stripped = line.lstrip
        break unless line.size - stripped.size == indent &&
                     stripped =~ MAPPING_KEY_RE && !stripped.start_with?("!ruby/object:")
        key = $1.strip
        @lines.shift
        val = strip_comment($2.to_s.strip)

        key = decode_binary_tag(key) if key.start_with?("!binary ")

        val_anchor, val = consume_value_anchor(val)
        value = parse_mapping_value(val, indent)
        value = register_anchor(val_anchor, value) if val_anchor

        pairs << [Scalar.new(value: key), value]
      end
      register_anchor(anchor, Mapping.new(pairs: pairs))
    end

parse_mapping_value (val, indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 172
    def parse_mapping_value(val, indent)
      if val.start_with?("*")
        parse_inline_alias(val)
      elsif val.start_with?("!ruby/object:")
        parse_tagged_content(val.strip, indent)
      elsif val.start_with?("!binary ")
        parse_binary_value(val, indent)
      elsif val.empty?
        next_stripped = nil
        next_indent = nil
        if @lines.any?
          next_stripped = @lines[0].lstrip
          next_indent = @lines[0].size - next_stripped.size
        end
        if next_stripped &&
           (next_stripped.start_with?("- ") || next_stripped == "-") &&
           next_indent == indent
          parse_node(indent)
        else
          parse_node(indent + 1)
        end
      elsif val == "[]"
        Sequence.new
      elsif val == "{}"
        Mapping.new
      elsif val.start_with?("|")
        Scalar.new(value: parse_block_scalar(indent, val[1..].to_s.strip))
      else
        parse_inline_scalar(val, indent)
      end
    end

parse_node (base_indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 73
    def parse_node(base_indent)
      @depth += 1
      if @depth > MAX_NESTING_DEPTH
        raise Psych::SyntaxError, "exceeded maximum nesting depth (#{MAX_NESTING_DEPTH})"
      end

      skip_blank_and_comments
      return nil if @lines.empty?

      line = @lines[0]
      stripped = line.lstrip
      indent = line.size - stripped.size
      return nil if indent < base_indent

      return parse_alias_ref if stripped.start_with?("*")

      anchor = consume_anchor

      if anchor
        line = @lines[0]
        stripped = line.lstrip
      end

      if stripped.start_with?("- ") || stripped == "-"
        parse_sequence(indent, anchor)
      elsif stripped =~ MAPPING_KEY_RE && !stripped.start_with?("!ruby/object:")
        parse_mapping(indent, anchor)
      elsif stripped.start_with?("!ruby/object:")
        parse_tagged_node(indent, anchor)
      elsif stripped.start_with?("|")
        modifier = stripped[1..].to_s.strip
        @lines.shift
        register_anchor(anchor, Scalar.new(value: parse_block_scalar(indent, modifier)))
      else
        parse_plain_scalar(indent, anchor)
      end
    ensure
      @depth -= 1
    end

parse_plain_scalar (indent, anchor)
Source

    # File lib/rubygems/yaml_serializer.rb, line 247
    def parse_plain_scalar(indent, anchor)
      result = coerce(@lines.shift.strip)
      return register_anchor(anchor, result) if result.is_a?(Mapping) || result.is_a?(Sequence)

      while result.is_a?(String) && @lines.any? &&
            !@lines[0].strip.empty? && current_indent > indent
        result << " " << @lines.shift.strip
      end
      register_anchor(anchor, Scalar.new(value: result))
    end

parse_sequence (indent, anchor)
Source

    # File lib/rubygems/yaml_serializer.rb, line 113
    def parse_sequence(indent, anchor)
      items = []
      while @lines.any?
        line = @lines[0]
        stripped = line.lstrip
        break unless line.size - stripped.size == indent &&
                     (stripped.start_with?("- ") || stripped == "-")
        content = @lines.shift.lstrip[1..].strip
        item_anchor, content = extract_item_anchor(content)
        item = parse_sequence_item(content, indent)
        items << register_anchor(item_anchor, item)
      end
      register_anchor(anchor, Sequence.new(items: items))
    end

parse_sequence_item (content, indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 128
    def parse_sequence_item(content, indent)
      if content.start_with?("*")
        parse_inline_alias(content)
      elsif content.empty?
        @lines.any? && current_indent > indent ? parse_node(indent) : nil
      elsif content.start_with?("!ruby/object:")
        parse_tagged_content(content.strip, indent)
      elsif content.start_with?("!binary ")
        parse_binary_value(content, indent)
      elsif content.start_with?("-")
        @lines.unshift("#{" " * (indent + 2)}#{content}")
        parse_node(indent)
      elsif content =~ MAPPING_KEY_RE && !content.start_with?("!ruby/object:")
        @lines.unshift("#{" " * (indent + 2)}#{content}")
        parse_node(indent)
      elsif content.start_with?("|")
        Scalar.new(value: parse_block_scalar(indent, content[1..].to_s.strip))
      else
        parse_inline_scalar(content, indent)
      end
    end

parse_tagged_content (tag, indent)
Source

    # File lib/rubygems/yaml_serializer.rb, line 210
    def parse_tagged_content(tag, indent)
      nested = parse_node(indent)
      apply_tag(nested, tag, nil)
    end

parse_tagged_node (indent, anchor)
Source

    # File lib/rubygems/yaml_serializer.rb, line 204
    def parse_tagged_node(indent, anchor)
      tag = @lines.shift.strip
      nested = parse_node(indent)
      apply_tag(nested, tag, anchor)
    end

register_anchor (name, node)
Source

    # File lib/rubygems/yaml_serializer.rb, line 368
    def register_anchor(name, node)
      if name
        @anchors[name] = node
        node.anchor = name if node.respond_to?(:anchor=)
      end
      node
    end

skip_blank_and_comments ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 376
    def skip_blank_and_comments
      while @lines.any?
        line = @lines[0]
        stripped = line.lstrip
        break unless stripped.empty? || stripped.start_with?("#")
        @lines.shift
      end
    end

strip_comment (val)
Source

    # File lib/rubygems/yaml_serializer.rb, line 385
    def strip_comment(val)
      return val unless val.include?("#")
      return val if val.lstrip.start_with?("#")

      in_single = false
      in_double = false
      escape = false

      val.each_char.with_index do |ch, i|
        if escape
          escape = false
          next
        end

        if in_single
          in_single = false if ch == "'"
        elsif in_double
          if ch == "\\"
            escape = true
          elsif ch == '"'
            in_double = false
          end
        else
          case ch
          when "'" then in_single = true
          when '"' then in_double = true
          when "#" then return val[0...i].rstrip
          end
        end
      end

      val
    end

strip_document_prefix ()
Source

    # File lib/rubygems/yaml_serializer.rb, line 62
    def strip_document_prefix
      return if @lines.empty?
      return unless @lines[0]&.start_with?("---")

      if @lines[0].strip == "---"
        @lines.shift
      else
        @lines[0] = @lines[0].sub(/^---\s*/, "")
      end
    end