Class: Gem::SpecificationPolicy
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
|
|
Inherits: | Object |
Defined in: | lib/rubygems/specification_policy.rb |
Constant Summary
-
HOMEPAGE_URI_PATTERN =
# File 'lib/rubygems/specification_policy.rb', line 392/\A[a-z][a-z\d+.-]*:/i.freeze
-
LAZY =
# File 'lib/rubygems/specification_policy.rb', line 390'"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, "")
-
LAZY_PATTERN =
# File 'lib/rubygems/specification_policy.rb', line 391/\AFI XME|\ATO DO/x.freeze
-
METADATA_LINK_KEYS =
Internal use only
# File 'lib/rubygems/specification_policy.rb', line 14%w[ bug_tracker_uri changelog_uri documentation_uri homepage_uri mailing_list_uri source_code_uri wiki_uri funding_uri ].freeze
-
SPECIAL_CHARACTERS =
Internal use only
# File 'lib/rubygems/specification_policy.rb', line 10/\A[#{Regexp.escape('.-_')}]+/.freeze
-
VALID_NAME_PATTERN =
Internal use only
# File 'lib/rubygems/specification_policy.rb', line 8/\A[a-zA-Z0-9\.\-\_]+\z/.freeze
-
VALID_URI_PATTERN =
Internal use only
# File 'lib/rubygems/specification_policy.rb', line 12%r{\Ahttps?:\/\/([^\s:@]:[^\s:@]*@)?[A-Za-z\d\-](\.[A-Za-z\d\-])\.?(:\d{1,5})?([\/?]\S*)?\z}.freeze
Class Method Summary
- .new(specification) ⇒ SpecificationPolicy constructor Internal use only
Instance Attribute Summary
-
#packaging
rw
If set to true, run packaging-specific checks, as well.
DefaultUserInteraction
- Included
Instance Method Summary
-
#validate(strict = false)
Does a sanity check on the specification.
-
#validate_metadata
Implementation for Specification#validate_metadata
- #validate_optional(strict)
-
#validate_permissions
Issues a warning for each file to be packaged which is world-readable.
-
#validate_required!
Does a sanity check on the specification.
- #validate_array_attribute(field) private
- #validate_array_attributes private
- #validate_attribute_present(attribute) private
- #validate_authors_field private
- #validate_lazy_metadata private
- #validate_licenses private
- #validate_licenses_length private
- #validate_name private
- #validate_nil_attributes private
- #validate_non_files private
- #validate_platform private
- #validate_require_paths private
- #validate_required_attributes private
- #validate_rubygems_version private
- #validate_self_inclusion_in_files_list private
- #validate_shebang_line_in(executable) private
- #validate_specification_version private
- #validate_values private
-
#validate_dependencies
Internal use only
Checks that the gem does not depend on itself.
-
#validate_duplicate_dependencies
Internal use only
Checks that no duplicate dependencies are specified.
- #error(statement) private Internal use only
- #help_text private Internal use only
- #validate_extensions private Internal use only
- #validate_rake_extensions(builder) private Internal use only
- #validate_removed_attributes private Internal use only
- #validate_rust_extensions(builder) private Internal use only
- #warning(statement) private Internal use only
UserInteraction
- Included
#alert | Displays an alert |
#alert_error | Displays an error |
#alert_warning | Displays a warning |
#ask | Asks a |
#ask_for_password | Asks for a password with a |
#ask_yes_no | Asks a yes or no |
#choose_from_list | Asks the user to answer |
#say | Displays the given |
#terminate_interaction | Terminates the RubyGems process with the given |
#verbose | Calls |
DefaultUserInteraction
- Included
Text
- Included
#clean_text | Remove any non-printable characters and make the text suitable for printing. |
#format_text | Wraps |
#levenshtein_distance | Returns a value representing the “cost” of transforming str1 into str2 Vendored version of |
#truncate_text, #min3 |
Constructor Details
.new(specification) ⇒ SpecificationPolicy
# File 'lib/rubygems/specification_policy.rb', line 25
def initialize(specification) @warnings = 0 @specification = specification end
Instance Attribute Details
#packaging (rw)
If set to true, run packaging-specific checks, as well.
# File 'lib/rubygems/specification_policy.rb', line 34
attr_accessor :packaging
Instance Method Details
#error(statement) (private)
# File 'lib/rubygems/specification_policy.rb', line 498
def error(statement) # :nodoc: raise Gem::InvalidSpecificationException, statement ensure alert_warning help_text end
#help_text (private)
# File 'lib/rubygems/specification_policy.rb', line 504
def help_text # :nodoc: "See https://guides.rubygems.org/specification-reference/ for help" end
#validate(strict = false)
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.
# File 'lib/rubygems/specification_policy.rb', line 45
def validate(strict = false) validate_required! validate_optional(strict) if packaging || strict true end
#validate_array_attribute(field) (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 339
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.kind_of?(klass) } error "#{field} must be an Array of #{klass}" end end
#validate_array_attributes (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 333
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 448
def validate_attribute_present(attribute) value = @specification.send attribute warning("no #{attribute} specified") if value.nil? || value.empty? end
#validate_authors_field (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 353
def return unless @specification. .empty? error "authors may not be empty" end
#validate_dependencies
Checks that the gem does not depend on itself. Checks that dependencies use requirements as we recommend. Warnings are issued when dependencies are open-ended or overly strict for semantic versioning.
# File 'lib/rubygems/specification_policy.rb', line 183
def validate_dependencies # :nodoc: = [] @specification.dependencies.each do |dep| if dep.name == @specification.name # warn on self reference << "Self referencing dependency is unnecessary and strongly discouraged." end prerelease_dep = dep.requirements_list.any? do |req| Gem::Requirement.new(req).prerelease? end << "prerelease dependency on #{dep} is not recommended" if prerelease_dep && !@specification.version.prerelease? open_ended = dep.requirement.requirements.all? do |op, version| !version.prerelease? && (op == ">" || 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 == ">=" && base != segments ", '>= #{dep_version}'" end " if #{dep.name} is semantically versioned, use:\n" \ " add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}" end << ["open-ended dependency on #{dep} is not recommended", recommendation].join("\n") + "\n" end end if .any? .each {|| warning } end end
#validate_duplicate_dependencies
Checks that no duplicate dependencies are specified.
# File 'lib/rubygems/specification_policy.rb', line 157
def validate_duplicate_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 => {} }) } = [] @specification.dependencies.each do |dep| if prev = seen[dep.type][dep.name] << <<-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 end if .any? error .join end end
#validate_extensions (private)
# File 'lib/rubygems/specification_policy.rb', line 466
def validate_extensions # :nodoc: require_relative "ext" builder = Gem::Ext::Builder.new(@specification) validate_rake_extensions(builder) validate_rust_extensions(builder) end
#validate_lazy_metadata (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 394
def unless @specification. .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 @specification.description =~ LAZY_PATTERN error "#{LAZY} is not a description" end if @specification.summary =~ LAZY_PATTERN error "#{LAZY} is not a summary" end homepage = @specification.homepage # Make sure a homepage is valid HTTP/HTTPS URI if homepage && !homepage.empty? require "uri" 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 369
def validate_licenses licenses = @specification.licenses licenses.each do |license| if !Gem::Licenses.match?(license) suggestions = Gem::Licenses.suggestions(license) = <<-WARNING license value '#{license}' is invalid. Use a license identifier from http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license. WARNING += "Did you mean #{suggestions.map {|s| "'#{s}'" }.join(', ')}?\n" unless suggestions.nil? warning( ) end 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_licenses_length (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 359
def validate_licenses_length licenses = @specification.licenses licenses.each do |license| if license.length > 64 error "each license must be 64 characters or less" end end end
#validate_metadata
Implementation for Specification#validate_metadata
# File 'lib/rubygems/specification_policy.rb', line 121
def = @specification. unless Hash === error "metadata must be a hash" end .each do |key, value| entry = "metadata['#{key}']" if !key.kind_of?(String) error "metadata keys must be a String" end if key.size > 128 error "metadata key is too large (#{key.size} > 128)" end if !value.kind_of?(String) error "#{entry} value must be a String" end if value.size > 1024 error "#{entry} value is too large (#{value.size} > 1024)" end if METADATA_LINK_KEYS.include? key if value !~ VALID_URI_PATTERN error "#{entry} has invalid link: #{value.inspect}" end end end end
#validate_name (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 279
def validate_name name = @specification.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 253
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 (private)
[ GitHub ]#validate_optional(strict)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 96
def validate_optional(strict) validate_licenses validate_values validate_dependencies validate_extensions validate_removed_attributes if @warnings > 0 if strict error "specification has warnings" else alert_warning help_text end end end
#validate_permissions
Issues a warning for each file to be packaged which is world-readable.
Implementation for Specification#validate_permissions
# File 'lib/rubygems/specification_policy.rb', line 234
def return if Gem.win_platform? @specification.files.each do |file| next unless File.file?(file) next if File.stat(file).mode & 0444 == 0444 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
#validate_platform (private)
[ GitHub ]#validate_rake_extensions(builder) (private)
# File 'lib/rubygems/specification_policy.rb', line 483
def validate_rake_extensions(builder) # :nodoc: rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder } rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" } warning <<-WARNING if rake_extension && !rake_dependency You have specified rake based extension, but rake is not added as dependency. It is recommended to add rake as a dependency in gemspec since there's no guarantee rake will be already installed. WARNING end
#validate_removed_attributes (private)
# File 'lib/rubygems/specification_policy.rb', line 460
def validate_removed_attributes # :nodoc: @specification.removed_method_calls.each do |attr| warning("#{attr} is deprecated and ignored. Please remove this from your gemspec to ensure that your gem continues to build in the future.") end end
#validate_require_paths (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 293
def validate_require_paths return unless @specification.raw_require_paths.empty? error "specification must have at least one require_path" end
#validate_required!
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.
# File 'lib/rubygems/specification_policy.rb', line 62
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_licenses_length validate_duplicate_dependencies end
#validate_required_attributes (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 271
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 (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 261
def validate_rubygems_version return unless packaging rubygems_version = @specification.rubygems_version return if rubygems_version == Gem::VERSION error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" end
#validate_rust_extensions(builder) (private)
# File 'lib/rubygems/specification_policy.rb', line 474
def validate_rust_extensions(builder) # :nodoc: rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder } missing_cargo_lock = !@specification.files.any? {|f| f.end_with?("Cargo.lock") } error <<-ERROR if rust_extension && missing_cargo_lock You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec. ERROR end
#validate_self_inclusion_in_files_list (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 309
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) (private)
[ GitHub ]#validate_specification_version (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 317
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_values (private)
[ GitHub ]# File 'lib/rubygems/specification_policy.rb', line 427
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
#warning(statement) (private)
# File 'lib/rubygems/specification_policy.rb', line 492
def warning(statement) # :nodoc: @warnings += 1 alert_warning statement end