lib/bezel.rb in bezel-0.1.0 vs lib/bezel.rb in bezel-0.2.0

- old
+ new

@@ -18,113 +18,190 @@ # module JanesProgram # TomsLib = lib('tomslib', '1.0') # FancyApp = lib('fancyapp') # use newest available # ... # -# How does this work? When you call library(), Bezel looks for -# the package in the current gem paths (and, in the future, Roll ledger) -# then it reads the primary package file (eg. tomslib.rb) fro the package +# How does this work? When you call lib(), Bezel looks for the +# package in the current gem paths (and, in the future, Roll ledger) +# then it reads the bezel file (e.g. lib/tomslib.rbz) from the package # and evals it into an anonymous module. # # This has a some important effects on how you write your Ruby programs: # # 1. Any reference to core/standard libraries must be referenced via -# :: prefix (eg. ::Enumerable). +# toplevel `::` prefix (eg. ::Enumerable). # # 2. Core extensions are not version controlled. So avoid them when # possible, or depend on highly stable standardized bases such as # Facets and ActiveSupport. # # 3. Since Bezel is a completely different alternative to Ruby's normal # load system, your program will require Bezel be installed by your # users. # -# Despite the limitations and necessary practices required for its use -# Bezel is highly advantageous to the developers and end-users alike -# in that it puts an end to the dreaded Dependency Hell. +# Despite these necessary practices for its use, Bezel is highly advantageous +# to developers and end-users alike in that it puts an end to the dreaded +# Dependency Hell. # +# TODO: Consider how best to support alternate loadpaths beyond 'lib/'. + class Bezel < Module - require 'rubygems' + require 'finder' # Cache of loaded modules. This speeds things # up if two libraries load the same third library. - TABLE = Hash.new{|h,k|h[k]={}} + TABLE = Hash.new #{|h,k|h[k]={}} + # Script cache. + SCRIPT = {} + # Load stack keeps track of what modules are in the process # of being loaded. STACK = [] - # - def self.gem_paths - @gem_paths ||= ( - Gem.path.map do |dir| - Dir[File.join(dir, 'gems', '*')] - end.flatten - ) + # When in development mode, Bezel uses the latest and greatest + # available version regardless of version settings. + def self.development=(boolean) + @development = !!boolean end # - def self.select(name) - gem_paths.select do |path| - File.basename(path) =~ /^#{name}-/ - end + def self.development? + @development end + # Load library into a module namespace. + def self.lib(name, version=nil) + version = nil if development? + + ##path = find(name, version) + ##raise LoadError, "#{name}-#{version} not found" unless path + ## location of main requirement file + ## TODO: should we require a dedicated bezel file instead? e.g. `*.rbz`. + #main = File.join(path, 'lib', name + '.rb') #.rbz #TODO: LOADPATH + + main = Find.feature(name, :from=>name, :version=>version, :absolute=>true).first + + ## check cache + return TABLE[main] if TABLE.key?(main) + + ## load error if no bezel file + raise LoadError, "#{name}-#{version} has no bezel!" unless main && File.exist?(main) + + ## read in bezel file + #script = File.read(main) + + ## create new Bezel module for file + mod = new(name, version) #, main) + + ## put module on STACK + STACK.push mod + + ## evaluate script in the context of module + #mod.module_eval(script, main, 0) # r = + mod.__load_feature__(main) + + ## pop module off STACK + STACK.pop + + ## add module to cache, and return module + TABLE[main] = mod + end + # - def self.find(name, version=nil) - if version - basename = "#{name}-#{version}" - select(name).find do |path| - File.basename(path) == basename + def self.require(path) + if current = STACK.last + begin + current.__load_feature__(path) ? true : false + rescue LoadError + require_without_bezel(path) end else - select(name).max + require_without_bezel(path) end end # #def self.main(name, version=nil) # path = find(name, version) # File.join(path, 'lib', name + '.rb') #end - # - def initialize(name, path) - @__name__ = name - @__path__ = path + # Construct new Bezel module. + def initialize(name, version) #, path) + @__name__ = name + @__version__ = version + #@__path__ = path + @__features__ = [] + super() end + # Name of library. def __name__ @__name__ end - def __path__ - @__path__ + # Version of library. + def __version__ + @__version__ end -end + # Path to library. + #def __path__ + # @__path__ + #end -# -def lib(name, version=nil) - path = Bezel.find(name, version) - main = File.join(path, 'lib', name + '.rb') + # + def __features__ + @__features__ + end - return Bezel::TABLE[main] if Bezel::TABLE.key?(main) + # + def __load_feature__(path) + if path =~ /^[\.\/]/ # if absolute path + file = File.expand_path(path) + else + file = Find.feature(path, :from=>__name__, :version=>__version__, :absolute=>true).first + end - mod = Bezel.new(name, path) + raise LoadError, "no such file to load -- #{file}" unless file - Bezel::STACK << mod + if __features__.include?(file) + return false + else + __features__ << file + script = File.read(file) #(SCRIPT[file] ||= File.read(file)) + module_eval(script, file, 0) + end - mod.module_eval(File.read(main), main, 0) + return file + end - Bezel::STACK.pop - - Bezel::TABLE[main] = mod end -# When using Bezel, rather than use #require or #load, you use #import. -def import(fname) - mod = Bezel::STACK.last - file = File.join(mod.__path__, 'lib', mod.__name__, fname) - mod.module_eval(File.read(file), file, 0) +module Kernel + # TODO require_relative + + class << self + # Alias original require. + alias require_without_bezel require + + # Override require to try bezel first. + def require(fname) + Bezel.require(fname) + end + end + + # Alias original require. + alias require_without_bezel require + + # Override require to try bezel first. + def require(fname) + Bezel.require(fname) + end + + # Retuns a new Bezel Module. + def lib(name, version=nil) + Bezel.lib(name, version) + end end