Class: Library::Ledger
- Inherits:
-
Object
- Object
- Library::Ledger
- Includes:
- Enumerable
- Defined in:
- lib/library/ledger.rb
Overview
Ledger class track available libraries by library name. It is essentially a hash object, but with a special way of storing them to track versions. Each have key is the name of a library, as a String, and each value is either a Library object, if that particular version is active, or an Array of available versions of the library if inactive.
Instance Method Summary collapse
-
#[](name) ⇒ Library, Array
Get library or library version set by name.
-
#[]=(name, library) ⇒ Object
Set ledger entry.
-
#acivate_requirements(library, development = false, checklist = {}) ⇒ Object
private
Activate library requirements.
-
#activate(name, constraint = nil) ⇒ Library
Activate a library, retrieving a Library instance by name, or name and version, and ensuring only that instance will be returned for all subsequent requests.
-
#add(lib) ⇒ Library
(also: #<<)
Add a library to the ledger.
-
#add_library(library) ⇒ Library
Add library to ledger given a Library object.
-
#add_location(location) ⇒ Library
Add library to ledger given a location.
-
#constrain(name, contraint) ⇒ Array
Limit versions of a library to the given constraint.
-
#dotruby?(path) ⇒ Boolean
private
Does the directory have a ‘.ruby` file?.
-
#each(&block) ⇒ Object
Iterate over each ledger entry.
-
#expound_paths(*entries) ⇒ Object
private
For flexible priming, this method can be used to recursively lookup library locations.
-
#find_any(path, options = {}) ⇒ Feature, Array
Brute force variation of ‘#find` looks through all libraries for a matching features.
-
#find_feature(path, options = {}) ⇒ Object
Find matching library features.
-
#gemspec?(path) ⇒ Boolean
private
Does a path have a ‘.gemspec` file? This is fallback measure if a .ruby file is not found.
-
#glob(match, options = {}) ⇒ Array
Search for all matching library files that match the given pattern.
-
#initialize ⇒ Ledger
constructor
A new instance of Ledger.
-
#inspect ⇒ String
Inspection string.
-
#isolate(name, constraint = nil) ⇒ Ledger
Reduce the ledger to only those libraries the given library requires.
-
#key?(name) ⇒ Boolean
Checks ledger for presents of library by name.
-
#keys ⇒ Array<String>
Get a list of names of all libraries in the ledger.
-
#library_path?(path) ⇒ Boolean
private
Is a directory a Ruby library?.
-
#monitor? ⇒ Boolean
State of monitoring setting.
-
#prime(*paths) ⇒ Ledger
Load up the ledger with a given set of paths and add an instance of the special ‘RubyLibrary` class after that.
- #replace(table) ⇒ Object
-
#search(path, options = {}) ⇒ Feature, Array
Brute force search looks through all libraries for matching features.
-
#size ⇒ Fixnum
Size of the ledger is the number of libraries available.
-
#values ⇒ Array<Library,Array>
Get a list of libraries and library version sets in the ledger.
Constructor Details
Instance Method Details
#[](name) ⇒ Library, Array
Get library or library version set by name.
97 98 99 |
# File 'lib/library/ledger.rb', line 97 def [](name) @table[name.to_s] end |
#[]=(name, library) ⇒ Object
Set ledger entry.
108 109 110 111 112 |
# File 'lib/library/ledger.rb', line 108 def []=(name, library) raise TypeError unless Library === library @table[name.to_s] = library end |
#acivate_requirements(library, development = false, checklist = {}) ⇒ Object (private)
Activate library requirements.
@todo: checklist is used to prevent possible infinite recursion, but
it might be better to put a flag in Library instead.
610 611 612 613 614 615 616 617 618 619 620 621 622 623 |
# File 'lib/library/ledger.rb', line 610 def acivate_requirements(library, development=false, checklist={}) reqs = development ? library.requirements : library.runtime_requirements checklist[library] = true reqs.each do |req| name = req['name'] vers = req['version'] library = activate(name, vers) acivate_requirements(library, development, checklist) unless checklist[library] end end |
#activate(name, constraint = nil) ⇒ Library
Should we also check $“? Eg. ‘return false if $”.include?(path)`.
Activate a library, retrieving a Library instance by name, or name and version, and ensuring only that instance will be returned for all subsequent requests. Libraries are singleton, so once activated the same object is always returned.
This method will raise a LoadError if the name is not found.
Note that activating all runtime requirements of a library being activated was considered, but decided against. There’s no reason to activatea library until it is actually needed. However this is not so when testing, or verifying available requirements, so other methods are provided such as ‘#activate_requirements`.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/library/ledger.rb', line 227 def activate(name, constraint=nil) raise LoadError, "no such library -- #{name}" unless key?(name) library = self[name] if Library === library if constraint unless library.version.satisfy?(constraint) raise Library::VersionConflict, library end end else # library is an array of versions if constraint verscon = Version::Constraint.parse(constraint) library = library.select{ |lib| verscon.compare(lib.version) }.max else library = library.max end unless library raise VersionError, "no library version -- #{name} #{constraint}" end self[name] = library #constrain(library) end library end |
#add(lib) ⇒ Library Also known as: <<
Add a library to the ledger.
34 35 36 37 38 39 40 41 |
# File 'lib/library/ledger.rb', line 34 def add(lib) case lib when Library add_library(lib) else add_location(lib) end end |
#add_library(library) ⇒ Library
Add library to ledger given a Library object.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/library/ledger.rb', line 73 def add_library(library) #begin raise TypeError unless Library === library entry = @table[library.name] if Array === entry entry << library unless entry.include?(library) end #rescue Exception => error # warn error.message if ENV['debug'] #end library end |
#add_location(location) ⇒ Library
Add library to ledger given a location.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/library/ledger.rb', line 50 def add_location(location) begin library = Library.new(location) entry = @table[library.name] if Array === entry entry << library unless entry.include?(library) else # todo: what to do here? end rescue Exception => error warn error. if ENV['debug'] end library end |
#constrain(name, contraint) ⇒ Array
Limit versions of a library to the given constraint. Unlike ‘#activate` this does not reduce the possible versions to a single library, but only reduces the number of possibilites.
190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/library/ledger.rb', line 190 def constrain(name, contraint) libraries = self[name] return nil unless Array === libraries vers = libraries.select do |library| library.version.satisfy?(constraint) end self[name] = vers end |
#dotruby?(path) ⇒ Boolean (private)
Does the directory have a ‘.ruby` file?
628 629 630 |
# File 'lib/library/ledger.rb', line 628 def dotruby?(path) File.file?(File.join(path, '.ruby')) end |
#each(&block) ⇒ Object
Iterate over each ledger entry.
127 128 129 |
# File 'lib/library/ledger.rb', line 127 def each(&block) @table.each(&block) end |
#expound_paths(*entries) ⇒ Object (private)
For flexible priming, this method can be used to recursively lookup library locations.
If a given path is a file, it will considered a lookup “roll”, such that each line entry in the file is considered another path to be expounded upon.
If a given path is a directory, it will be returned if it is a valid Ruby library location, otherwise each subdirectory will be checked to see if it is a valid Ruby library location, and returned if so.
If, on the other hand, a given path is a file glob pattern, the pattern will be expanded and those paths will expounded upon in turn.
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/library/ledger.rb', line 559 def expound_paths(*entries) paths = [] entries.each do |entry| entry = entry.strip next if entry.empty? next if entry.start_with?('#') if File.directory?(entry) if library_path?(entry) paths << entry else subpaths = Dir.glob(File.join(entry, '*/')) subpaths.each do |subpath| paths << subpath if library_path?(subpath) end end elsif File.file?(entry) paths.concat(expound_paths(*File.readlines(entry))) else glob_paths = Dir.glob(entry) if glob_paths.first != entry paths.concat(expound_paths(*glob_paths)) end end end paths end |
#find_any(path, options = {}) ⇒ Feature, Array
Brute force variation of ‘#find` looks through all libraries for a matching features. This serves as the fallback method if `#find` comes up empty.
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/library/ledger.rb', line 360 def find_any(path, ={}) = .merge(:main=>true) latest = [:latest] # TODO: Perhaps the selected and unselected should be kept in separate lists? unselected, selected = *partition{ |name, libs| Array === libs } # broad search of pre-selected libraries selected.each do |(name, lib)| if ftr = lib.find(path, ) next if Library.load_stack.last == ftr return ftr end end # finally a broad search on unselected libraries unselected.each do |(name, libs)| libs = libs.sort libs = [libs.first] if latest libs.each do |lib| ftr = lib.find(path, ) return ftr if ftr end end nil end |
#find_feature(path, options = {}) ⇒ Object
Find matching library features. This is the “mac daddy” method used by the #require and #load methods to find the specified path
among the various libraries and their load paths.
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 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/library/ledger.rb', line 260 def find_feature(path, ={}) path = path.to_s #suffix = options[:suffix] search = [:search] local = [:local] from = [:from] $stderr.print path if monitor? # debugging # absolute, home or current path # # NOTE: Ideally we would try to find a matching path among avaliable libraries # so that the library can be activated, however this would probably add a # too much overhead and will by mostly a YAGNI, so we forgo any such # functionality, at least for now. case path[0,1] when '/', '~', '.' $stderr.puts " (absolute)" if monitor? # debugging return nil end # from explicit library if from lib = library(from) ftr = lib.find(path, ) raise LoadError, "no such file to load -- #{path}" unless file $stderr.puts " (direct)" if monitor? # debugging return ftr end # check the load stack (TODO: just last or all?) if local if last = $LOAD_STACK.last #$LOAD_STACK.reverse_each do |feature| lib = last.library if ftr = lib.find(path, ) unless $LOAD_STACK.include?(ftr) # prevent recursive loading $stderr.puts " (2 stack)" if monitor? # debugging return ftr end end end end name, fname = ::File.split_root(path) # if the head of the path is the library if fname lib = Library[name] if lib && ftr = lib.find(path, ) || lib.find(fname, ) $stderr.puts " (3 indirect)" if monitor? # debugging return ftr end end # plain library name? if !fname && lib = Library.instance(path) if ftr = lib.default # default feature to load $stderr.puts " (5 plain library name)" if monitor? # debugging return ftr end end # fallback to brute force search #if search #or legacy #options[:legacy] = true if ftr = find_any(path, ) $stderr.puts " (6 brute search)" if monitor? # debugging return ftr end #end $stderr.puts " (7 fallback)" if monitor? # debugging nil end |
#gemspec?(path) ⇒ Boolean (private)
Does a path have a ‘.gemspec` file? This is fallback measure if a .ruby file is not found.
635 636 637 638 |
# File 'lib/library/ledger.rb', line 635 def gemspec?(path) glob = File.file?(File.join(path, '{,*}.gemspec')) Dir[glob].first end |
#glob(match, options = {}) ⇒ Array
Should this return list of Feature objects instead of file paths?
Search for all matching library files that match the given pattern. This could be of useful for plugin loader.
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/library/ledger.rb', line 456 def glob(match, ={}) latest = [:latest] matches = [] each do |name, libs| case libs when Array libs = libs.sort libs = [libs.first] if latest else libs = [libs] end libs.each do |lib| lib.loadpath.each do |path| find = File.join(lib.location, path, match) list = Dir.glob(find) list = list.map{ |d| d.chomp('/') } matches.concat(list) end end end matches end |
#inspect ⇒ String
Inspection string.
173 174 175 |
# File 'lib/library/ledger.rb', line 173 def inspect @table.inspect end |
#isolate(name, constraint = nil) ⇒ Ledger
Reduce the ledger to only those libraries the given library requires.
494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/library/ledger.rb', line 494 def isolate(name, constraint=nil) library = activate(name, constraint) # TODO: shouldn't this be done in #activate ? acivate_requirements(library) unused = [] each do |name, libs| ununsed << name if Array === libs end unused.each{ |name| @table.delete(name) } self end |
#key?(name) ⇒ Boolean
Checks ledger for presents of library by name.
145 146 147 |
# File 'lib/library/ledger.rb', line 145 def key?(name) @table.key?(name.to_s) end |
#keys ⇒ Array<String>
Get a list of names of all libraries in the ledger.
154 155 156 |
# File 'lib/library/ledger.rb', line 154 def keys @table.keys end |
#library_path?(path) ⇒ Boolean (private)
Support gem home location.
Is a directory a Ruby library?
595 596 597 |
# File 'lib/library/ledger.rb', line 595 def library_path?(path) dotruby?(path) || (ENV['RUBYLIBS_GEMSPEC'] && gemspec?(path)) end |
#monitor? ⇒ Boolean
State of monitoring setting. This is used for debugging.
17 18 19 |
# File 'lib/library/ledger.rb', line 17 def monitor? ENV['monitor'] || $MONITOR end |
#prime(*paths) ⇒ Ledger
Load up the ledger with a given set of paths and add an instance of the special ‘RubyLibrary` class after that.
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'lib/library/ledger.rb', line 520 def prime(*paths) = Hash === paths.last ? paths.pop : {} paths = expound_paths(*paths) if [:expound] require 'library/rubylib' paths.each do |path| begin add_location(path) if library_path?(path) rescue => err $stderr.puts err. if ENV['debug'] end end add_library(RubyLibrary.new) self end |
#replace(table) ⇒ Object
117 118 119 120 121 122 |
# File 'lib/library/ledger.rb', line 117 def replace(table) initialize table.each do |name, value| @table[name.to_s] = value end end |
#search(path, options = {}) ⇒ Feature, Array
Brute force search looks through all libraries for matching features. This is the same as #find_any, but returns a list of matches rather then the first matching feature found.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/library/ledger.rb', line 411 def search(path, ={}) = .merge(:main=>true) latest = [:latest] matches = [] # TODO: Perhaps the selected and unselected should be kept in separate lists? unselected, selected = *partition{ |name, libs| Array === libs } # broad search of pre-selected libraries selected.each do |(name, lib)| if ftr = lib.find(path, ) next if Library.load_stack.last == ftr matches << ftr end end # finally a broad search on unselected libraries unselected.each do |(name, libs)| libs = [libs.sort.first] if latest libs.each do |lib| ftr = lib.find(path, ) matches << ftr if ftr end end matches.uniq end |
#size ⇒ Fixnum
Size of the ledger is the number of libraries available.
136 137 138 |
# File 'lib/library/ledger.rb', line 136 def size @table.size end |
#values ⇒ Array<Library,Array>
Get a list of libraries and library version sets in the ledger.
164 165 166 |
# File 'lib/library/ledger.rb', line 164 def values @table.values end |