# class Gem::SpecificationPolicy

### Constants

HOMEPAGE_URI_PATTERN
LAZY
LAZY_PATTERN

### Attributes

packaging [RW]

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

### Public Class Methods

new (specification)
Source

    # File lib/rubygems/specification_policy.rb, line 26
    def initialize(specification)
      @warnings = 0

      @specification = specification
    end

### Public Instance Methods

validate (strict = false)
Source

    # File lib/rubygems/specification_policy.rb, line 46
    def validate(strict = false)
      validate_required!
      validate_required_metadata!

      validate_optional(strict) if packaging || strict

      true
    end

Does a sanity check on the specification.

Raises InvalidSpecificationException if the spec does not pass the checks.

It also performs some validations that do not raise but print warning messages instead.

validate_for_resolution ()
Source

    # File lib/rubygems/specification_policy.rb, line 129
    def validate_for_resolution
      validate_required!
    end

Implementation for Specification#validate_for_resolution

validate_metadata ()
Source

    # File lib/rubygems/specification_policy.rb, line 136
    def validate_metadata
      metadata = @specification.metadata

      unless Hash === metadata
        error "metadata must be a hash"
      end

      metadata.each do |key, value|
        entry = "metadata['#{key}']"
        unless key.is_a?(String)
          error "metadata keys must be a String"
        end

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

        unless value.is_a?(String)
          error "#{entry} value must be a String"
        end

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

        next unless METADATA_LINK_KEYS.include? key
        unless VALID_URI_PATTERN.match?(value)
          error "#{entry} has invalid link: #{value.inspect}"
        end
      end
    end

Implementation for Specification#validate_metadata

validate_optional (strict)
Source

    # File lib/rubygems/specification_policy.rb, line 100
    def validate_optional(strict)
      validate_licenses

      validate_permissions

      validate_values

      validate_dependencies

      validate_required_ruby_version

      validate_extensions

      validate_removed_attributes

      validate_unique_links

      if @warnings > 0
        if strict
          error "specification has warnings"
        else
          alert_warning help_text
        end
      end
    end

validate_permissions ()
Source

    # File lib/rubygems/specification_policy.rb, line 253
    def validate_permissions
      return if Gem.win_platform?

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

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

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

Implementation for Specification#validate_permissions

validate_required! ()
Source

    # File lib/rubygems/specification_policy.rb, line 64
    def validate_required!
      validate_nil_attributes

      validate_rubygems_version

      validate_required_attributes

      validate_name

      validate_require_paths

      @specification.keep_only_files_and_directories

      validate_non_files

      validate_self_inclusion_in_files_list

      validate_specification_version

      validate_platform

      validate_array_attributes

      validate_authors_field

      validate_licenses_length

      validate_duplicate_dependencies
    end

Does a sanity check on the specification.

Raises InvalidSpecificationException if the spec does not pass the checks.

Only runs checks that are considered necessary for the specification to be functional.

validate_required_metadata! ()
Source

    # File lib/rubygems/specification_policy.rb, line 94
    def validate_required_metadata!
      validate_metadata

      validate_lazy_metadata
    end

validate_required_ruby_version ()
Source

    # File lib/rubygems/specification_policy.rb, line 242
    def validate_required_ruby_version
      if @specification.required_ruby_version.requirements == [Gem::Requirement::DefaultRequirement]
        warning "make sure you specify the oldest ruby version constraint (like \">= 3.0\") that you want your gem to support by setting the `required_ruby_version` gemspec attribute"
      end
    end

### Private Instance Methods

validate_array_attribute (field)
Source

    # File lib/rubygems/specification_policy.rb, line 360
    def validate_array_attribute(field)
      val = @specification.send(field)
      klass = case field
              when :dependencies then
                Gem::Dependency
              else
                String
      end

      unless Array === val && val.all? {|x| x.is_a?(klass) || (field == :licenses && x.nil?) }
        error "#{field} must be an Array of #{klass}"
      end
    end

validate_array_attributes ()
Source

    # File lib/rubygems/specification_policy.rb, line 354
    def validate_array_attributes
      Gem::Specification.array_attributes.each do |field|
        validate_array_attribute(field)
      end
    end

validate_attribute_present (attribute)
Source

    # File lib/rubygems/specification_policy.rb, line 483
    def validate_attribute_present(attribute)
      value = @specification.send attribute
      warning("no #{attribute} specified") if value.nil? || value.empty?
    end

validate_authors_field ()
Source

    # File lib/rubygems/specification_policy.rb, line 374
    def validate_authors_field
      return unless @specification.authors.empty?

      error "authors may not be empty"
    end

