Class: Gem::SpecificationPolicy

Inherits:
Object
  • Object
show all
Includes:
UserInteraction
Defined in:
lib/rubygems/specification_policy.rb

Constant Summary collapse

VALID_NAME_PATTERN =

:nodoc:

/\A[a-zA-Z0-9\.\-\_]+\z/
SPECIAL_CHARACTERS =

:nodoc:

/\A[#{Regexp.escape(".-_")}]+/
VALID_URI_PATTERN =

:nodoc:

%r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}
%w[
  homepage_uri
  changelog_uri
  source_code_uri
  documentation_uri
  wiki_uri
  mailing_list_uri
  bug_tracker_uri
  download_uri
  funding_uri
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from UserInteraction

#alert, #alert_error, #alert_warning, #ask, #ask_for_password, #ask_yes_no, #choose_from_list, #say, #terminate_interaction, #verbose

Methods included from DefaultUserInteraction

ui, #ui, ui=, #ui=, use_ui, #use_ui

Methods included from Text

#clean_text, #format_text, #levenshtein_distance, #min3, #truncate_text

Constructor Details

#initialize(specification) ⇒ SpecificationPolicy

:nodoc:



26
27
28
29
30
# File 'lib/rubygems/specification_policy.rb', line 26

def initialize(specification)
  @warnings = 0

  @specification = specification
end

Instance Attribute Details

#packagingObject

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



35
36
37
# File 'lib/rubygems/specification_policy.rb', line 35

def packaging
  @packaging
end

Instance Method Details

#validate(strict = false) ⇒ Object

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.



46
47
48
49
50
51
52
# File 'lib/rubygems/specification_policy.rb', line 46

def validate(strict = false)
  validate_required!

  validate_optional(strict) if packaging || strict

  true
end

#validate_dependenciesObject

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.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/rubygems/specification_policy.rb', line 187

def validate_dependencies # :nodoc:
  warning_messages = []
  @specification.dependencies.each do |dep|
    if dep.name == @specification.name # warn on self reference
      warning_messages << "Self referencing dependency is unnecessary and strongly discouraged."
    end

    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 && !@specification.version.prerelease?

    open_ended = dep.requirement.requirements.all? do |op, version|
      !version.prerelease? && [">", ">="].include?(op)
    end

    next unless open_ended
    op, dep_version = dep.requirement.requirements.first

    segments = dep_version.segments

    base = segments.first 2

    recommendation = if [">", ">="].include?(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

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

#validate_duplicate_dependenciesObject

Checks that no duplicate dependencies are specified.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/rubygems/specification_policy.rb', line 161

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 => {} }) }

  error_messages = []
  @specification.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
  end
  if error_messages.any?
    error error_messages.join
  end
end

#validate_metadataObject

Implementation for Specification#validate_metadata



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/rubygems/specification_policy.rb', line 126

def 
   = @specification.

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

  .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

#validate_optional(strict) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rubygems/specification_policy.rb', line 97

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_permissionsObject

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

Implementation for Specification#validate_permissions



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/rubygems/specification_policy.rb', line 243

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

#validate_required!Object

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.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rubygems/specification_policy.rb', line 63

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

#validate_required_ruby_versionObject



232
233
234
235
236
# File 'lib/rubygems/specification_policy.rb', line 232

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