library 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ if not defined?(Gem)
2
+
3
+ module Gem
4
+ # Some libraries, such as RDoc search through
5
+ # all libraries for plugins using this method.
6
+ # If RubyGems is not being used, then Rolls
7
+ # emulates it.
8
+ #
9
+ # Gem.find_files('rdoc/discover')
10
+ #
11
+ # TODO: Perhaps it should override if it exists
12
+ # and call back to it on failuer?
13
+ def self.find_files(path)
14
+ ::Library.search(path).map{ |f| f.to_s }
15
+ end
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,206 @@
1
+ require 'rbconfig'
2
+ require 'library'
3
+
4
+ # RubyLibrary is a specialized subclass of Library specifically designed
5
+ # to sever Ruby's standard library. It is used to speed up load times for
6
+ # for library files that are standard Ruby scripts and should never be
7
+ # overriden by any 3rd party libraries. Good examples are 'ostruct' and
8
+ # 'optparse'.
9
+ #
10
+ # This class is in the proccess of being refined to exclude certian 3rd
11
+ # party redistributions, such RDoc and Soap4r.
12
+ #
13
+ class RubyLibrary < Library
14
+
15
+ include ::RbConfig
16
+
17
+ #
18
+ # Setup Ruby library.
19
+ #
20
+ def initialize(*) #(location, metadata={})
21
+ #rubylibdir = ::RbConfig::CONFIG['rubylibdir']
22
+ #rubyarchdir = ::RbConfig::CONFIG['archdir']
23
+ #rel_archdir = rubyarchdir.sub(rubylibdir+'/', '')
24
+ #
25
+ #@location = rubylibdir
26
+ #@loadpath = ['', rel_archpath]
27
+
28
+ location = find_base_path(CONFIG.values_at('rubylibdir', 'sitelibdir', 'vendorlibdir'))
29
+ loadpath = CONFIG.values_at(
30
+ 'rubylibdir',
31
+ 'archdir',
32
+ 'sitelibdir',
33
+ 'sitearchdir',
34
+ 'vendorlibdir',
35
+ 'vendorarchdir'
36
+ ).map{ |d| d.sub(location + '/','') }
37
+
38
+ @location = location
39
+ @loadpath = loadpath
40
+ @name = 'ruby'
41
+ @metadata = {} # TODO: can we fillout Ruby's metadata some ?
42
+ end
43
+
44
+ #
45
+ # Then name of RubyLibrary is `ruby`.
46
+ #
47
+ def name
48
+ 'ruby'
49
+ end
50
+
51
+ #
52
+ # Ruby version is RUBY_VERSION.
53
+ #
54
+ def version
55
+ RUBY_VERSION
56
+ end
57
+
58
+ # TODO: Remove rugbygems from $LOAD_PATH and use that?
59
+
60
+ # TODO: Sometimes people add paths directly to $LOAD_PATH,
61
+ # should these be accessible via `ruby/`?
62
+
63
+ #
64
+ # Load path is essentially $LOAD_PATH, less gem paths.
65
+ #
66
+ def loadpath
67
+ #$LOAD_PATH - ['.']
68
+ @loadpath
69
+ end
70
+
71
+ alias load_path loadpath
72
+
73
+ #
74
+ # Release date.
75
+ #
76
+ # @todo This currently just returns current date/time.
77
+ # Is there a way to get Ruby's own release date?
78
+ #
79
+ def date
80
+ Time.now
81
+ end
82
+
83
+ #
84
+ alias released date
85
+
86
+ #
87
+ # Ruby requires nothing.
88
+ #
89
+ def requirements
90
+ []
91
+ end
92
+
93
+ #
94
+ # Ruby needs to ignore a few 3rd party libraries. They will
95
+ # be picked up by the final fallback to Ruby's original require
96
+ # if all else fails.
97
+ #
98
+ def find(file, suffix=true)
99
+ return nil if /^rdoc/ =~ file
100
+ super(file, suffix)
101
+ end
102
+
103
+ #
104
+ # Location of executables, which for Ruby is `RbConfig::CONFIG['bindir']`.
105
+ #
106
+ def bindir
107
+ ::RbConfig::CONFIG['bindir']
108
+ end
109
+
110
+ #
111
+ # Is there a `bin/` location?
112
+ #
113
+ def bindir?
114
+ File.exist?(bindir)
115
+ end
116
+
117
+ #
118
+ # Location of library system configuration files.
119
+ # For Ruby this is `RbConfig::CONFIG['sysconfdir']`.
120
+ #
121
+ def confdir
122
+ ::RbConfig::CONFIG['sysconfdir']
123
+ end
124
+
125
+ #
126
+ # Is there a "`etc`" location?
127
+ #
128
+ def confdir?
129
+ File.exist?(confdir)
130
+ end
131
+
132
+ #
133
+ # Location of library shared data directory. For Ruby this is
134
+ # `RbConfig::CONFIG['datadir']`.
135
+ #
136
+ def datadir
137
+ ::RbConfig::CONFIG['datadir']
138
+ end
139
+
140
+ #
141
+ # Is there a `data/` location?
142
+ #
143
+ def datadir?
144
+ File.exist?(datadir)
145
+ end
146
+
147
+ #
148
+ # Require library +file+ given as a Script instance.
149
+ #
150
+ # @param [String] feature
151
+ # Instance of Feature.
152
+ #
153
+ # @return [Boolean] Success of requiring the feature.
154
+ #
155
+ def require_absolute(feature)
156
+ return false if $".include?(feature.localname) # ruby 1.8 does not use absolutes
157
+ success = super(feature)
158
+ $" << feature.localname # ruby 1.8 does not use absolutes TODO: move up?
159
+ $".uniq!
160
+ success
161
+ end
162
+
163
+ #
164
+ # Load library +file+ given as a Script instance.
165
+ #
166
+ # @param [String] feature
167
+ # Instance of Feature.
168
+ #
169
+ # @return [Boolean] Success of loading the feature.
170
+ #
171
+ def load_absolute(feature, wrap=nil)
172
+ success = super(feature, wrap)
173
+ $" << feature.localname # ruby 1.8 does not use absolutes TODO: move up?
174
+ $".uniq!
175
+ success
176
+ end
177
+
178
+ #
179
+ # The loadpath sorted by largest path first.
180
+ #
181
+ def loadpath_sorted
182
+ loadpath.sort{ |a,b| b.size <=> a.size }
183
+ end
184
+
185
+ #
186
+ # Construct a Script match.
187
+ #
188
+ def libfile(lpath, file, ext=nil)
189
+ Library::Feature.new(self, lpath, file, ext)
190
+ end
191
+
192
+ private
193
+
194
+ # Given an array of path strings, find the longest common prefix path.
195
+ def find_base_path(paths)
196
+ return paths.first if paths.length <= 1
197
+ arr = paths.sort
198
+ f = arr.first.split('/')
199
+ l = arr.last.split('/')
200
+ i = 0
201
+ i += 1 while f[i] == l[i] && i <= f.length
202
+ f.slice(0, i).join('/')
203
+ end
204
+
205
+ end
206
+
@@ -0,0 +1,387 @@
1
+ class Library
2
+
3
+ # The Library::Version class is essentially a tuple (immutable array)
4
+ # with special comparision operators.
5
+ #
6
+ class Version
7
+ include Comparable
8
+ include Enumerable
9
+
10
+ #
11
+ # Convenience alias for ::new.
12
+ #
13
+ def self.[](*args)
14
+ new(*args)
15
+ end
16
+
17
+ # Parses a string constraint returning the operation as a lambda.
18
+ #
19
+ # @param constraint [String]
20
+ # valid version constraint, e.g. ">1.0" and "1.0+"
21
+ #
22
+ # @return [Proc] procedure for making contraint comparisons
23
+ #
24
+ def self.constraint_lambda(constraint)
25
+ op, val = *parse_constraint(constraint)
26
+ lambda do |t|
27
+ case t
28
+ when Version
29
+ t.send(op, val)
30
+ else
31
+ Version.new(t).send(op, val)
32
+ end
33
+ end
34
+ end
35
+
36
+ #
37
+ # Converts a constraint into an operator and value.
38
+ #
39
+ # @param [String]
40
+ # valid version constraint , e.g. "= 2.1.3"
41
+ #
42
+ # @return [Array<String>] operator and version number pair
43
+ #
44
+ def self.parse_constraint(constraint)
45
+ constraint = constraint.strip
46
+ re = %r{^(=~|~>|<=|>=|==|=|<|>)?\s*(\d+(:?\.\S+)*)$}
47
+ if md = re.match(constraint)
48
+ if op = md[1]
49
+ op = '=~' if op == '~>'
50
+ op = '==' if op == '='
51
+ val = new(*md[2].split(/\W+/))
52
+ else
53
+ op = '=='
54
+ val = new(*constraint.split(/\W+/))
55
+ end
56
+ else
57
+ raise ArgumentError, "invalid constraint"
58
+ end
59
+ return op, val
60
+ end
61
+
62
+ #
63
+ # Parse common Hash-based version, i.e. Jeweler format.
64
+ #
65
+ # @param [Hash] version hash
66
+ #
67
+ # @return [Library::Version] instance of Version
68
+ #
69
+ def self.parse_hash(data)
70
+ data = data.inject({}){ |h,(k,v)| h[k.to_sym] = v; h }
71
+ if data[:major]
72
+ vers = data.values_at(:major, :minor, :patch, :build)
73
+ else
74
+ vers = data[:vers] || data[:version]
75
+ end
76
+ new vers
77
+ end
78
+
79
+ #
80
+ # Parse YAML-based version.
81
+ # TODO: deprecate ?
82
+ #
83
+ def parse_yaml(yaml)
84
+ require 'yaml'
85
+ parse_hash( YAML.load(yaml) )
86
+ end
87
+
88
+ #
89
+ # Instantiate new instance of Version.
90
+ #
91
+ def initialize(*args)
92
+ args = args.flatten.compact
93
+ args = args.join('.').split(/\W+/)
94
+ @tuple = args.map{ |i| /^\d+$/ =~ i.to_s ? i.to_i : i }
95
+ end
96
+
97
+ #
98
+ # Returns string representation of version, e.g. "1.0.0".
99
+ #
100
+ # @return [String] version number in dot format
101
+ #
102
+ def to_s
103
+ @tuple.compact.join('.')
104
+ end
105
+
106
+ #
107
+ # Library::Version is not technically a String-type. This is here
108
+ # only becuase `File.join` calls it instead of #to_s.
109
+ #
110
+ # @return [String] version number in dot format
111
+ #
112
+ def to_str
113
+ @tuple.compact.join('.')
114
+ end
115
+
116
+ #
117
+ #def inspect; to_s; end
118
+
119
+ #
120
+ # Access indexed segment of version number.
121
+ # Returns `0` if index is non-existant.
122
+ #
123
+ # @param index [Integer] a segment index of the version
124
+ #
125
+ # @return [Integer, String] version segment
126
+ #
127
+ def [](index)
128
+ @tuple.fetch(index,0)
129
+ end
130
+
131
+ #
132
+ # "Spaceship" comparsion operator.
133
+ #
134
+ # @param other [Library::Version, Array]
135
+ # a Library::Version or equvalent Array to compare
136
+ #
137
+ # @return [Integer]
138
+ # `-1`, `0`, or `1` for less, equal or greater, respectively
139
+ #
140
+ def <=>(other)
141
+ [size, other.size].max.times do |i|
142
+ c = self[i] <=> (other[i] || 0)
143
+ return c if c != 0
144
+ end
145
+ 0
146
+ end
147
+
148
+ #
149
+ # Pessimistic constraint (like '~>' in gems).
150
+ #
151
+ # @param other [Library::Version]
152
+ # another instance of version
153
+ #
154
+ # @return [Boolean] match pessimistic constraint?
155
+ #
156
+ def =~(other)
157
+ #other = other.to_t
158
+ upver = other.tuple.dup
159
+ i = upver.index(0)
160
+ i = upver.size unless i
161
+ upver[i-1] += 1
162
+ self >= other && self < upver
163
+ end
164
+
165
+ #
166
+ # Major is the first number in the version series.
167
+ #
168
+ def major ; @tuple[0] ; end
169
+
170
+ #
171
+ # Minor is the second number in the version series.
172
+ #
173
+ def minor ; @tuple[1] || 0 ; end
174
+
175
+ #
176
+ # Patch is third number in the version series.
177
+ #
178
+ def patch ; @tuple[2] || 0 ; end
179
+
180
+ #
181
+ # Build returns the remaining portions of the version
182
+ # tuple after +patch+ joined by '.'.
183
+ #
184
+ # @return [String] version segments after the 3rd in point-format
185
+ #
186
+ def build
187
+ @tuple[3..-1].join('.')
188
+ end
189
+
190
+ #
191
+ # Iterate over each version segment.
192
+ #
193
+ def each(&block)
194
+ @tuple.each(&block)
195
+ end
196
+
197
+ #
198
+ # Size of version tuple.
199
+ #
200
+ # @return [Integer] number of segments
201
+ #
202
+ def size
203
+ @tuple.size
204
+ end
205
+
206
+ # Delegate to the array.
207
+ #def method_missing(sym, *args, &blk)
208
+ # @tuple.__send__(sym, *args, &blk) rescue super
209
+ #end
210
+
211
+ #
212
+ # Does this version satisfy a given constraint?
213
+ #
214
+ def satisfy?(constraint)
215
+ c = Constraint.parse(constraint)
216
+ send(c.operator, c.number)
217
+ end
218
+
219
+ protected
220
+
221
+ #
222
+ # The internal tuple modeling the version number.
223
+ #
224
+ # @return [Array] internal tuple representing the version
225
+ #
226
+ def tuple
227
+ @tuple
228
+ end
229
+
230
+ end
231
+
232
+ # VersionError is raised when a requested version cannot be found.
233
+ #
234
+ class VersionError < ::RangeError # :nodoc:
235
+ end
236
+
237
+ # VersionConflict is raised when selecting another version
238
+ # of a library when a previous version has already been selected.
239
+ #
240
+ class VersionConflict < ::LoadError # :nodoc:
241
+
242
+ #
243
+ # Setup conflict error instance.
244
+ #
245
+ def initialize(lib1, lib2=nil)
246
+ @lib1 = lib1
247
+ @lib2 = lib2
248
+ end
249
+
250
+ #
251
+ #
252
+ #
253
+ def to_s
254
+ if @lib2
255
+ @lib1.inspect + " vs. " + @lib2.inspect
256
+ else
257
+ "previously selected version -- #{@lib1.inspect}"
258
+ end
259
+ end
260
+
261
+ end
262
+
263
+ class Version
264
+
265
+ # The Constraint class models a single version equality or inequality.
266
+ # It consists of the operator and the version number.
267
+ #--
268
+ # TODO: Please improve me!
269
+ #
270
+ # TODO: This should ultimately replace the class methods of Version::Number.
271
+ #
272
+ # TODO: Do we need to support version "from-to" spans ?
273
+ #++
274
+ class Constraint
275
+
276
+ #
277
+ def self.parse(constraint)
278
+ return constraint if self === constraint
279
+ new(constraint)
280
+ end
281
+
282
+ #
283
+ def self.[](operator, number)
284
+ new([operator, number])
285
+ end
286
+
287
+ #
288
+ def initialize(constraint)
289
+ @operator, @number = parse(constraint || '0+')
290
+
291
+ case constraint
292
+ when Array
293
+ @stamp = "%s %s" % [@operator, @number]
294
+ when String
295
+ @stamp = constraint || '0+'
296
+ end
297
+ end
298
+
299
+ # Constraint operator.
300
+ attr :operator
301
+
302
+ # Verison number.
303
+ attr :number
304
+
305
+ #
306
+ def to_s
307
+ @stamp
308
+ end
309
+
310
+ # Converts the version into a constraint string recognizable
311
+ # by RubyGems.
312
+ #--
313
+ # TODO: Better name Constraint#to_s2.
314
+ #++
315
+ def to_gem_version
316
+ op = (operator == '=~' ? '~>' : operator)
317
+ "%s %s" % [op, number]
318
+ end
319
+
320
+ # Convert constraint to Proc object which can be
321
+ # used to test a version number.
322
+ def to_proc
323
+ lambda do |v|
324
+ n = Version::Number.parse(v)
325
+ n.send(operator, number)
326
+ end
327
+ end
328
+
329
+ #
330
+ def compare(version)
331
+ version.send(operator, number)
332
+ end
333
+
334
+ private
335
+
336
+ #
337
+ def parse(constraint)
338
+ case constraint
339
+ when Array
340
+ op, num = constraint
341
+ when /^(.*?)\~$/
342
+ op, val = "=~", $1
343
+ when /^(.*?)\+$/
344
+ op, val = ">=", $1
345
+ when /^(.*?)\-$/
346
+ op, val = "<", $1
347
+ when /^(=~|~>|<=|>=|==|=|<|>)?\s*(\d+(:?[-.]\w+)*)$/
348
+ if op = $1
349
+ op = '=~' if op == '~>'
350
+ op = '==' if op == '='
351
+ val = $2.split(/\W+/)
352
+ else
353
+ op = '=='
354
+ val = constraint.split(/\W+/)
355
+ end
356
+ else
357
+ raise ArgumentError #constraint.split(/\s+/)
358
+ end
359
+ return op, Version.new(*val) #Version::Number.new(*val)
360
+ end
361
+
362
+ # Parse package entry into name and version constraint.
363
+ #def parse(package)
364
+ # parts = package.strip.split(/\s+/)
365
+ # name = parts.shift
366
+ # vers = parts.empty? ? nil : parts.join(' ')
367
+ # [name, vers]
368
+ #end
369
+
370
+ public
371
+
372
+ # Parses a string constraint returning the operation as a lambda.
373
+ def self.constraint_lambda(constraint)
374
+ new(constraint).to_proc
375
+ end
376
+
377
+ # Parses a string constraint returning the operator and value.
378
+ def self.parse_constraint(constraint)
379
+ c = new(constraint)
380
+ return c.operator, c.number
381
+ end
382
+
383
+ end
384
+
385
+ end
386
+
387
+ end