Class: Composer::Package::Version::VersionParser

Inherits:
Object
  • Object
show all
Defined in:
lib/composer/package/version/version_parser.rb

Overview

Version Parser

PHP Authors: Jordi Boggiano <[email protected]>

Ruby Authors: Ioannis Kappas <[email protected]>

Constant Summary collapse

MODIFIER_REGEX =
'[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?'.freeze()

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.format_version(package, truncate = true) ⇒ Object

Formats package version Param: Composer::Package::Package package Param: boolean truncate Return: string



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/composer/package/version/version_parser.rb', line 76

def format_version(package, truncate = true)
  if !package.is_dev || !['hg', 'git'].include?(package.source_type)
    return package.pretty_version
  end

  # if source reference is a sha1 hash -- truncate
  if truncate && package.source_reference.length === 40
    return "#{package.pretty_version} #{package.source_reference[0..6]}"
  end

  "#{package.pretty_version} #{package.source_reference}"
end

.normalize_stability(stability) ⇒ Object

Normalize the specified stability Param: string stability Return: string

Raises:



65
66
67
68
69
70
# File 'lib/composer/package/version/version_parser.rb', line 65

def normalize_stability(stability)
  raise ArgumentError, 'stability must be specified' unless stability
  raise TypeError, 'stability must be of type String' unless stability.is_a?(String)
  stability = stability.downcase
  stability === 'rc' ? 'RC' : stability
end

.parse_stability(version) ⇒ Object

Returns the stability of a version

Params:

version

string The version to parse for stability

Returns: string The version’s stability

Raises:



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/composer/package/version/version_parser.rb', line 34

