123456789_123456789_123456789_123456789_123456789_

Class: Gem::YAMLSerializer::Builder

Relationships & Source Files
Inherits: Object
Defined in: lib/rubygems/yaml_serializer.rb

Constant Summary

Class Method Summary

Instance Method Summary

Constructor Details

.new(permitted_classes: [], permitted_symbols: [], aliases: true) ⇒ Builder

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 385

def initialize(permitted_classes: [], permitted_symbols: [], aliases: true)
  @permitted_tags = Array(permitted_classes).map do |c|
    "!ruby/object:#{c.is_a?(Module) ? c.name : c}"
  end
  @permitted_symbols = permitted_symbols
  @aliases = aliases
  @anchor_values = {}
end

Instance Method Details

#build(node)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 394

def build(node)
  return {} if node.nil?

  result = build_node(node)

  if result.is_a?(Hash) &&
     (result[:tag] == "!ruby/object:Gem::Specification" ||
      result["tag"] == "!ruby/object:Gem::Specification")
    build_specification(result)
  else
    result
  end
end

#build_dependency(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 514

def build_dependency(node)
  hash = pairs_to_hash(node)
  d = Gem::Dependency.allocate
  d.instance_variable_set(:@name, hash["name"])

  requirement = build_safe_requirement(hash["requirement"])
  d.instance_variable_set(:@requirement, requirement)

  type = hash["type"]
  type = type ? type.to_s.sub(/^:/, "").to_sym : :runtime
  validate_symbol!(type)
  d.instance_variable_set(:@type, type)

  d.instance_variable_set(:@prerelease, ["true", true].include?(hash["prerelease"]))
  d.instance_variable_set(:@version_requirements, d.instance_variable_get(:@requirement))
  d
end

#build_hash(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 455

def build_hash(node)
  result = {}
  node.pairs.each do |key_node, value_node|
    key = key_node.is_a?(Scalar) ? key_node.value.to_s : build_node(key_node).to_s
    value = build_node(value_node)

    if ARRAY_FIELDS.include?(key)
      value = normalize_array_field(value)
    end

    result[key] = value
  end
  result
end

#build_mapping(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 431

def build_mapping(node)
  validate_tag!(node.tag) if node.tag
  check_anchor!(node)

  result = case node.tag
           when "!ruby/object:Gem::Version"
             build_version(node)
           when "!ruby/object:Gem::Platform"
             build_platform(node)
           when "!ruby/object:Gem::Requirement", "!ruby/object:Gem::Version::Requirement"
             build_requirement(node)
           when "!ruby/object:Gem::Dependency"
             build_dependency(node)
           when nil
             build_hash(node)
           else
             hash = build_hash(node)
             hash[:tag] = node.tag
             hash
  end

  store_anchor(node.anchor, result)
end

#build_node(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 410

def build_node(node)
  case node
  when nil then nil
  when AliasRef then resolve_alias(node)
  when Scalar then store_anchor(node.anchor, node.value)
  when Mapping then build_mapping(node)
  when Sequence then store_anchor(node.anchor, node.items.map {|item| build_node(item) })
  else node # already a Ruby object
  end
end

#build_platform(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 475

def build_platform(node)
  hash = pairs_to_hash(node)
  if hash["value"]
    Gem::Platform.new(hash["value"])
  else
    Gem::Platform.new([hash["cpu"], hash["os"], hash["version"]])
  end
end

#build_requirement(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 484

def build_requirement(node)
  r = Gem::Requirement.allocate
  hash = pairs_to_hash(node)
  reqs = hash["requirements"] || hash["value"]
  reqs = [] unless reqs.is_a?(Array)

  if reqs.is_a?(Array) && !reqs.empty?
    safe_reqs = []
    reqs.each do |item|
      if item.is_a?(Array) && item.size == 2
        op = item[0].to_s
        ver = item[1]
        if VALID_OPS.include?(op)
          version_obj = ver.is_a?(Gem::Version) ? ver : Gem::Version.new(ver.to_s)
          safe_reqs << [op, version_obj]
        end
      elsif item.is_a?(String)
        parsed = Gem::Requirement.parse(item)
        safe_reqs << parsed
      end
    rescue Gem::Requirement::BadRequirementError, Gem::Version::BadVersionError
      # Skip malformed items silently
    end
    reqs = safe_reqs unless safe_reqs.empty?
  end

  r.instance_variable_set(:@requirements, reqs)
  r
