123456789_123456789_123456789_123456789_123456789_

Class: Gem::SpecificationPolicy

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, SimpleDelegator
Instance Chain:
self, SimpleDelegator
Inherits: SimpleDelegator
  • Object
Defined in: lib/rubygems/specification_policy.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

  • #packaging rw

    If set to true, run packaging-specific checks, as well.

Instance Method Summary

Constructor Details

.new(specification) ⇒ SpecificationPolicy

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 21

def initialize(specification)
  @warnings = 0

  super(specification)
end

Instance Attribute Details

#packaging (rw)

If set to true, run packaging-specific checks, as well.

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 30

attr_accessor :packaging

Instance Method Details

#error(statement) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 398

def error(statement) # :nodoc:
  raise Gem::InvalidSpecificationException, statement
ensure
  alert_warning help_text
end

#help_text (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 404

def help_text # :nodoc:
  "See http://guides.rubygems.org/specification-reference/ for help"
end

#validate(strict = false)

Checks that the specification contains all required fields, and does a very basic sanity check.

Raises InvalidSpecificationException if the spec does not pass the checks.

[ GitHub ]

#validate_array_attribute(field) (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 281

def validate_array_attribute(field)
  val = self.send(field)
  klass = case field
          when :dependencies then
            Gem::Dependency
          else
            String
          end

  unless Array === val and val.all? {|x| x.kind_of?(klass)}
    raise(Gem::InvalidSpecificationException,
          "#{field} must be an Array of #{klass}")
  end
end

#validate_array_attributes (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 275

def validate_array_attributes
  Gem::Specification.array_attributes.each do |field|
    validate_array_attribute(field)
  end
end

#validate_attribute_present(attribute) (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 380

def validate_attribute_present(attribute)
  value = self.send attribute
  warning("no #{attribute} specified") if value.nil? || value.empty?
end

#validate_authors_field (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 296

def validate_authors_field
  return unless authors.empty?

  error "authors may not be empty"
end

#validate_dependencies

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 123

def validate_dependencies # :nodoc:
  # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle
  seen = Gem::Dependency::TYPES.inject({}) { |types, type| types.merge({ type => {}}) }

  error_messages = []
  warning_messages = []
  dependencies.each do |dep|
    if prev = seen[dep.type][dep.name]
      error_messages << <<-MESSAGE
duplicate dependency on #{dep}, (#{prev.requirement}) use:
  add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
      MESSAGE
    end

    seen[dep.type][dep.name] = dep

    prerelease_dep = dep.requirements_list.any? do |req|
      Gem::Requirement.new(req).prerelease?
    end

    warning_messages << "prerelease dependency on #{dep} is not recommended" if
        prerelease_dep && !version.prerelease?

    open_ended = dep.requirement.requirements.all? do |op, version|
      not version.prerelease? and (op == '>' or op == '>=')
    end

    if open_ended
      op, dep_version = dep.requirement.requirements.first

      segments = dep_version.segments

      base = segments.first 2

      recommendation = if (op == '>' || op == '>=') && segments == [0]
                         "  use a bounded requirement, such as '~> x.y'"
                       else
                         bugfix = if op == '>'
                                    ", '> #{dep_version}'"
                                  elsif op == '>=' and base != segments
                                    ", '>= #{dep_version}'"
                                  end

                         "  if #{dep.name} is semantically versioned, use:\n" \
                         "    add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}"
                       end

      warning_messages << ["open-ended dependency on #{dep} is not recommended", recommendation].join("\n") + "\n"
    end
  end
  if error_messages.any?
    error error_messages.join
  end
  if warning_messages.any?
    warning_messages.each { |warning_message| warning warning_message }
  end
end

#validate_lazy_metadata (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 329

def 
  unless authors.grep(LAZY_PATTERN).empty?
    error "#{LAZY} is not an author"
  end

  unless Array(email).grep(LAZY_PATTERN).empty?
    error "#{LAZY} is not an email"
  end

  if description =~ LAZY_PATTERN
    error "#{LAZY} is not a description"
  end

  if summary =~ LAZY_PATTERN
    error "#{LAZY} is not a summary"
  end

  # Make sure a homepage is valid HTTP/HTTPS URI
  if homepage and not homepage.empty?
    begin
      homepage_uri = URI.parse(homepage)
      unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
        error "\"#{homepage}\" is not a valid HTTP URI"
      end
    rescue URI::InvalidURIError
      error "\"#{homepage}\" is not a valid HTTP URI"
    end
  end
end

#validate_licenses (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 302

