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 425

def initialize(permitted_classes: [], permitted_symbols: [], aliases: true)
  @permitted_classes = permitted_classes.map {|c| "!ruby/object:#{c}" }
  @permitted_symbols = permitted_symbols
  @aliases = aliases
  @anchor_values = {}
  @alias_count = 0
end

Instance Method Details

#build(node)

[ GitHub ]

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

def build(node)
  return nil 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 569

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

  d.instance_variable_set(:@requirement, hash["requirement"] || hash["version_requirements"])

  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 500

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 474

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

  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)
           when "!ruby/object:Gem::Specification"
             hash = build_hash(node)
             hash[:tag] = node.tag
             hash
           else
             raise ArgumentError, "undefined class/module #{node.tag.sub("!ruby/object:", "")}"
  end

  store_anchor(node.anchor, result)
end

#build_node(node) (private)

[ GitHub ]

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

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 523

def build_platform(node)
  hash = pairs_to_hash(node)
  if (hash.keys & PLATFORM_FIELDS).any?
    Gem::Platform.new([hash["cpu"], hash["os"], hash["version"]])
  elsif hash["value"].is_a?(Array)
    # Malformed platform (e.g. sequence instead of mapping).
    # Return the raw value so yaml_initialize handles it like Psych does.
    hash["value"]
  else
    plat = Gem::Platform.allocate
    hash.each do |k, v|
      plat.instance_variable_set(:"@#{k}", v) if PLATFORM_ALLOWED_IVARS.include?(k)
    end
    plat
  end
end

#build_requirement(node) (private)

[ GitHub ]

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

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

  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_specification(hash) (private)

[ GitHub ]

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

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

  normalize_specification_version!(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 515

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 628

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 646

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 640

def normalize_array_fields!(hash)
  ARRAY_FIELDS.each do |field|
    hash[field] = normalize_array_field(hash[field]) if hash[field]
  end
end

#normalize_specification_version!(hash) (private)

[ GitHub ]

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

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 596

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

#raise_disallowed_class!(tag) (private)

[ GitHub ]

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

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

#resolve_alias(node) (private)

[ GitHub ]

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

def resolve_alias(node)
  raise Psych::AliasesNotEnabled unless @aliases
  @alias_count += 1
  if @alias_count > MAX_ALIAS_RESOLUTIONS
    raise Psych::BadAlias, "exceeded maximum alias resolutions (#{MAX_ALIAS_RESOLUTIONS})"
  end
  @anchor_values.fetch(node.name, nil)
end

#store_anchor(name, value) (private)

[ GitHub ]

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

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 618

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 605

def validate_tag!(tag)
  return if @permitted_classes.include?(tag)
  raise_disallowed_class!(tag)
end