def parse_stability(version)
  raise ArgumentError, 'version must be specified' unless version
  raise TypeError, 'version must be of type String' unless version.is_a?(String)
  raise UnexpectedValueError, 'version string must not be empty' if version.empty?

  version = version.gsub(/#.+$/i, '')

  if version.start_with?('dev-') || version.end_with?('-dev')
    return 'dev'
  end

  if matches = /#{MODIFIER_REGEX}$/i.match(version.downcase)
    if matches[3]
      return 'dev'
    elsif matches[1]
      if 'beta' === matches[1] || 'b' === matches[1]
        return 'beta'
      elsif 'alpha' === matches[1] || 'a' === matches[1]
        return 'alpha'
      elsif 'rc' === matches[1]
        return 'RC'
      end
    end
  end

  'stable'
end

Instance Method Details

#normalize(version, full_version = nil) ⇒ Object

Normalizes a version string to be able to perform comparisons on it

Params:

version

String The version string to normalize

full_version

String optional complete version string to give more context

Throws:

+InvalidVersionStringError+

Returns:

+String+

Raises:



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/composer/package/version/version_parser.rb', line 102

def normalize(version, full_version = nil)
  raise ArgumentError, 'version must be specified' unless version
  raise TypeError, 'version must be of type String' unless version.is_a?(String)
  raise UnexpectedValueError, 'version string must not be empty' if version.empty?

  version.strip!
  if full_version == nil
    full_version = version
  end

  # ignore aliases and just assume the alias is required
  # instead of the source
  if matches = /^([^,\s]+) +as +([^,\s]+)$/.match(version)
    version = matches[1]
  end

  # ignore build metadata
  if matches = /^([^,\s+]+)\+[^\s]+$/.match(version)
    version = matches[1]
  end

  # match master-like branches
  if matches = /^(?:dev-)?(?:master|trunk|default)$/i.match(version)
    return '9999999-dev'
  end

  if 'dev-' === version[0...4].downcase
    return "dev-#{version[4..version.size]}"
  end

  # match classical versioning
  index = 0
  if matches = /^v?(\d{1,3})(\.\d+)?(\.\d+)?(\.\d+)?#{MODIFIER_REGEX}$/i.match(version)
    version = ''
    matches.to_a[1..4].each do |c|
      version += c ? c : '.0'
    end
    index = 5
  elsif matches = /^v?(\d{4}(?:[.:-]?\d{2}){1,6}(?:[.:-]?\d{1,3})?)#{MODIFIER_REGEX}$/i.match(version)
    version = matches[1].gsub(/\D/, '-')
    index = 2
  elsif matches = /^v?(\d{4,})(\.\d+)?(\.\d+)?(\.\d+)?#{MODIFIER_REGEX}$/i.match(version)
    version = ''
    matches.to_a[1..4].each do |c|
      version << (c.nil? ? '.0' : c)
    end
    index = 5
  end

  # add version modifiers if a version was matched
  if index > 0
    if matches[index]
      if 'stable' === matches[index]
        return version
      end
      stability = expand_stability(matches[index])
      version = "#{version}-#{stability ? stability : matches[index]}#{matches[index + 1] ? matches[index + 1] : ''}"
    end

    if matches[index + 2]
      version = "#{version}-dev"
    end

    return version
  end

  # match dev branches
  if matches = /(.*?)[.-]?dev$/i.match(version)
    begin
      return normalize_branch(matches[1])
    rescue
    end
  end

  extra_message = ''
  if matches = / +as +#{Regexp.escape(version)}$/.match(full_version)
    extra_message = " in \"#{full_version}\", the alias must be an exact version"
  elsif matches = /^#{Regexp.escape(version)} +as +/.match(full_version)
    extra_message = " in \"#{full_version}\", the alias source must be an exact version, if it is a branch name you should prefix it with dev-"
  end

  raise UnexpectedValueError, "Invalid version string \"#{version}\"#{extra_message}"
end

#normalize_branch(name) ⇒ Object

Normalizes a branch name to be able to perform comparisons on it

Params:

name

string The branch name to normalize

Returns: string The normalized branch name



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/composer/package/version/version_parser.rb', line 193

def normalize_branch(name)
  name.strip!

  if ['master', 'trunk', 'default'].include?(name)
    return normalize(name)
  end

  if matches = /^v?(\d+)(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?(\.(?:\d+|[xX*]))?$/i.match(name)
    version = ''

    # for i in 0..3
    #   # version << matches[i] ? matches[i].gsub('*', 'x').gsub('X', 'x') : '.x'
    # end
    matches.captures.each { |match| version << (match != nil ? match.gsub('*', 'x').gsub('X', 'x') : '.x') }
    return "#{version.gsub('x', '9999999')}-dev"
  end

  "dev-#{name}"
end

#parse_constraints(constraints) ⇒ Object

Raises:



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/composer/package/version/version_parser.rb', line 240

def parse_constraints(constraints)
  raise ArgumentError, 'version must be specified' unless constraints
  raise TypeError, 'version must be of type String' unless constraints.is_a?(String)
  raise UnexpectedValueError, 'version string must not be empty' if constraints.empty?

  pretty_constraint = constraints

  stabilites = Composer::Package::BasePackage.stabilities.keys.join('|')
  if match = /^([^,\s]*?)@(#{stabilites})$/i.match(constraints)
    constraints = match[1].nil? || match[1].empty? ? '*' : match[1]
  end

  if match = /^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#.+$/i.match(constraints)
    constraints = match[1]
  end

  # or_constraints = preg_split('{\s*\|\|?\s*}', trim(constraints))
  or_constraints = constraints.strip.split(/\s*\|\|?\s*/)
  or_groups = []
  or_constraints.each do |constraints|

    # and_constraints = preg_split('{(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)}', constraints)
    and_constraints = constraints.split(/(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)/)

    if and_constraints.length > 1
        constraint_objects = []
        and_constraints.each do |constraint|
          constraint_objects << parse_constraint(constraint)
        end
    else
      constraint_objects = parse_constraint(and_constraints[0])
    end

    if constraint_objects.length === 1
      constraint = constraint_objects[0]
    else
      constraint = Composer::Package::LinkConstraint::MultiConstraint.new(constraint_objects)
    end

    or_groups << constraint
  end

  if or_groups.length === 1
    constraint = or_groups[0]
  else
    constraint = Composer::Package::LinkConstraint::MultiConstraint.new(or_groups, false)
  end

  constraint.pretty_string = pretty_constraint

  constraint
end

Params:

source

string source package name

source_version

string source package version (pretty version ideally)

description

string link description (e.g. requires, replaces, ..)

links

array An array of package name => constraint mappings

Returns: Link[]



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/composer/package/version/version_parser.rb', line 221

def parse_links(source, source_version, description, links)
  res = {}
  links.each do |target, constraint|
    if 'self.version' === constraint
      parsed_constraint = parse_constraints(source_version)
    else
      parsed_constraint = parse_constraints(constraint)
    end
    res[target.downcase] = Composer::Package::Link.new(
      source,
      target,
      parsed_constraint,
      description,
      constraint
    )
  end
  res
end

#parse_name_version_pairs(pairs) ⇒ Object

Parses a name/version pairs and returns an array of pairs + the

Params:

pairs

array a set of package/version pairs separated by “:”, “=” or “ ”

Returns: array[] array of arrays containing a name and (if provided) a version



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/composer/package/version/version_parser.rb', line 315

def parse_name_version_pairs(pairs)
  pairs = pairs.values
  result = []

  for i in 0..(pairs.length - 1)
    pair = pairs[i].strip!.gsub(/^([^=: ]+)[=: ](.*)$/, '$1 $2')
    if nil === pair.index(' ') && pairs.key?(i + 1) && nil === pairs[i + 1].index('/')
      pair = "#{pair} #{pairs[i + 1]}"
      i = i + 1
    end

    if pair.index(' ')
      name, version = pair.split(' ', 2)
      result << { 'name' => name, 'version' => version }
    else
      result << { 'name' => pair }
    end

  end

  result
end

#parse_numeric_alias_prefix(branch) ⇒ Object

Extract numeric prefix from alias, if it is in numeric format, suitable for version comparison

Params:

branch

string Branch name (e.g. 2.1.x-dev)

Returns: string|false Numeric prefix if present (e.g. 2.1.) or false



301
302
303
304
305
306
# File 'lib/composer/package/version/version_parser.rb', line 301

def parse_numeric_alias_prefix(branch)
  if matches = /^(?<version>(\d+\.)*\d+)(?:\.x)?-dev$/i.match(branch)
    return "#{matches['version']}."
  end
  false
end