def validate_licenses
  licenses.each { |license|
    if license.length > 64
      error "each license must be 64 characters or less"
    end

    if !Gem::Licenses.match?(license)
      suggestions = Gem::Licenses.suggestions(license)
      message = <<-warning
license value '#{license}' is invalid.  Use a license identifier from
http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
      warning
      message += "Did you mean #{suggestions.map { |s| "'#{s}'"}.join(', ')}?\n" unless suggestions.nil?
      warning(message)
    end
  }

  warning <<-warning if licenses.empty?
licenses is empty, but is recommended.  Use a license identifier from
http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
  warning
end

#validate_metadata

Implementation for Specification#validate_metadata

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 90

def 
  unless Hash === 
    error 'metadata must be a hash'
  end

  .each do |key, value|
    if !key.kind_of?(String)
      error "metadata keys must be a String"
    end

    if key.size > 128
      error "metadata key too large (#{key.size} > 128)"
    end

    if !value.kind_of?(String)
      error "metadata values must be a String"
    end

    if value.size > 1024
      error "metadata value too large (#{value.size} > 1024)"
    end

    if METADATA_LINK_KEYS.include? key
      if value !~ VALID_URI_PATTERN
        error "metadata['#{key}'] has invalid link: #{value.inspect}"
      end
    end
  end
end

#validate_name (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 228

def validate_name
  if !name.is_a?(String)
    error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
  elsif name !~ /[a-zA-Z]/
    error "invalid value for attribute name: #{name.dump} must include at least one letter"
  elsif name !~ VALID_NAME_PATTERN
    error "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
  elsif name =~ SPECIAL_CHARACTERS
    error "invalid value for attribute name: #{name.dump} can not begin with a period, dash, or underscore"
  end
end

#validate_nil_attributes (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 205

def validate_nil_attributes
  nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
    __getobj__.instance_variable_get("@#{attrname}").nil?
  end
  return if nil_attributes.empty?
  error "#{nil_attributes.join ', '} must not be nil"
end

#validate_non_files (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 246

def validate_non_files
  return unless packaging
  non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}

  unless non_files.empty?
    error "[\"#{non_files.join "\", \""}\"] are not files"
  end
end

#validate_permissions

Issues a warning for each file to be packaged which is world-readable.

Implementation for Specification#validate_permissions

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 186

def validate_permissions
  return if Gem.win_platform?

  files.each do |file|
    next unless File.file?(file)
    next if File.stat(file).mode & 0444 == 0444
    warning "#{file} is not world-readable"
  end

  executables.each do |name|
    exec = File.join bindir, name
    next unless File.file?(exec)
    next if File.stat(exec).executable?
    warning "#{exec} is not executable"
  end
end

#validate_platform (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 267

def validate_platform
  case platform
  when Gem::Platform, Gem::Platform::RUBY  # ok
  else
    error "invalid platform #{platform.inspect}, see Gem::Platform"
  end
end

#validate_require_paths (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 240

def validate_require_paths
  return unless raw_require_paths.empty?

  error 'specification must have at least one require_path'
end

#validate_required_attributes (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 220

def validate_required_attributes
  Gem::Specification.required_attributes.each do |symbol|
    unless send symbol
      error "missing value for attribute #{symbol}"
    end
  end
end

#validate_rubygems_version (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 213

def validate_rubygems_version
  return unless packaging
  return if rubygems_version == Gem::VERSION

  error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
end

#validate_self_inclusion_in_files_list (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 255

def validate_self_inclusion_in_files_list
  return unless files.include?(file_name)

  error "#{full_name} contains itself (#{file_name}), check your files list"
end

#validate_shebang_line_in(executable) (private)

[ GitHub ]

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

def validate_shebang_line_in(executable)
  executable_path = File.join(bindir, executable)
  return if File.read(executable_path, 2) == '#!'

  warning "#{executable_path} is missing #! line"
end

#validate_specification_version (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 261

def validate_specification_version
  return if specification_version.is_a?(Integer)

  error 'specification_version must be an Integer (did you mean version?)'
end

#validate_values (private)

[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 359

def validate_values
  %w[author homepage summary files].each do |attribute|
    validate_attribute_present(attribute)
  end

  if description == summary
    warning "description and summary are identical"
  end

  # TODO: raise at some given date
  warning "deprecated autorequire specified" if autorequire

  executables.each do |executable|
    validate_shebang_line_in(executable)
  end

  files.select { |f| File.symlink?(f) }.each do |file|
    warning "#{file} is a symlink, which is not supported on all platforms"
  end
end

#warning(statement) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/specification_policy.rb', line 392

def warning(statement) # :nodoc:
  @warnings += 1

  alert_warning statement
end