library 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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