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/.freeze
SPECIAL_CHARACTERS =

:nodoc:

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

:nodoc:

%r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}.freeze
%w[
  bug_tracker_uri
  changelog_uri
  documentation_uri
  homepage_uri
  mailing_list_uri
  source_code_uri
  wiki_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:



23
24
25
26
27
# File 'lib/rubygems/specification_policy.rb', line 23

def initialize(specification)
  @warnings = 0

  @specification = specification
end

Instance Attribute Details

#packagingObject

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



32
33
34
# File 'lib/rubygems/specification_policy.rb', line 32

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.



43
44
45
46
47
48
49
# File 'lib/rubygems/specification_policy.rb', line 43

def validate(strict = false)
  validate_required!

  validate_optional(strict) if packaging || strict

  true
end

#validate_dependenciesObject

Checks that dependencies use requirements as we recommend. Warnings are issued when dependencies are open-ended or overly strict for semantic versioning.



180
181
182
183
184
185
186
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
# File 'lib/rubygems/specification_policy.rb', line 180

def validate_dependencies # :nodoc:
  warning_messages = []
  @specification.dependencies.each do |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 && !@specification.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 warning_messages.any?
    warning_messages.each {|warning_message| warning warning_message }
  end
end

#validate_duplicate_dependenciesObject

Checks that no duplicate dependencies are specified.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rubygems/specification_policy.rb', line 155

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



119
120
121
122
123
124
125
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
# File 'lib/rubygems/specification_policy.rb', line 119

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_optional(strict) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rubygems/specification_policy.rb', line 94

def validate_optional(strict)
  validate_licenses

  validate_permissions

  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_permissionsObject

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

Implementation for Specification#validate_permissions



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/rubygems/specification_policy.rb', line 227

def validate_permissions
  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_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.



60
61
62
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
# File 'lib/rubygems/specification_policy.rb', line 60

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