1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
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
93
94
95
96
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
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
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
221
222
223
224
225
226
227
228
|
class VersionElement
include Comparable
def initialize elem
elem = elem.to_s.downcase
@elem = case elem
when 'a', 'alpha' then 'alpha'
when 'b', 'beta' then 'beta'
when /\d+/ then elem.to_i
else elem
end
end
def <=>(other)
return unless other.is_a? VersionElement
return -1 if string? and other.numeric?
return 1 if numeric? and other.string?
return elem <=> other.elem
end
def to_s
@elem.to_s
end
protected
attr_reader :elem
def string?
elem.is_a? String
end
def numeric?
elem.is_a? Numeric
end
end
class Version
include Comparable
def initialize val, detected=false
@version = val.to_s
@detected_from_url = detected
end
def detected_from_url?
@detected_from_url
end
def head?
@version == 'HEAD'
end
def devel?
alpha? or beta? or rc?
end
def alpha?
to_a.any? { |e| e.to_s == 'alpha' }
end
def beta?
to_a.any? { |e| e.to_s == 'beta' }
end
def rc?
to_a.any? { |e| e.to_s == 'rc' }
end
def <=>(other)
# Return nil if objects aren't comparable
return unless other.is_a? Version
# Versions are equal if both are HEAD
return 0 if head? and other.head?
# HEAD is greater than any numerical version
return 1 if head? and not other.head?
return -1 if not head? and other.head?
stuple, otuple = to_a, other.to_a
max = [stuple.length, otuple.length].max
stuple.fill(VersionElement.new(0), stuple.length, max - stuple.length)
otuple.fill(VersionElement.new(0), otuple.length, max - otuple.length)
stuple <=> otuple
end
def to_s
@version
end
alias_method :to_str, :to_s
def self.parse spec
version = _parse(spec)
Version.new(version, true) unless version.nil?
end
protected
def to_a
@array ||= @version.scan(/\d+|[a-zA-Z]+/).map { |e| VersionElement.new(e) }
end
private
def self._parse spec
spec = Pathname.new(spec) unless spec.is_a? Pathname
stem = if spec.directory?
spec.basename.to_s
elsif %r[((?:sourceforge.net|sf.net)/.*)/download$].match(spec.to_s)
Pathname.new(spec.dirname).stem
else
spec.stem
end
# GitHub tarballs, e.g. v1.2.3
m = %r[github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+)$].match(spec.to_s)
return m.captures.first unless m.nil?
# e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4
m = %r[github.com/.+/(?:zip|tar)ball/.*-((\d+\.)+\d+)$].match(spec.to_s)
return m.captures.first unless m.nil?
# e.g. https://github.com/isaacs/npm/tarball/v0.2.5-1
m = %r[github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+-(\d+))$].match(spec.to_s)
return m.captures.first unless m.nil?
# e.g. https://github.com/petdance/ack/tarball/1.93_02
m = %r[github.com/.+/(?:zip|tar)ball/v?((\d+\.)+\d+_(\d+))$].match(spec.to_s)
return m.captures.first unless m.nil?
# e.g. https://github.com/erlang/otp/tarball/OTP_R15B01 (erlang style)
m = /[-_](R\d+[AB]\d*(-\d+)?)/.match(spec.to_s)
return m.captures.first unless m.nil?
# e.g. boost_1_39_0
m = /((\d+_)+\d+)$/.match(stem)
return m.captures.first.gsub('_', '.') unless m.nil?
# e.g. foobar-4.5.1-1
# e.g. ruby-1.9.1-p243
m = /-((\d+\.)*\d\.\d+-(p|rc|RC)?\d+)(?:[-._](?:bin|dist|stable|src|sources))?$/.match(stem)
return m.captures.first unless m.nil?
# e.g. lame-398-1
m = /-((\d)+-\d)/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.1
m = /-((\d+\.)*\d+)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.1b
m = /-((\d+\.)*\d+([abc]|rc|RC)\d*)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.0-beta1, or foobar-4.50-beta
m = /-((\d+\.)*\d+-beta(\d+)?)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar4.5.1
m = /((\d+\.)*\d+)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.0-bin
m = /-((\d+\.)+\d+[abc]?)[-._](bin|dist|stable|src|sources?)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. dash_0.5.5.1.orig.tar.gz (Debian style)
m = /_((\d+\.)+\d+[abc]?)[.]orig$/.match(stem)
return m.captures.first unless m.nil?
# e.g. http://www.openssl.org/source/openssl-0.9.8s.tar.gz
m = /-([^-]+)/.match(stem)
return m.captures.first unless m.nil?
# e.g. astyle_1.23_macosx.tar.gz
m = /_([^_]+)/.match(stem)
return m.captures.first unless m.nil?
# e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
m = /\/(\d\.\d+)\//.match(spec.to_s)
return m.captures.first unless m.nil?
end
# DSL for defining comparators
class << self
def compare &blk
send(:define_method, '<=>', &blk)
end
end
end
class VersionSchemeDetector
def initialize scheme
@scheme = scheme
end
def detect
if @scheme.is_a? Class and @scheme.ancestors.include? Version
@scheme
elsif @scheme.is_a? Symbol then detect_from_symbol
else
raise "Unknown version scheme #{@scheme} was requested."
end
end
private
def detect_from_symbol
raise "Unknown version scheme #{@scheme} was requested."
end
end
# Enable things like "MacOS.version >= :lion"
class MacOSVersion < Version
compare do |other|
super Version.new case other
when :mountain_lion then 10.8
when :lion then 10.7
when :snow_leopard then 10.6
when :leopard then 10.5
else other.to_s
end
end
end
|