lib/tap/env.rb in tap-0.18.0 vs lib/tap/env.rb in tap-0.19.0

- old
+ new

@@ -1,26 +1,245 @@ require 'tap/root' +require 'tap/env/constant' +require 'tap/env/context' require 'tap/env/manifest' require 'tap/templater' autoload(:YAML, 'yaml') module Tap - # Env abstracts an execution environment that spans many directories. + + # == Description + # + # Env provides access to an execution environment spanning many directories, + # such as the working directory plus a series of gem directories. Envs merge + # the files from each directory into an abstract directory that may be + # globbed and accessed as a single unit. For example: + # + # # /one + # # |-- a.rb + # # `-- b.rb + # # + # # /two + # # |-- b.rb + # # `-- c.rb + # env = Env.new('/one') + # env << Env.new('/two') + # + # env.collect {|e| e.root.root} + # # => ["/one", "/two"] + # + # env.glob(:root, "*.rb") + # # => [ + # # "/one/a.rb", + # # "/one/b.rb", + # # "/two/c.rb" + # # ] + # + # As illustrated, files in the nested environment are accessible within the + # nesting environment. Envs provide methods for finding files associated + # with a specific class, and allow the generation of manifests that provide + # succinct access to various environment resources. + # + # Usage of Envs is fairly straightforward, but the internals and default + # setup require some study as they have to span numerous functional domains. + # The most common features are detailed below. + # + # ==== Class Paths + # + # Class paths are a kind of inheritance for files associated with a class. + # Say we had the following classes: + # + # class A; end + # class B < A; end + # + # The naturally associated directories are 'a' and 'b'. To look these up: + # + # env.class_path(:root, A) # => "/one/a" + # env.class_path(:root, B) # => "/one/b" + # + # And to look up an associated file: + # + # env.class_path(:root, A, "index.html") # => "/one/a/index.html" + # env.class_path(:root, B, "index.html") # => "/one/b/index.html" + # + # A block may be given to filter paths, for instance to test if a given file + # exists. The class_path method will check each env then roll up the + # inheritance hierarchy until the block returns true. + # + # FileUtils.touch("/two/a/index.html") + # + # visited_paths = [] + # env.class_path(:root, B, "index.html) do |path| + # visited_paths << path + # File.exists?(path) + # end # => "/two/a/index.html" + # + # visited_paths + # # => [ + # # "/one/b/index.html", + # # "/two/b/index.html", + # # "/one/a/index.html", + # # "/two/a/index.html" + # # ] + # + # This behavior is very useful for associating views with a class. + # + # ==== Manifest + # + # Envs can generate manifests of various resources so they may be identified + # using minipaths (see Minimap for details regarding minipaths). Command + # files used by the tap executable are one example of a resource, and the + # constants used in building a workflow are another. + # + # Manifest are generated by defining a builder, typically a block, that + # receives an env and returns an array of the associated resources. + # Using the same env as above: + # + # manifest = env.manifest {|e| e.root.glob(:root, "*.rb") } + # + # manifest.seek("a") # => "/one/a.rb" + # manifest.seek("b") # => "/one/b.rb" + # manifest.seek("c") # => "/two/c.rb" + # + # As illustrated, seek finds the first entry across all envs that matches the + # input minipath. A minipath for the env may be prepended to only search + # within a specific env. + # + # manifest.seek("one:b") # => "/one/b.rb" + # manifest.seek("two:b") # => "/two/b.rb" + # + # Env caches a manifest of constants identified by {constant attributes}[http://tap.rubyforge.org/lazydoc] + # in files specified by under the Env.const_paths. These constants are used + # when interpreting signals from the command line. Constants may be manually + # registered to the constants manifest and classified by type like this: + # + # class CustomTask + # def call; end + # end + # env.register(CustomTask).register_as(:task, "this is a custom task") + # + # const = env.constants.seek('custom_task') + # const.const_name # => "CustomTask" + # const.types # => {:task => "this is a custom task"} + # const.constantize # => CustomTask + # + # == Setup + # + # Envs may be manually setup in code by individually generating instances + # and nesting them. More commonly envs are defined in configuration files + # and instantiated by specifying where the files are located. The default + # config basename is 'tap.yml'; any env_paths specified in the config file + # will be added. + # + # This type of instantiation is recursive: + # + # # [/one/tap.yml] + # # env_paths: [/two] + # # + # # [/two/tap.yml] + # # env_paths: [/three] + # # + # + # env = Env.new("/one", :basename => 'tap.yml') + # env.collect {|e| e.root.root} + # # => ["/one", "/two", "/three"] + # + # Gem directories are fair game. Env allows specific gems to be specified + # by name (via the 'gems' config), and if a gem has a tap.yml file then it + # will be used to configure the gem env. Alternatively, an env may be set + # to automatically discover and nest gem environments. In this case gems + # are discovered when they have a tap.yml file. + # + # ==== ENV configs + # + # Configurations may be also specified as an ENV variables. This type of + # configuration is very useful on the command line. Config variables + # should be prefixed by TAP_ and named like the capitalized config key + # (ex: TAP_GEMS or TAP_ENV_PATHS). See the + # {Command Line Examples}[link:files/doc/Examples/Command%20Line.html] + # to see ENV configs in action. + # + # These configurations may be accessed from Env#config, and are + # automatically incorporated by Env#setup. + # class Env autoload(:Gems, 'tap/env/gems') class << self - attr_writer :instance - def instance(auto_initialize=true) - @instance ||= (auto_initialize ? new : nil) + # Returns the Env configs specified in ENV. Config variables are + # prefixed by TAP_ and named like the capitalized config key + # (ex: TAP_GEMS or TAP_ENV_PATHS). + def config(env_vars=ENV) + config = {} + env_vars.each_pair do |key, value| + if key =~ /\ATAP_(.*)\z/ + config[$1.downcase] = value + end + end + config end - def from_gemspec(spec, context={}) + # Initializes and activates an env as described in the config file under + # dir. The config file should be a relative path and will be used for + # determining configuration files under each env_path. + # + # The env configuration is determined by merging the following in order: + # * defaults {root => dir, gems => all} + # * ENV configs + # * config_file configs + # + # The HOME directory for Tap will be added as an additonal environment + # if not already added somewhere in the env hierarchy. By default all + # gems will be included in the Env. + def setup(dir=Dir.pwd, config_file=CONFIG_FILE) + # setup configurations + config = {'root' => dir, 'gems' => :all} + + user_config_file = config_file ? File.join(dir, config_file) : nil + user = load_config(user_config_file) + + config.merge!(self.config) + config.merge!(user) + + # keys must be symbolized as they are immediately + # used to initialize the Env configs + config = config.inject({}) do |options, (key, value)| + options[key.to_sym || key] = value + options + end + + # instantiate + context = Context.new(:basename => config_file) + env = new(config, context) + + # add the tap env if necessary + unless env.any? {|e| e.root.root == HOME } + env.push new(HOME, context) + end + + env.activate + env + end + + # Generates an Env for the specified gem or Gem::Specification. The + # gemspec for the gem is used to determine the env configuration in + # the following way: + # + # root: the gem path + # gems: all gem dependencies with a config_file + # const_paths: the gem require paths + # set_const_paths: false (because RubyGems sets them for you) + # + # Configurations specified in the gem config_file override these + # defaults. + def setup_gem(gem_name, context=Context.new) + spec = Gems.gemspec(gem_name) path = spec.full_gem_path - basename = context[:basename] + # determine gem dependencies that have a config_file; + # these will be set as the gems for the new Env dependencies = [] spec.dependencies.each do |dependency| unless dependency.type == :runtime next end @@ -30,137 +249,98 @@ # been uninstalled for a particular gem warn "missing gem dependency: #{dependency.to_s} (#{spec.full_name})" next end - if basename && !File.exists?(File.join(gemspec.full_gem_path, basename)) - next + if config_file = context.config_file(gemspec.full_gem_path) + next unless File.exists?(config_file) end dependencies << gemspec end config = { 'root' => path, 'gems' => dependencies, - 'load_paths' => spec.require_paths, - 'set_load_paths' => false + 'const_paths' => spec.require_paths, + 'set_const_paths' => false } - if context[:basename] - config.merge!(Env.load_config(File.join(path, context[:basename]))) + # override the default configs with whatever configs + # are specified in the gem config file + if config_file = context.config_file(path) + config.merge!(load_config(config_file)) end new(config, context) end # Loads configurations from path as YAML. Returns an empty hash if the path # loads to nil or false (as happens for empty files), or doesn't exist. + # + # Raises a ConfigError if the configurations do not load properly. def load_config(path) return {} unless path begin Root::Utils.trivial?(path) ? {} : (YAML.load_file(path) || {}) rescue(Exception) raise ConfigError.new($!, path) end end - - def scan_dir(load_path, pattern='**/*.rb') - Dir.chdir(load_path) do - Dir.glob(pattern).each do |require_path| - next unless File.file?(require_path) - - default_const_name = require_path.chomp('.rb').camelize - - # note: the default const name has to be set here to allow for implicit - # constant attributes. An error can arise if the same path is globed - # from two different dirs... no surefire solution. - Lazydoc[require_path].default_const_name = default_const_name - - # scan for constants - Lazydoc::Document.scan(File.read(require_path)) do |const_name, type, comment| - const_name = default_const_name if const_name.empty? - constant = Constant.new(const_name, require_path, comment) - yield(type, constant) - - ############################################################### - # [depreciated] manifest as a task key will be removed at 1.0 - if type == 'manifest' - warn "depreciation: ::task should be used instead of ::manifest as a resource key (#{require_path})" - yield('task', constant) - end - ############################################################### - end - end - end - end - - def scan(path, key='[a-z_]+') - Lazydoc::Document.scan(File.read(path), key) do |const_name, type, comment| - if const_name.empty? - unless const_name = Lazydoc[path].default_const_name - raise "could not determine a constant name for #{type} in: #{path.inspect}" - end - end - - constant = Constant.new(const_name, path, comment) - yield(type, constant) - - ############################################################### - # [depreciated] manifest as a task key will be removed at 1.0 - if type == 'manifest' - warn "depreciation: ::task should be used instead of ::manifest as a resource key (#{require_path})" - yield('task', constant) - end - ############################################################### - end - end end - self.instance = nil - include Enumerable include Configurable + include Enumerable include Minimap - # Matches a compound registry search key. After the match, if the key is - # compound then: - # - # $1:: env_key - # $2:: key - # - # If the key is not compound, $2 is nil and $1 is the key. - COMPOUND_KEY = /^((?:[A-z]:(?:\/|\\))?.*?)(?::(.*))?$/ - + # The config file path + CONFIG_FILE = "tap.yml" + + # The home directory for Tap + HOME = File.expand_path("#{File.dirname(__FILE__)}/../..") + # An array of nested Envs, by default comprised of the env_path - # + gem environments (in that order). + # + gem environments (in that order). Envs can be manually set + # to override these defaults. attr_reader :envs + # A Context tracking information shared among a set of envs. attr_reader :context - attr_reader :manifests - # The Root directory structure for self. - nest(:root, Root, :set_default => false) + nest(:root, Root, :init => false) - # Specify gems to add as nested Envs. Gems may be specified - # by name and/or version, like 'gemname >= 1.2'; by default the - # latest version of the gem is selected. Gems are not activated - # by Env. + # Specify gems to add as nested Envs. Gems may be specified by name + # and/or version, like 'gemname >= 1.2'; by default the latest version + # of the gem is selected. + # + # Several special values also exist: + # + # :NONE, :none indicates no gems (same as nil, false) + # :LATEST the latest version of all gems + # :ALL all gems + # :latest the latest version of all gems with a config file + # :all all gems with a config file + # + # Gems are not activated by Env. config_attr :gems, [] do |input| input = yaml_load(input) if input.kind_of?(String) @gems = case input when false, nil, :NONE, :none [] when :LATEST, :ALL # latest and all, no filter Gems.select_gems(input == :LATEST) when :latest, :all - # latest and all, filtering by basename + # latest and all, filtering by the existence of a + # config file; all gems are selected if no config + # file can be determined. Gems.select_gems(input == :latest) do |spec| - basename == nil || File.exists?(File.join(spec.full_gem_path, basename)) + config_file = context.config_file(spec.full_gem_path) + config_file == nil || File.exists?(config_file) end else # resolve gem names manually [*input].collect do |name| Gems.gemspec(name) @@ -168,50 +348,49 @@ end reset_envs end - # Specify directories to load as nested Envs. + # Specify directories to load as nested Envs. Configurations for the + # env are loaded from the config file under dir, if it exists. config_attr :env_paths, [] do |input| @env_paths = resolve_paths(input) reset_envs end - # Designates paths added to $LOAD_PATH on activation (see set_load_paths). - # These paths are also the default directories searched for resources. - config_attr :load_paths, [:lib] do |input| - raise "load_paths cannot be modified once active" if active? - @load_paths = resolve_paths(input) + # Designates directories searched for constants. The const_paths are + # added to $LOAD_PATH on activation if set_const_paths is specified. + config_attr :const_paths, [:lib] do |input| + raise "const_paths cannot be modified once active" if active? + @const_paths = resolve_paths(input) end - # If set to true load_paths are added to $LOAD_PATH on activation. - config_attr :set_load_paths, true do |input| - raise "set_load_paths cannot be modified once active" if active? - @set_load_paths = Configurable::Validation.boolean[input] + # If set to true const_paths are added to $LOAD_PATH on activation. + config_attr :set_const_paths, true do |input| + raise "set_const_paths cannot be modified once active" if active? + @set_const_paths = Configurable::Validation.boolean[input] end - # Initializes a new Env linked to the specified directory. A config file - # basename may be specified to load configurations from 'dir/basename' as - # YAML. If a basename is specified, the same basename will be used to - # load configurations for nested envs. + # Initializes a new Env linked to the specified directory. Configurations + # for the env will be loaded from the config file (as determined by the + # context) if it exists. # - # Configurations may be manually provided in the place of dir. In that - # case, the same rules apply for loading configurations for nested envs, - # but no configurations will be loaded for the current instance. + # A configuration hash may be manually provided in the place of dir. In + # that case, no configurations will be loaded, even if the config file + # exists. # - # The cache is used internally to prevent infinite loops of nested envs, - # and to optimize the generation of manifests. + # Context can be specified as a Context, or a Hash used to initialize a + # Context. def initialize(config_or_dir=Dir.pwd, context={}) - @active = false - @manifests = {} - @context = context # setup root config = nil @root = case config_or_dir - when Root then config_or_dir - when String then Root.new(config_or_dir) + when Root + config_or_dir + when String + Root.new(config_or_dir) else config = config_or_dir if config.has_key?(:root) && config.has_key?('root') raise "multiple values mapped to :root" @@ -219,30 +398,34 @@ root = config.delete(:root) || config.delete('root') || Dir.pwd root.kind_of?(Root) ? root : Root.new(root) end - if basename && !config - config = Env.load_config(File.join(@root.root, basename)) + # note registration requires root.root, and so the + # setup of context must follow the setup of root. + @context = case context + when Context + context + when Hash + Context.new(context) + else raise "cannot convert #{context.inspect} to Tap::Env::Context" end + @context.register(self) - if instance(@root.root) - raise "context already has an env for: #{@root.root}" - end - instances << self - - # set these for reset_env + # these need to be set for reset_env + @active = false @gems = nil @env_paths = nil - initialize_config(config || {}) + + # only load configurations if configs were not provided + config ||= Env.load_config(@context.config_file(@root.root)) + initialize_config(config) + + # set the invert flag + @invert = false end - # The minikey for self (root.root). - def minikey - root.root - end - # Sets envs removing duplicates and instances of self. Setting envs # overrides any environments specified by env_path and gem. def envs=(envs) raise "envs cannot be modified once active" if active? @envs = envs.uniq.delete_if {|env| env == self } @@ -263,10 +446,11 @@ envs = self.envs.reject {|e| e == env } self.envs = envs.push(env) end self end + alias_method :<<, :push # Passes each nested env to the block in order, starting with self. def each visit_envs.each {|e| yield(e) } end @@ -304,37 +488,33 @@ inject_envs(memo, &block) end # Activates self by doing the following, in order: # - # * sets Env.instance to self (unless already set) # * activate nested environments - # * unshift load_paths to $LOAD_PATH (if set_load_paths is true) + # * unshift const_paths to $LOAD_PATH (if set_const_paths is true) # - # Once active, the current envs and load_paths are frozen and cannot be + # Once active, the current envs and const_paths are frozen and cannot be # modified until deactivated. Returns true if activate succeeded, or # false if self is already active. def activate return false if active? @active = true - unless self.class.instance(false) - self.class.instance = self - end - # freeze envs and load paths + # freeze envs and const paths @envs.freeze - @load_paths.freeze + @const_paths.freeze # activate nested envs envs.reverse_each do |env| env.activate end - # add load paths - if set_load_paths - load_paths.reverse_each do |path| + # add const paths + if set_const_paths + const_paths.reverse_each do |path| $LOAD_PATH.unshift(path) end $LOAD_PATH.uniq! end @@ -343,48 +523,59 @@ end # Deactivates self by doing the following in order: # # * deactivates nested environments - # * removes load_paths from $LOAD_PATH (if set_load_paths is true) - # * sets Env.instance to nil (if set to self) - # * clears cached manifest data + # * removes const_paths from $LOAD_PATH (if set_const_paths is true) # - # Once deactivated, envs and load_paths are unfrozen and may be modified. + # Once deactivated, envs and const_paths are unfrozen and may be modified. # Returns true if deactivate succeeded, or false if self is not active. + # + # ==== Note + # + # Deactivation does not necessarily leave $LOAD_PATH in the same condition + # as before activation. A pre-existing $LOAD_PATH entry can go missing if + # it is also registered as an env load_path (deactivation doesn't know to + # leave such paths alone). + # + # Deactivation, like constant unloading should be done with caution. def deactivate return false unless active? @active = false # dectivate nested envs envs.reverse_each do |env| env.deactivate end - # remove load paths - load_paths.each do |path| + # remove const paths + const_paths.each do |path| $LOAD_PATH.delete(path) - end if set_load_paths + end if set_const_paths - # unfreeze envs and load paths + # unfreeze envs and const paths @envs = @envs.dup - @load_paths = @load_paths.dup + @const_paths = @const_paths.dup - # clear cached data - klass = self.class - if klass.instance(false) == self - klass.instance = nil - end - true end # Return true if self has been activated. def active? @active end + # Globs the abstract directory for files in the specified directory alias, + # matching the pattern. The expanded path of each matching file is + # returned. + def glob(dir, pattern="**/*") + hlob(dir, pattern).values.sort! + end + + # Same as glob but returns results as a hash of (relative_path, path) + # pairs. In short the hash defines matching files in the abstract + # directory, linked to the actual path for these files. def hlob(dir, pattern="**/*") results = {} each do |env| root = env.root root.glob(dir, pattern).each do |path| @@ -393,15 +584,15 @@ end end results end - def glob(dir, pattern="**/*") - hlob(dir, pattern).values.sort! - end - - def path(dir, *paths) + # Returns the path to the specified file, as determined by root. + # + # If a block is given, a path for each env will be yielded until the block + # returns a true value. Returns nil if the block never returns true. + def path(dir = :root, *paths) each do |env| path = env.root.path(dir, *paths) return path if !block_given? || yield(path) end nil @@ -411,17 +602,16 @@ # An array of modules (which naturally can include classes) are provided # and module_path traverses each, forming paths like: # # path(dir, module_path, *paths) # - # By default, 'module_path' is 'module.to_s.underscore', but modules can + # By default 'module_path' is 'module.to_s.underscore' but modules can # specify an alternative by providing a module_path method. # - # The paths are yielded to the block and when the block returns true, - # the path will be returned. If no block is given, the first module path - # is returned. Returns nil if the block never returns true. - # + # Paths are yielded to the block until the block returns true, at which + # point the current the path is returned. If no block is given, the + # first path is returned. Returns nil if the block never returns true. def module_path(dir, modules, *paths, &block) paths.compact! while current = modules.shift module_path = if current.respond_to?(:module_path) current.module_path @@ -444,115 +634,123 @@ klass = obj.kind_of?(Class) ? obj : obj.class superclasses = klass.ancestors - klass.included_modules module_path(dir, superclasses, *paths, &block) end - def registry(build=false) - builders.each_pair do |type, builder| - registry[type] ||= builder.call(self) - end if build - - registries[minikey] ||= begin - registry = {} - load_paths.each do |load_path| + # Generates a Manifest for self using the block as a builder. The builder + # receives an env and should return an array of resources, each of which + # can be minimappped. Minimapping requires that the resource is either + # a path string, or provides a 'path' method that returns a path string. + # Alternatively, a Minimap may be returned. + # + # If a type is specified, then the manifest cache will be linked to the + # context cache. + def manifest(type=nil, &block) # :yields: env + cache = type ? (context.cache[type] ||= {}) : {} + Manifest.new(self, block, cache) + end + + # Returns a manifest of Constants located in .rb files under each of the + # const_paths. Constants are identified using Lazydoc constant attributes; + # all attributes are registered to the constants for classification + # (for example as a task, join, etc). + def constants + @constants ||= manifest(:constants) do |env| + constants = {} + + env.const_paths.each do |load_path| next unless File.directory?(load_path) - - Env.scan_dir(load_path) do |type, constant| - (registry[type.to_sym] ||= []) << constant - end + Constant.scan(load_path, "**/*.rb", constants) end - - registry + + constants.keys.sort!.collect! do |key| + constants[key] + end end end - def register(type, override=false, &block) - type = type.to_sym - - # error for existing, or overwrite - case - when override - builders.delete(type) - registries.each {|root, registry| registry.delete(type) } - when builders.has_key?(type) - raise "a builder is already registered for: #{type.inspect}" - when registries.any? {|root, registry| registry.has_key?(type) } - raise "entries are already registered for: #{type.inspect}" + # Seeks a constant for the key, constantizing if necessary. If invert is + # true, this method seeks and returns a key for the input constant. In + # both cases AGET returns nil if no key-constant pair can be found. + def [](key, invert=invert?) + if invert + const_name = key.to_s + constants.unseek(true) do |const| + const_name == const.const_name + end + else + if constant = constants.seek(key) + constant.constantize + else + nil + end end - - builders[type] = block end - #-- - # Potential bug, constants can be added twice. - def scan(path, key='[a-z_]+') - registry = self.registry - Env.scan(path, key) do |type, constant| - (registry[type.to_sym] ||= []) << constant - end + # Indicates AGET looks up constants from keys (false), or keys from + # constants (true). + def invert? + @invert end - def manifest(type) # :yields: env - type = type.to_sym - - registry[type] ||= begin - builder = builders[type] - builder ? builder.call(self) : [] - end - - manifests[type] ||= Manifest.new(self, type) + # Inverts the AGET lookup for self. + def invert! + @invert = !@invert + self end - def [](type) - manifest(type) + # Returns a duplicate of self with inverted AGET lookup. + def invert + dup.invert! end - def reset - manifests.clear - registries.clear + # Scans the files matched under the directory and pattern for constants + # and adds them to the existing constants for self. + def scan(dir, pattern="**/*.rb") + new_entries = {} + entries = constants.entries(self) + entries.each {|const| new_entries[const.const_name] = const } + + Constant.scan(root[dir], pattern, new_entries) + + entries.replace(new_entries.keys) + entries.sort!.collect! {|key| new_entries[key] } + entries end - # Searches across each for the first registered object minimatching key. A - # single env can be specified by using a compound key like 'env_key:key'. - # - # Returns nil if no matching object is found. - def seek(type, key, value_only=true) - key =~ COMPOUND_KEY - envs = if $2 - # compound key, match for env - key = $2 - [minimatch($1)].compact - else - # not a compound key, search all envs by iterating self - self + # Registers a constant with self. The constant is stored as a new + # Constant in the constants manifest. Returns the new Constant. + # If the constant is already registered, the existing Constant is + # returned. + def register(constant) + const_name = constant.to_s + entries = constants.entries(self) + + # try to find the existing Constant before making a new constant + unless constant = entries.find {|const| const.const_name == const_name} + constant = Constant.new(const_name) + entries << constant + entries.replace entries.sort_by {|const| const.const_name } end - - # traverse envs looking for the first - # manifest entry matching key - envs.each do |env| - if value = env.manifest(type).minimatch(key) - return value_only ? value : [env, value] - end - end - - nil + + constant end - def reverse_seek(type, key_only=true, &block) - each do |env| - manifest = env.manifest(type) - if value = manifest.find(&block) - key = manifest.minihash(true)[value] - return key_only ? key : "#{minihash(true)[env]}:#{key}" - end - end - - nil - end - - # All templaters are yielded to the block before any are built. This - # allows globals to be determined for all environments. + # When no template is specified, inspect generates a fairly standard + # inspection string. When a template is provided, inspect builds a + # Templater for each env with the following local variables: + # + # variable value + # env the current env + # env_keys a minihash for all envs + # + # If a block is given, the globals and templater are yielded before + # any templater is built; this allows each env to add env-specific + # variables. After this preparation, each templater is built with + # the globals and the results concatenated. + # + # The template is built with filename, if specified (for debugging). def inspect(template=nil, globals={}, filename=nil) # :yields: templater, globals if template == nil return "#<#{self.class}:#{object_id} root='#{root.root}'>" end @@ -563,46 +761,32 @@ templater end.collect! do |templater| templater.build(globals, filename) end.join end - + protected - def registries # :nodoc: - context[:registries] ||= {} + # helper for Minimap; note that specifying env.root.root via path + # is not possible because path is required for other purposes. + def entry_to_path(env) # :nodoc: + env.root.root end - def basename # :nodoc: - context[:basename] - end - - def builders # :nodoc: - context[:builders] ||= {} - end - - def instances # :nodoc: - context[:instances] ||= [] - end - - def instance(path) # :nodoc: - instances.find {|env| env.root.root == path } - end - # resets envs using the current env_paths and gems. does nothing # until both env_paths and gems are set. def reset_envs # :nodoc: if env_paths && gems self.envs = env_paths.collect do |path| - instance(path) || Env.new(path, context) + context.instance(path) || Env.new(path, context) end + gems.collect do |spec| - instance(spec.full_gem_path) || Env.from_gemspec(spec, context) + context.instance(spec.full_gem_path) || Env.setup_gem(spec, context) end end end - # arrayifies, compacts, and resolves input paths using root, and - # removes duplicates. in short: + # arrayifies, compacts, and resolves input paths using root. + # also removes duplicates. in short: # # resolve_paths ['lib', nil, 'lib', 'alt] # => [root['lib'], root['alt']] # def resolve_paths(paths) # :nodoc: paths = yaml_load(paths) if paths.kind_of?(String) \ No newline at end of file