validate_lazy_metadata ()
Source

    # File lib/rubygems/specification_policy.rb, line 429
    def validate_lazy_metadata
      unless @specification.authors.grep(LAZY_PATTERN).empty?
        error "#{LAZY} is not an author"
      end

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

      if LAZY_PATTERN.match?(@specification.description)
        error "#{LAZY} is not a description"
      end

      if LAZY_PATTERN.match?(@specification.summary)
        error "#{LAZY} is not a summary"
      end

      homepage = @specification.homepage

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

validate_licenses ()
Source

    # File lib/rubygems/specification_policy.rb, line 392
      def validate_licenses
        licenses = @specification.licenses

        licenses.each do |license|
          next if Gem::Licenses.match?(license) || license.nil?
          license_id_deprecated = Gem::Licenses.deprecated_license_id?(license)
          exception_id_deprecated = Gem::Licenses.deprecated_exception_id?(license)
          suggestions = Gem::Licenses.suggestions(license)

          if license_id_deprecated
            main_message = "License identifier '#{license}' is deprecated"
          elsif exception_id_deprecated
            main_message = "Exception identifier at '#{license}' is deprecated"
          else
            main_message = "License identifier '#{license}' is invalid"
          end

          message = <<-WARNING
    #{main_message}. Use an identifier from
    https://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license,
    or set it to nil if you don't want to specify a 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 an license identifier from
    https://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license,
    or set it to nil if you don't want to specify a license.
        WARNING
      end

validate_licenses_length ()
Source

    # File lib/rubygems/specification_policy.rb, line 380
    def validate_licenses_length
      licenses = @specification.licenses

      licenses.each do |license|
        next if license.nil?

        if license.length > 64
          error "each license must be 64 characters or less"
        end
      end
    end

validate_name ()
Source

    # File lib/rubygems/specification_policy.rb, line 300
    def validate_name
      name = @specification.name

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

validate_nil_attributes ()
Source

    # File lib/rubygems/specification_policy.rb, line 272
    def validate_nil_attributes
      nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
        @specification.instance_variable_get("@#{attrname}").nil?
      end
      return if nil_attributes.empty?
      error "#{nil_attributes.join ", "} must not be nil"
    end

validate_non_files ()
Source

    # File lib/rubygems/specification_policy.rb, line 320
    def validate_non_files
      return unless packaging

      non_files = @specification.files.reject {|x| File.file?(x) || File.symlink?(x) }

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

validate_platform ()
Source

    # File lib/rubygems/specification_policy.rb, line 344
    def validate_platform
      platform = @specification.platform

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

validate_require_paths ()
Source

    # File lib/rubygems/specification_policy.rb, line 314
    def validate_require_paths
      return unless @specification.raw_require_paths.empty?

      error "specification must have at least one require_path"
    end

validate_required_attributes ()
Source

    # File lib/rubygems/specification_policy.rb, line 292
    def validate_required_attributes
      Gem::Specification.required_attributes.each do |symbol|
        unless @specification.send symbol
          error "missing value for attribute #{symbol}"
        end
      end
    end

validate_rubygems_version ()
Source

    # File lib/rubygems/specification_policy.rb, line 280
    def validate_rubygems_version
      return unless packaging

      rubygems_version = @specification.rubygems_version

      return if rubygems_version == Gem::VERSION

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

      @specification.rubygems_version = Gem::VERSION
    end

validate_self_inclusion_in_files_list ()
Source

    # File lib/rubygems/specification_policy.rb, line 330
    def validate_self_inclusion_in_files_list
      file_name = @specification.file_name

      return unless @specification.files.include?(file_name)

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

validate_shebang_line_in (executable)
Source

    # File lib/rubygems/specification_policy.rb, line 488
    def validate_shebang_line_in(executable)
      executable_path = File.join(@specification.bindir, executable)
      return if File.read(executable_path, 2) == "#!"

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

validate_specification_version ()
Source

    # File lib/rubygems/specification_policy.rb, line 338
    def validate_specification_version
      return if @specification.specification_version.is_a?(Integer)

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

validate_unique_links ()
Source

    # File lib/rubygems/specification_policy.rb, line 527
      def validate_unique_links
        links = @specification.metadata.slice(*METADATA_LINK_KEYS)
        grouped = links.group_by {|_key, uri| uri }
        grouped.each do |uri, copies|
          next unless copies.length > 1
          keys = copies.map(&:first).join("\n  ")
          warning <<~WARNING
            You have specified the uri:
              #{uri}
            for all of the following keys:
              #{keys}
            Only the first one will be shown on rubygems.org
          WARNING
        end
      end

validate_values ()
Source

    # File lib/rubygems/specification_policy.rb, line 462
    def validate_values
      %w[author homepage summary files].each do |attribute|
        validate_attribute_present(attribute)
      end

      if @specification.description == @specification.summary
        warning "description and summary are identical"
      end

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

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

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