module Roll require 'roll/original' require 'roll/environment' require 'roll/library' # = Ledger class # class Ledger include Enumerable # def initialize @index = Hash.new{|h,k| h[k] = []} @environment = Environment.new @environment.each do |name, paths| paths.each do |path| unless File.directory?(path) warn "roll: invalid path for #{name} -- #{path}" next end lib = Library.new(path, name) @index[name] << lib if lib.active? end end @load_stack = [] @load_monitor = Hash.new{ |h,k| h[k]=[] } end # def enironment @environment end # def [](name) @index[name] end # def []=(name, value) @index[name] = value end # def include?(name) @index.include?(name) end # def names @index.keys end # def each(&block) @index.each(&block) end # def size @index.size end # def load_stack @load_stack end # NOTE: Not used yet. def load_monitor @load_monitor end #-- # TODO: Should Ruby's underlying require be tried first, # then fallback to Rolls. Or vice-versa? #++ # def require(path) #begin # original_require(path) #rescue LoadError => load_error # lib, file = *match(path) # if lib && file # constrain(lib) # lib.require_absolute(file) # else # raise clean_backtrace(load_error) # end #end lib, file = *match(path) if lib && file constrain(lib) lib.require_absolute(file) else begin original_require(path) rescue LoadError => load_error raise clean_backtrace(load_error) end end end # def load(path, wrap=nil) lib, file = *match(path, false) if lib && file constrain(lib) lib.load_absolute(file, wrap) else begin original_load(path, wrap) rescue LoadError => load_error raise clean_backtrace(load_error) end end end # Use acquire to use Roll-style loading. This first # looks for a specific library via ':'. If ':' is # not present it then tries the current library. # Failing that it fallsback to Ruby itself. # # acquire('facets:string/margin') # # To "load" the library, rather than "require" it set # the +:load+ option to true. # # acquire('facets:string/margin', :load=>true) # def acquire(file, opts={}) if file.index(':') # a specific library name, file = file.split(':') lib = Library.open(name) else # try the current library cur = load_stack.last if cur && cur.include?(file) lib = cur elsif !file.index('/') # is this a library name? if cur = Library.instance(file) lib = cur file = lib.default # default file to load end end end if opts[:load] lib ? lib.load(file) : original_load(file) else lib ? lib.require(file) : original_require(file) end end # def constrain(lib) cmp = self[lib.name] if Array === cmp self[lib.name] = lib else if lib.version != cmp.version raise VersionError end end end private # Find require matches. def match(path, suffix=true) path = path.to_s return nil if /^\// =~ path # absolute path if path.index(':') # a specified library name, path = path.split(':') lib = Library.open(name) if lib.active? file = lib.find(path, suffix) return lib, file end end matches = [] # try the load stack first load_stack.reverse_each do |lib| if file = lib.find(path, suffix) return [lib, file] unless $VERBOSE matches << [lib, file] end end # TODO: Perhaps the selected and unselected should be kept in separate lists? unselected, selected = *@index.partition{ |name, libs| Array === libs } selected.each do |(name, lib)| if file = lib.find(path, suffix) #matches << [lib, file] #return matches.first unless $VERBOSE return [lib, file] unless $VERBOSE matches << [lib, file] end end unselected.each do |(name, libs)| pos = [] libs.each do |lib| if file = lib.find(path, suffix) pos << [lib, file] end end unless pos.empty? latest = pos.sort{ |a,b| b[0].version <=> a[0].version }.first return latest unless $VERBOSE matches << latest #return matches.first unless $VERBOSE end end matches.uniq! if matches.size > 1 warn_multiples(path, matches) end matches.first end # def warn_multiples(path, matches) warn "multiple matches for same request -- #{path}" matches.each do |lib, file| warn " #{file}" end end # def clean_backtrace(error) if $DEBUG error else bt = error.backtrace bt = bt.reject{ |e| /roll/ =~ e } error.set_backtrace(bt) error end end end#class Ledger #-- # Ledger augments the Library metaclass. #++ class << Library # def ledger @ledger ||= Ledger.new end # Current environment def environment ledger.environment end # List of library names. def list ledger.names end # def require(path) ledger.require(path) end # def load(path, wrap=nil) ledger.load(path, wrap) end # def load_stack ledger.load_stack end # NOTE: Not used yet. def load_monitor ledger.load_monitor end end end#module Roll