roll 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+
2
+ t = Time.now
3
+
4
+ require 'roll/library'
5
+
6
+ puts "Load Time: #{Time.now - t} seconds"
7
+
@@ -0,0 +1,12 @@
1
+ require 'roll/library'
2
+
3
+ p Library.list
4
+
5
+
6
+ foolib = Library.open('foo')
7
+
8
+ p foolib
9
+
10
+ foolib.require 'tryme'
11
+ foolib.require 'trymetoo'
12
+
@@ -0,0 +1 @@
1
+ puts "Try Me v1.0"
@@ -0,0 +1 @@
1
+ puts "Try Me v1.1"
@@ -0,0 +1,2 @@
1
+ require 'roll/library'
2
+
@@ -0,0 +1,72 @@
1
+ module Roll
2
+
3
+ # Attributes DSL
4
+ module Attributes
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ #
11
+ def valid?
12
+ begin
13
+ validate
14
+ return true
15
+ rescue ValidationError
16
+ return false
17
+ end
18
+ end
19
+
20
+ #
21
+ def validate
22
+ self.class.validation.each do |message, block|
23
+ raise(ValidationError, message) unless instance_eval(&block)
24
+ end
25
+ end
26
+
27
+ alias_method :assert_valid, :validate
28
+
29
+ #
30
+
31
+ module ClassMethods
32
+
33
+ def instance_attributes
34
+ @@attributes ||= []
35
+ end
36
+
37
+ # Define an attribute.
38
+
39
+ def attr_accessor(name, *aliases)
40
+ instance_attributes << name.to_s
41
+ instance_attributes.uniq!
42
+ super(name)
43
+ aliases.each{ |aliaz| alias_accessor(aliaz, name) }
44
+ end
45
+
46
+ # Define an attribute alias.
47
+
48
+ def alias_accessor(aliaz, name)
49
+ alias_method aliaz, name
50
+ alias_method "#{aliaz}=", "#{name}="
51
+ end
52
+
53
+ def validation
54
+ @@validation ||= []
55
+ end
56
+
57
+ # # Does this class provide open access?
58
+ # def open_access?
59
+ # false
60
+ # end
61
+
62
+ def validate(message, &block)
63
+ validation << [message, block]
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+
70
+ #
71
+ class ValidationError < Exception
72
+ end
@@ -0,0 +1,635 @@
1
+ # TITLE:
2
+ #
3
+ # library.rb
4
+ #
5
+ # LICENSE:
6
+ #
7
+ # Copyright (c)2007 Thomas Sawyer, Ruby License
8
+ #
9
+ # TODO:
10
+ #
11
+ # - Commented out #import for now. Facets' module_require and module_load
12
+ # are probably better. Implement those for Rolls instead. (Yes?)
13
+ # - Maybe add autopackage install system?
14
+ # - What about remotes? What about version tiers when remote requiring?
15
+
16
+ require 'rbconfig'
17
+ require 'fileutils'
18
+ require 'roll/package'
19
+ require 'roll/version'
20
+ #require 'roll/sign'
21
+
22
+ # We need to hold a copy of the original $LOAD_PATH
23
+ # for specified "ruby: ..." loads.
24
+
25
+ $RUBY_PATH = $LOAD_PATH.dup
26
+
27
+ # Locations of rolls-ready libraries.
28
+ # # We could use the site_ruby locations instead, but that creates an odd sort of overlap in
29
+ # # the load paths, and would also promote putting the information file in the lib/ dir. So
30
+ # # I think it's better to use a separate location. (After all site_ruby may just fade away.)
31
+ # $LOAD_SITE = $LOAD_PATH - Config::CONFIG.values_at('rubylibdir', 'archdir') - ['.']
32
+
33
+ rolldir = 'ruby_library'
34
+ sitedir = ::Config::CONFIG['sitedir']
35
+ version = ::Config::CONFIG['ruby_version']
36
+
37
+ $LOAD_SITE = [File.join(File.dirname(sitedir), rolldir, version)] + ENV['ROLL_PATH'].to_s.split(':')
38
+
39
+ # = Library Class
40
+ #
41
+ # The Library class serves as an objecified location in Ruby's load paths.
42
+ #
43
+ # The Library qua class also serves as the library manager, storing a ledger of
44
+ # available libraries.
45
+ #
46
+ # A library is roll-ready when it supplies a {name}-{verison}.roll file in either its
47
+ # base directory or in the base's meta/ directory. The roll file name is specifically
48
+ # designed to make library lookup fast. There is no need for Rolls to open the roll
49
+ # file until an actual version is used. It also gives the most flexability in repository
50
+ # layout. Rolls searches up to three subdirs deep looking for roll files. This is
51
+ # suitable to non-versioned libs, versioned libs, non-versioned subprojects and subprojects,
52
+ # including typical Subversion repository layouts.
53
+
54
+ class Library
55
+
56
+ # Dynamic link extension.
57
+ def self.dlext
58
+ @dlext ||= '.' + ::Config::CONFIG['DLEXT']
59
+ end
60
+
61
+ # Location of rolls-ready libs.
62
+ def self.load_site
63
+ $LOAD_SITE
64
+ end
65
+
66
+ #
67
+ def self.load_path
68
+ $LOAD_PATH
69
+ end
70
+
71
+ #
72
+ def self.ruby_path
73
+ $RUBY_PATH
74
+ end
75
+
76
+ # Name of library.
77
+ attr_reader :name
78
+
79
+ # Version of library. This is a VersionNumber object.
80
+ attr_reader :version
81
+
82
+ # Path to library.
83
+ attr_reader :location
84
+
85
+ # New libray.
86
+ def initialize(location, identity=nil) #name, version, location=nil)
87
+ @location = location
88
+
89
+ if identity
90
+ @name = identity[:name]
91
+ @version = identity[:version]
92
+ @load_path = identity[:load_path] || identity[:load_paths]
93
+ else
94
+ identify(location)
95
+ end
96
+
97
+ raise unless @name
98
+ raise unless @version
99
+
100
+ @identity = identity
101
+
102
+ # TODO Version number needs to be more flexiable in handling non-numeric tuples.
103
+ @version = VersionNumber.new(@version) unless VersionNumber === @version
104
+ end
105
+
106
+ # Identify a library based on it's location.
107
+ def identify(location)
108
+ file = File.join(location,'{,meta/}*.roll')
109
+ if file = Dir.glob(file).first
110
+ @name, @version = file.chomp('.roll').split('-')
111
+ end
112
+ end
113
+
114
+ # Inspection.
115
+ def inspect
116
+ if version
117
+ "#<Library #{name}/#{version}>"
118
+ else
119
+ "#<Library #{name}>"
120
+ end
121
+ end
122
+
123
+ def roll_file
124
+ find = "{,meta/}*.roll" # "{,meta/}#{name}-#{version}.roll"
125
+ file = Dir.glob(File.join(location, find), File::FNM_CASEFOLD).first
126
+ end
127
+
128
+ # Read roll file.
129
+ def load_roll
130
+ return unless location # NEED TO DO THIS BETTER. THIS IS HERE FOR THE ONE 'ruby' CASE.
131
+ raise LoadError unless File.directory?(location)
132
+ glob = File.join(location, "{,meta/}*.roll") # "{,meta/}#{name}-#{version}.roll"
133
+ file = Dir.glob(glob, File::FNM_CASEFOLD).first
134
+ if file
135
+ @roll_file = file
136
+ @roll = Roll::Package.open(file, :name=>name, :version=>version.to_s)
137
+ else
138
+ raise "THIS SHOULD NOT POSSIBLE!"
139
+ #@roll = false #Roll::Package.new # THIS IN NO LONGER POSSIBLE
140
+ end
141
+ #use #?
142
+ return @roll
143
+ end
144
+
145
+ # Does this library have an roll file?
146
+ #def rolled?
147
+ # roll ? true : false
148
+ #end
149
+
150
+ # Does this library have a remote source?
151
+
152
+ #def remote?
153
+ # rolled? and source and pubkey
154
+ #end
155
+
156
+ # Meta information about the libray.
157
+ def roll
158
+ @roll = load_roll if @roll.nil? # If it's false, we don't have one.
159
+ @roll
160
+ end
161
+ alias_method :info, :roll
162
+
163
+ # Compare by version.
164
+ def <=>(other)
165
+ version <=> other.verison
166
+ end
167
+
168
+ # List of subdirectories that are searched when loading libs.
169
+ # In general this should include all the libs internal load paths,
170
+ # so long as there will be no name conflicts between directories.
171
+ def lib_path
172
+ return unless roll # NEED TO DO THIS BETTER. THIS IS HERE FOR THE ONE 'ruby' CASE.
173
+ return @load_path if @load_path
174
+ if roll.load_path
175
+ @load_path = roll.load_path.collect{ |path| File.join(location, path) }
176
+ else
177
+ @load_path = ['lib'] # ["lib/#{name}"]
178
+ end
179
+ end
180
+ alias_method :load_path, :lib_path
181
+ alias_method :load_paths, :lib_path
182
+
183
+ # List of subdirectories that are searched for binaries.
184
+ #++
185
+ # TODO I think it would be better just to make this a fix
186
+ # convention that bin/ is always the place to put these.
187
+ # (Unlike lib/ which needs to be more flexable.)
188
+ #--
189
+ def bin_path
190
+ return @bin_path if @bin_path
191
+ return [] unless location # NEED TO DO THIS BETTER.
192
+ if roll.bin_path
193
+ @bin_path = roll.bin_path.collect{ |path| File.join(location, path) }
194
+ else
195
+ if File.directory?(File.join(location, 'bin'))
196
+ @bin_path = [File.join(location, 'bin')]
197
+ else
198
+ @bin_path = []
199
+ end
200
+ end
201
+ return @bin_path
202
+ end
203
+
204
+ # Return the path to the data directory associated with the given
205
+ # package name. Normally this is just
206
+ # "#{Config::CONFIG['datadir']}/#{package_name}", but may be
207
+ # modified by packages like RubyGems and Rolls to handle
208
+ # versioned data directories.
209
+ def datadir(versionless=false)
210
+ if version and not versionless
211
+ File.join(Config::CONFIG['datadir'], name, version)
212
+ else
213
+ File.join(Config::CONFIG['datadir'], name)
214
+ end
215
+ end
216
+
217
+ # Return the path to the configuration directory.
218
+ #--
219
+ # Can or should configuration directories be versioned?
220
+ #++
221
+ def confdir
222
+ File.join(location, 'data')
223
+ #if version
224
+ # File.join(Config::CONFIG['confdir'], name, version)
225
+ #else
226
+ # File.join(Config::CONFIG['datadir'], name)
227
+ #end
228
+ end
229
+
230
+ # Require find.
231
+ def require_find(file)
232
+ file = roll.index_file if (file.nil? or file.empty?)
233
+ glob = File.join('{' + load_path.join(',') + '}', file + "{.rb,#{Library.dlext},}")
234
+ Dir.glob(glob).first
235
+ end
236
+
237
+ # Load find.
238
+ def load_find(file)
239
+ file = roll.index_file if (file.nil? or file.empty?)
240
+ glob = File.join('{' + load_path.join(',') + '}', file)
241
+ Dir.glob(glob).first
242
+ end
243
+
244
+ # Library specific #require.
245
+ def require(file)
246
+ if path = require_find(file)
247
+ Kernel.require(path)
248
+ else
249
+ raise LoadError, "no such file to load -- #{name}:#{file}"
250
+ end
251
+ end
252
+
253
+ # Library specific load.
254
+ def load(file, wrap=nil)
255
+ if path = load_find(file)
256
+ Kernel.load(path, wrap)
257
+ else
258
+ raise LoadError, "no such file to load -- #{name}:#{file}"
259
+ end
260
+ end
261
+
262
+ # Library specific autoload.
263
+ def autoload(base, file)
264
+ if path = require_find(file)
265
+ Kernel.autoload(base, file)
266
+ else
267
+ raise LoadError, "no such file to autoload -- #{name}:#{file}"
268
+ end
269
+ end
270
+
271
+ # Require into module.
272
+ def module_require(mod, file)
273
+ if path = require_find(file)
274
+ mod.module_require(path) # FIXME
275
+ else
276
+ raise LoadError, "no such file to load -- #{name}:#{file}"
277
+ end
278
+ end
279
+
280
+ # Load into module.
281
+ def module_load(mod, file)
282
+ if path = load_find(file)
283
+ mod.module_load(path) # FIXME
284
+ else
285
+ raise LoadError, "no such file to load -- #{name}:#{file}"
286
+ end
287
+ end
288
+
289
+ # Library specific autoload for module.
290
+ def module_autoload(mod, base, file)
291
+ if path = require_find(file)
292
+ mod.autoload_without_roll(base, file)
293
+ else
294
+ raise LoadError, "no such file to autoload -- #{name}:#{file}"
295
+ end
296
+ end
297
+
298
+ # Put the libs load paths into the global lookup.
299
+ #--
300
+ # TODO Maybe call 'import' instead?
301
+ #++
302
+ def utilize
303
+ lib_path.each do |path|
304
+ Library.load_path.unshift(path)
305
+ end
306
+ Library.load_path.uniq!
307
+ self
308
+ end
309
+
310
+ # Class instance variable @ledger stores the library references.
311
+
312
+ @ledger = {}
313
+
314
+ class << self
315
+
316
+ # Scan the site locations for libraries.
317
+ def scan
318
+ # First we add Ruby core and standard libraries.
319
+ @ledger['ruby'] = Library.new(nil, :name=>'ruby', :version=>VERSION, :load_path=>Library.ruby_path)
320
+
321
+ scan_working() if $DEBUG
322
+
323
+ projs1 = Dir.glob( '{' + $LOAD_SITE.join(',') + '}/*{/meta,}/*.roll', File::FNM_CASEFOLD )
324
+ projs2 = Dir.glob( '{' + $LOAD_SITE.join(',') + '}/*/*{/meta,}/*.roll', File::FNM_CASEFOLD )
325
+ projs3 = Dir.glob( '{' + $LOAD_SITE.join(',') + '}/*/*/*{/meta,}/*.roll', File::FNM_CASEFOLD )
326
+
327
+ projs = projs1 + projs2 + projs3
328
+
329
+ #dirs = projs.collect do |prj|
330
+ # metafile = File.basename(prj)
331
+ # dir = File.dirname(prj)
332
+ # dir = File.dirname(dir) if dir =~ /meta$/
333
+ # dir
334
+ #end
335
+
336
+ projs.uniq!
337
+
338
+ #dirs -= $LOAD_SITE
339
+
340
+ projs.each do |proj|
341
+ name, ver = *File.basename(proj).chomp('.roll').split('-')
342
+ dir = File.dirname(proj)
343
+ dir = File.dirname(dir) if File.basename(dir) == 'meta'
344
+
345
+ name = name.downcase
346
+
347
+ #if versions.empty?
348
+ # @ledger[name] ||= Library.new(dir, :name=>name, :version=>'0') #Version.new('0', dir)
349
+ #else
350
+ @ledger[name] ||= []
351
+ @ledger[name] << Library.new(dir, :name=>name, :version=>ver)
352
+ #end
353
+ end
354
+ end
355
+
356
+ # Scan current working location to see if there's
357
+ # a library. This will ascend from the current
358
+ # working directy to one level below root looking
359
+ # for a lib/ directory.
360
+ #--
361
+ # TODO CHANGE TO LOOK FRO INDEX FILE.
362
+ #++
363
+ def scan_working
364
+ paths = Dir.pwd.split('/')
365
+ (paths.size-1).downto(1) do |n|
366
+ dir = File.join( *(paths.slice(0..n) << 'lib') )
367
+ if File.directory? dir
368
+ $LOAD_SITE.unshift dir
369
+ end
370
+ end
371
+ end
372
+
373
+ # Return a list of library names.
374
+ def list
375
+ @ledger.keys
376
+ end
377
+
378
+ # Libraries are Singleton pattern.
379
+ def instance(name, constraint=nil)
380
+ name = name.to_s
381
+ #raise "no library -- #{name}" unless @ledger.include?( name )
382
+ return nil unless @ledger.include?(name)
383
+ case lib = @ledger[name]
384
+ when Library
385
+ return lib unless constraint
386
+ raise VersionConflict, "previously selected library version" # -- #{lib.version}"
387
+ #when Version
388
+ # @ledger[name] = new(lib.location, :name=>name, :version=>lib) #new(name, lib, lib.location)
389
+ when Array
390
+ if constraint
391
+ compare = VersionNumber.constrant_lambda(constraint)
392
+ version = lib.select(&compare).max
393
+ else
394
+ version = lib.max
395
+ end
396
+ unless version
397
+ raise VersionError, "no library version -- #{name} #{constraint}"
398
+ end
399
+ @ledger[name] = version #new(version.location, :name=>name, :version=>version) #new(name, version, version.location)
400
+ else
401
+ raise "this should never happen"
402
+ end
403
+ @ledger[name].roll # Make sure the roll file is loaded.
404
+ @ledger[name]
405
+ end
406
+
407
+ # A shortcut for #instance.
408
+ alias_method :[], :instance
409
+
410
+ # Same as #instance but will raise and error if the library is not found.
411
+ def open(name, constraint=nil, &yld)
412
+ lib = instance(name, constraint)
413
+ unless lib
414
+ raise LoadError, "no library -- #{name}"
415
+ end
416
+ yield(lib) if yld
417
+ lib
418
+ end
419
+
420
+ end
421
+
422
+ # VersionError is raised when a requested version cannot be found.
423
+ class VersionError < ::RangeError # :nodoc:
424
+ end
425
+
426
+ # VersionConflict is raised when selecting another version
427
+ # of a library when a previous version has already been selected.
428
+ class VersionConflict < ::LoadError # :nodoc:
429
+ end
430
+ end
431
+
432
+
433
+ module ::Config
434
+ # Return the path to the data directory associated with the given
435
+ # package name. Normally this is just
436
+ # "#{Config::CONFIG['datadir']}/#{package_name}", but may be
437
+ # modified by packages like RubyGems and Rolls to handle
438
+ # versioned data directories.
439
+ def self.datadir(name, versionless=false)
440
+ if lib = Library.instance( name )
441
+ lib.datadir( versionless )
442
+ else
443
+ File.join(CONFIG['datadir'], name)
444
+ end
445
+ end
446
+
447
+ # Return the path to the configuration directory.
448
+ def self.confdir(name)
449
+ if lib = Library.instance( name )
450
+ lib.confdir
451
+ else
452
+ File.join(CONFIG['datadir'], name)
453
+ end
454
+ end
455
+ end
456
+
457
+
458
+ module ::Kernel
459
+
460
+ # Activate a library.
461
+ def library(name, constraint=nil)
462
+ Library.open(name, constraint)
463
+ end
464
+ module_function :library
465
+
466
+ # Use library. This activates a library, and adds
467
+ # it's load_path to the global $LOAD_PATH.
468
+ #--
469
+ # Maybe call #import instead ?
470
+ #++
471
+ def use(name, constraint=nil)
472
+ Library.open(name, constraint).utilize
473
+ end
474
+
475
+
476
+ def self.parse_load_parameters(file)
477
+ if must = file.index(':')
478
+ name, path = file.split(':')
479
+ else
480
+ name, *rest = file.split('/')
481
+ path = File.join(*rest)
482
+ end
483
+ name = nil if name == ''
484
+ lib = name ? Library.instance(name) : nil
485
+ raise LoadError, "no library found -- #{file}" if must && !lib
486
+ return lib, path
487
+ end
488
+
489
+ # Rolls requires a modification to #require and #load.
490
+ # So that it is not neccessary to make the library() call
491
+ # if you just want the latest version.
492
+ #
493
+ # This would be a bit simpler if we mandated the
494
+ # use of the ':' notation when specifying the library
495
+ # name. Use of the ':' is robust. But we cannot do this
496
+ # w/o loosing backward-compatability. Using '/' in its
497
+ # place has the potential for pathname clashing, albeit
498
+ # the likelihood is small. There are two ways to bypass
499
+ # the problem if it arises. Use 'ruby:{path}' if the
500
+ # conflicting lib is a ruby core or standard library.
501
+ # Use ':{path}' to bypass Roll system altogether.
502
+
503
+ # # Require script.
504
+ # def require(file)
505
+ # if file.index(':')
506
+ # name, file = file.split(':')
507
+ # if name == ''
508
+ # Kernel.require(file)
509
+ # #if lib == 'ruby'
510
+ # # Ruby.require(file)
511
+ # elsif lib = Library.instance(name)
512
+ # lib.require(file)
513
+ # else
514
+ # raise LoadError, "no library found -- #{name}"
515
+ # end
516
+ # else
517
+ # name, *rest = file.split('/')
518
+ # if lib = Library.instance(name)
519
+ # lib.require(File.join(*rest))
520
+ # else
521
+ # Kernel.require(file)
522
+ # end
523
+ # end
524
+ # end
525
+
526
+ # Require script.
527
+ def require(file)
528
+ lib, path = *Kernel.parse_load_parameters(file)
529
+ if lib
530
+ lib.require(path)
531
+ else
532
+ Kernel.require(file)
533
+ end
534
+ end
535
+
536
+ # # Load script.
537
+ # def load(file, wrap=nil)
538
+ # if file.index(':')
539
+ # name, file = file.split(':')
540
+ # if name == ''
541
+ # Kernel.require(file)
542
+ # #if lib == 'ruby'
543
+ # # Ruby.load(file, safe)
544
+ # elsif lib = Library.instance(name)
545
+ # lib.load(file, wrap)
546
+ # else
547
+ # raise LoadError, "no library found -- #{name}"
548
+ # end
549
+ # else
550
+ # name, *rest = file.split('/')
551
+ # if lib = Library.instance(name)
552
+ # lib.load(File.join(*rest))
553
+ # else
554
+ # Kernel.load(file, wrap)
555
+ # end
556
+ # end
557
+ # end
558
+
559
+ # Require script.
560
+ def load(file, wrap=false)
561
+ lib, path = *Kernel.parse_load_parameters(file)
562
+ if lib
563
+ lib.load(path, wrap)
564
+ else
565
+ Kernel.load(file, wrap)
566
+ end
567
+ end
568
+
569
+ # #
570
+ # def autoload(base, file)
571
+ # if file.index(':')
572
+ # name, file = file.split(':')
573
+ # if name == ''
574
+ # Kernel.autoload(base, file)
575
+ # elsif lib = Library.instance(name)
576
+ # lib.autoload(base, file)
577
+ # else
578
+ # raise LoadError, "no library found -- #{name}"
579
+ # end
580
+ # else
581
+ # name, *rest = file.split('/')
582
+ # if lib = Library.instance(name)
583
+ # lib.autoload(base, File.join(*rest))
584
+ # else
585
+ # Kernel.autoload(base, file)
586
+ # end
587
+ # end
588
+ # end
589
+
590
+ =begin
591
+ # Autoload script.
592
+ def autoload(base, file)
593
+ lib, path = *Kernel.parse_load_parameters(file)
594
+ if lib
595
+ lib.autoload(base, path)
596
+ else
597
+ Kernel.autoload(base, file)
598
+ end
599
+ end
600
+ =end
601
+
602
+ end
603
+
604
+ # TODO
605
+ class ::Module
606
+
607
+ =begin
608
+ alias_method :autoload_without_roll, :autoload
609
+
610
+ #
611
+ def autoload(base, file)
612
+ lib, path = *Kernel.parse_load_parameters(file)
613
+ if lib
614
+ lib.module_autoload(self, base, path)
615
+ else
616
+ autoload_without_roll(base, file)
617
+ end
618
+ end
619
+ =end
620
+
621
+ # TODO
622
+
623
+ # Adds importing to Module class called against +self+ as default.
624
+
625
+ # def module_require()
626
+ # end
627
+
628
+ # def module_load()
629
+ # end
630
+
631
+ end
632
+
633
+ # Prime the library ledger.
634
+ Library.scan
635
+