end

#build_safe_requirement(req_value) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 552

def build_safe_requirement(req_value)
  return Gem::Requirement.default unless req_value

  converted = req_value
  return Gem::Requirement.default unless converted.is_a?(Gem::Requirement)

  reqs = converted.instance_variable_get(:@requirements)
  if reqs&.is_a?(Array)
    valid = reqs.all? do |item|
      next true if item == Gem::Requirement::DefaultRequirement
      item.is_a?(Array) && item.size >= 2 && VALID_OPS.include?(item[0].to_s)
    end
    valid ? converted : Gem::Requirement.default
  else
    converted
  end
rescue StandardError
  Gem::Requirement.default
end

#build_specification(hash) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 532

def build_specification(hash)
  spec = Gem::Specification.allocate

  normalize_specification_version!(hash)
  normalize_rdoc_options!(hash)
  normalize_array_fields!(hash)

  spec.yaml_initialize("!ruby/object:Gem::Specification", hash)
  spec
end

#build_version(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 470

def build_version(node)
  hash = pairs_to_hash(node)
  Gem::Version.new((hash["version"] || hash["value"]).to_s)
end

#check_anchor!(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 592

def check_anchor!(node)
  if node.anchor
    raise Psych::AliasesNotEnabled unless @aliases
  end
end

#normalize_array_field(value) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 628

def normalize_array_field(value)
  if value.is_a?(Hash)
    value.values.flatten.compact
  elsif !value.is_a?(Array) && value
    [value].flatten.compact
  else
    value
  end
end

#normalize_array_fields!(hash) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 621

def normalize_array_fields!(hash)
  ARRAY_FIELDS.each do |field|
    next if field == "rdoc_options" # already handled
    hash[field] = normalize_array_field(hash[field]) if hash[field]
  end
end

#normalize_rdoc_options!(hash) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 604

def normalize_rdoc_options!(hash)
  opts = hash["rdoc_options"]
  if opts.is_a?(Hash)
    hash["rdoc_options"] = opts.values.flatten.compact.map(&:to_s)
  elsif opts.is_a?(Array)
    hash["rdoc_options"] = opts.flat_map do |opt|
      if opt.is_a?(Hash)
        opt.flat_map {|k, v| [k.to_s, v.to_s] }
      elsif opt.is_a?(String)
        opt
      else
        opt.to_s
      end
    end
  end
end

#normalize_specification_version!(hash) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 598

def normalize_specification_version!(hash)
  val = hash["specification_version"]
  return unless val && !val.is_a?(Integer)
  hash["specification_version"] = val.to_i if val.is_a?(String) && /\A\d+\z/.match?(val)
end

#pairs_to_hash(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 543

def pairs_to_hash(node)
  result = {}
  node.pairs.each do |key_node, value_node|
    key = key_node.is_a?(Scalar) ? key_node.value.to_s : build_node(key_node).to_s
    result[key] = build_node(value_node)
  end
  result
end

#resolve_alias(node) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 421

def resolve_alias(node)
  raise Psych::AliasesNotEnabled unless @aliases
  @anchor_values.fetch(node.name, nil)
end

#store_anchor(name, value) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 426

def store_anchor(name, value)
  @anchor_values[name] = value if name
  value
end

#validate_symbol!(sym) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 582

def validate_symbol!(sym)
  if @permitted_symbols.any? && !@permitted_symbols.include?(sym.to_s)
    if defined?(Psych::VERSION)
      raise Psych::DisallowedClass.new("load", sym.inspect)
    else
      raise Psych::DisallowedClass, "Tried to load unspecified class: #{sym.inspect}"
    end
  end
end

#validate_tag!(tag) (private)

[ GitHub ]

  
# File 'lib/rubygems/yaml_serializer.rb', line 572

def validate_tag!(tag)
  unless @permitted_tags.include?(tag)
    if defined?(Psych::VERSION)
      raise Psych::DisallowedClass.new("load", tag)
    else
      raise Psych::DisallowedClass, "Tried to load unspecified class: #{tag}"
    end
  end
end