lib/tap/env.rb in tap-0.10.1 vs lib/tap/env.rb in tap-0.11.0
- old
+ new
@@ -1,18 +1,17 @@
-require 'tap/support/manifest'
-require 'tap/support/constant'
-require 'tap/support/summary'
+require 'tap/support/constant_manifest'
require 'tap/support/gems'
module Tap
#--
# Note that gems and env_paths reset envs -- custom modifications to envs will be lost
# whenever these configs are reset.
class Env
- include Support::Configurable
include Enumerable
+ include Support::Configurable
+ include Support::Minimap
class << self
# Returns the active instance of Env.
def instance
@@ -24,17 +23,10 @@
# to a given path.
def instances
@@instances
end
- # A hash of predefined manifest classes that can be initialized
- # from an env. These classes are instantiated by instances
- # of Env, as needed.
- def manifests
- @@manifests
- end
-
# Creates a new Env for the specified path and adds it to Env#instances, or
# returns the existing instance for the path. Paths can point to an env config
# file, or to a directory. If a directory is provided, instantiate treats
# path as the DEFAULT_CONFIG_FILE in that directory. All paths are expanded.
#
@@ -75,81 +67,28 @@
path = File.join(path, DEFAULT_CONFIG_FILE)
end
File.expand_path(path)
end
- def manifest(name, pattern, default_paths=[], &block) # :yields: search_path
- manifest_class = Class.new(Support::Manifest)
- manifest_class.send(:define_method, :entries_for, &block) if block_given?
- manifest_class.send(:attr_reader, :env)
- manifest_class.send(:define_method, :initialize) do |env|
- @env = env
- search_paths = default_paths.collect {|path| env.root[path] }
- search_paths += env.root.glob(:root, pattern)
- super search_paths.sort_by {|p| File.basename(p) }
+ def manifest(name, &block) # :yields: env (returns manifest)
+ name = name.to_sym
+ define_method(name) do
+ self.manifests[name] ||= block.call(self).bind(self, name)
end
-
- manifests[name] = manifest_class
end
- #--
- # To manifest simply requires an glob_<name> method which
- # yields each (key, path) pair for the manifested object in
- # a predictable order.
- #
- #--
- # Alternate implementation would create the manifest for each individual
- # env, then merge the manifests. On the plus side, each env would then
- # carry it's own slice of the manifest without having to recalculate.
- # On the down side, the merging would have to occur in some separate
- # method that cannot be defined here.
- def path_manifest(name, paths_key, pattern, default_paths=[], &block) # :yields: search_path_root, search_path
- manifest_class = Class.new(Support::Manifest)
- manifest_class.send(:define_method, :entries_for, &block) if block_given?
- manifest_class.send(:attr_reader, :env)
- manifest_class.send(:define_method, :initialize) do |env|
- @env = env
- search_paths = default_paths.collect do |path|
- [env.root.root, env.root[path]]
- end
-
- env.send(paths_key).each do |search_path_root|
- env.root.glob(search_path_root, pattern).each do |search_path|
- search_paths << [search_path_root, search_path]
- end
- end
-
- super search_paths.sort_by {|pr, p| File.basename(p) }
- end
- manifests[name] = manifest_class
- end
-
- # Returns the gemspecs for all installed gems with a DEFAULT_TASK_FILE
- # or DEFAULT_CONFIG_FILE. If latest==true, then only the specs for the
- # most current gems will be returned.
+ # Returns the gemspecs for all installed gems with a DEFAULT_CONFIG_FILE.
+ # If latest==true, then only the specs for the most current gems will be
+ # returned.
def gemspecs(latest=true)
Support::Gems.select_gems(latest) do |spec|
- File.exists?(File.join(spec.full_gem_path, DEFAULT_TASK_FILE)) ||
File.exists?(File.join(spec.full_gem_path, DEFAULT_CONFIG_FILE))
end
end
protected
- # Defines a config that raises an error if set when the
- # instance is active. static_config MUST take a block
- # and raises an error if a block is not given.
- def static_config(key, value=nil, &block)
- raise ArgumentError.new("active config requires block") unless block_given?
-
- instance_variable = "@#{key}".to_sym
- config_attr(key, value) do |input|
- check_configurable
- instance_variable_set(instance_variable, block.call(input))
- end
- end
-
# Defines a config that collects the input into a unique,
# compact array where each member has been resolved using
# root[]. In short, ['lib', nil, 'lib', 'alt] becomes
# [root['lib'], root['alt']].
#
@@ -163,59 +102,58 @@
instance_variable_set(instance_variable, [*input].compact.collect {|path| root[path]}.uniq)
end
end
end
- class Manifest < Support::Manifest
- def initialize(env)
- super([])
- @entries = env.collect {|e| [e.root.root, e] }
- end
- end
-
@@instance = nil
@@instances = {}
- @@manifests = {:envs => Manifest}
- # The global config file path
- GLOBAL_CONFIG_FILE = File.join(Support::Gems.user_home, ".tap.yml")
-
# The default config file path
DEFAULT_CONFIG_FILE = "tap.yml"
- # The default task file path
- DEFAULT_TASK_FILE = "tapfile.rb"
-
# The Root directory structure for self.
attr_reader :root
# Gets or sets the logger for self
attr_accessor :logger
- # A hash of the manifests for self.
- attr_reader :manifests
+ # Specify files to require when self is activated.
+ config :requires, [], &c.array_or_nil
+ # Specify files to load when self is activated.
+ config :loads, [], &c.array_or_nil
+
# Specify gems to load 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 immediately loaded (via gem) through this method.
- #--
- # Note that the gems are resolved to gemspecs using Env.gemspec,
- # so self.gems returns an array of gemspecs.
config_attr :gems, [] do |input|
check_configurable
+ specs_by_name = {}
@gems = [*input].compact.collect do |gem_name|
spec = Support::Gems.gemspec(gem_name)
case spec
when nil then log(:warn, "unknown gem: #{gem_name}", Logger::WARN)
else Env.instance_for(spec.full_gem_path)
end
- spec
+ (specs_by_name[spec.name] ||= []) << spec
+ spec.name
end.uniq
+
+ # this song and dance is to ensure that the latest spec for a
+ # given gem appears first in the manifest
+ specs_by_name.each_pair do |name, specs|
+ specs_by_name[name] = specs.uniq.sort_by {|spec| spec.version }.reverse
+ end
+
+ @gems.collect! do |name|
+ specs_by_name[name]
+ end.flatten!
+
reset_envs
end
# Specify configuration files to load as nested Envs.
config_attr :env_paths, [] do |input|
@@ -233,50 +171,48 @@
path_config :command_paths, ["cmd"]
# Designate paths for discovering generators.
path_config :generator_paths, ["lib"]
- path_manifest(:tasks, :load_paths, "**/*.rb", [DEFAULT_TASK_FILE]) do |load_path, path|
- next unless File.file?(path) && document = Support::Lazydoc.scan_doc(path, 'manifest')
-
- document.const_names.collect do |const_name|
- if const_name.empty?
- key = env.root.relative_filepath(load_path, path).chomp('.rb')
- [key, Support::Constant.new(key.camelize, path)]
- else
- [const_name.underscore, Support::Constant.new(const_name, path)]
- end
+ manifest(:commands) do |env|
+ paths = []
+ env.command_paths.each do |path_root|
+ paths.concat env.root.glob(path_root)
end
+
+ paths = paths.sort_by {|path| File.basename(path) }
+ Support::Manifest.new(paths)
end
- path_manifest(:commands, :command_paths, "**/*.rb") do |command_path, path|
- File.file?(path) ? [[path, path]] : nil
+ manifest(:tasks) do |env|
+ tasks = Support::ConstantManifest.new('manifest')
+ env.load_paths.each do |path_root|
+ tasks.register(path_root, '**/*.rb')
+ end
+ # tasks.cache = env.cache[:tasks]
+ tasks
end
-
- path_manifest(:generators, :generator_paths, '**/*_generator.rb') do |generator_path, path|
- dirname = File.dirname(path)
- next unless File.file?(path) && "#{File.basename(dirname)}_generator.rb" == File.basename(path)
+
+ manifest(:generators) do |env|
+ generators = Support::ConstantManifest.intern('generator') do |manifest, const|
+ const.name.underscore.chomp('_generator')
+ end
- next unless document = Support::Lazydoc.scan_doc(path, 'generator')
- document.const_names.collect do |const_name|
- if const_name.empty?
- key = env.root.relative_filepath(generator_path, dirname)
- [key, Support::Constant.new((key + '_generator').camelize, path)]
- else
- [const_name.underscore, Support::Constant.new(const_name, path)]
- end
+ env.generator_paths.each do |path_root|
+ generators.register(path_root, '**/*_generator.rb')
end
+ # generators.cache = env.cache[:generators]
+ generators
end
-
+
def initialize(config={}, root=Tap::Root.new, logger=nil)
@root = root
@logger = logger
@envs = []
@active = false
@manifests = {}
- @manifested = []
-
+
# initialize these for reset_env
@gems = []
@env_paths = []
initialize_config(config)
@@ -453,10 +389,21 @@
load_paths.reverse_each do |path|
$LOAD_PATH.unshift(path)
end
$LOAD_PATH.uniq!
+
+ # perform requires
+ requires.each do |path|
+ require path
+ end
+
+ # perform loads
+ loads.each do |path|
+ load path
+ end
+
true
end
# Deactivates self by clearing manifests and deleting load_paths for self
# from the load_path_targets. Env.instance will no longer reference self
@@ -494,98 +441,129 @@
# Return true if self has been activated.
def active?
@active
end
- # Returns the manifest in manifests by the specified name. Yields
- # each entry in the manifest to the block, if given, or simply
- # builds and returns the manifest.
+ # Searches each env for the first existing file or directory at
+ # env.root.filepath(dir, path). Paths are expanded, and search_path
+ # checks to make sure the file is, in fact, relative to env.root[dir].
+ # An optional block may be used to check the file; the file will only
+ # be returned if the block returns true.
#
- # If the specified manifest does not exists, the manifest class
- # in self.class.manifests will be instatiated with self to make
- # the manifest. Raises an error if no manifest could be found
- # or instantiated.
- def manifest(name, build=false)
- manifest = manifests[name] ||= case
- when manifests_class = self.class.manifests[name]
- manifests_class.new(self)
- else
- raise "unknown manifest: #{name}"
+ # Returns nil if no file can be found.
+ def search_path(dir, path)
+ each do |env|
+ directory = env.root.filepath(dir)
+ file = env.root.filepath(dir, path)
+
+ # check the file is relative to the
+ # directory, and that the file exists.
+ if file.rindex(directory, 0) == 0 &&
+ File.exists?(file) &&
+ (!block_given? || yield(file))
+ return file
+ end
end
- manifest.build if build
- manifest
- end
-
- # Returns the first value in the specified manifest where the key
- # mini-matches the input pattern. See Tap::Root.minimal_match?
- # for details on mini-matching.
- def find(name, pattern, value_only=true)
- manifest(name).each do |key, value|
- return(value_only ? value : [key, value]) if Root.minimal_match?(key, pattern)
- end
nil
end
- # Like find, but searches across all envs for the matching value.
- # An env pattern can be provided in pattern, to select a single
- # env to search.
- #
- # The :envs manifest cannot be searched; use find instead.
- def search(name, pattern, value_only=true)
- if name == :envs
- raise ArgumentError, "cannot search the :envs manifest; use find instead"
- end
+ # def reset(name, &block)
+ # each do |env|
+ # env.manifests[name].each(&block)
+ # env.manifests[name] = nil
+ # end
+ # end
+
+ #
+ TEMPLATES = {}
+ TEMPLATES[:commands] = %Q{<% if count > 1 %>
+<%= env_name %>:
+<% end %>
+<% entries.each do |name, const| %>
+ <%= name.ljust(width) %>
+<% end %>}
+ TEMPLATES[:tasks] = %Q{<% if count > 1 %>
+<%= env_name %>:
+<% end %>
+<% entries.each do |name, const| %>
+<% desc = const.document[const.name]['manifest'] %>
+ <%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
+<% end %>}
+ TEMPLATES[:generators] = %Q{<% if count > 1 %>
+<%= env_name %>:
+<% end %>
+<% entries.each do |name, const| %>
+<% desc = const.document[const.name]['generator'] %>
+ <%= name.ljust(width) %><%= desc.empty? ? '' : ' # ' %><%= desc %>
+<% end %>}
+
+ def summarize(name, template=TEMPLATES[name])
+ count = 0
+ width = 10
- envs = case pattern
- when /^(.*):([^:]+)$/
- env_pattern = $1
- pattern = $2
- find(:envs, env_pattern)
- else manifest(:envs).values
+ env_names = {}
+ minimap.each do |env_name, env|
+ env_names[env] = env_name
end
- envs.each do |env|
- if result = env.find(name, pattern, value_only)
- return result
+ inspect(template) do |templater, share|
+ env = templater.env
+ entries = env.send(name).minimap
+ next(false) if entries.empty?
+
+ templater.env_name = env_names[env]
+ templater.entries = entries
+
+ count += 1
+ entries.each do |entry_name, entry|
+ width = entry_name.length if width < entry_name.length
end
- end if envs
-
- nil
+
+ share[:count] = count
+ share[:width] = width
+ true
+ end
end
- def constantize(name, *patterns)
- patterns.collect do |pattern|
- case const = search(name, pattern)
- when Support::Constant then const.constantize
- else raise "could not constantize: #{pattern} (#{name})"
- end
- end
+ def inspect(template=nil) # :yields: templater, attrs
+ return "#<#{self.class}:#{object_id} root='#{root.root}'>" if template == nil
+
+ attrs = {}
+ collect do |env|
+ templater = Support::Templater.new(template, :env => env)
+ block_given? ? (yield(templater, attrs) ? templater : nil) : templater
+ end.compact.collect do |templater|
+ templater.build(attrs)
+ end.join
end
- def summary(name)
- summary = Support::Summary.new
- manifest(:envs, true).minimize.each do |(key, env)|
- summary.add(key, env, env.manifest(name, true).minimize)
+ def recursive_inspect(template=nil, *args) # :yields: templater, attrs
+ return "#<#{self.class}:#{object_id} root='#{root.root}'>" if template == nil
+
+ attrs = {}
+ templaters = []
+ recursive_each(*args) do |env, *argv|
+ templater = Support::Templater.new(template, :env => env)
+ next_args = block_given? ? yield(templater, attrs, *argv) : argv
+ templaters << templater if next_args
+
+ next_args
end
- summary
+
+ templaters.collect do |templater|
+ templater.build(attrs)
+ end.join
end
- def summarize(name, &block)
- lines = summary(name).lines(&block)
- lines << "=== no #{name} found" if lines.empty?
- lines.join("\n")
- end
-
- def inspect(brief=false)
- brief ? "#<#{self.class}:#{object_id} root='#{root.root}'>" : super()
- end
+ protected
- def to_s
- inspect(true)
- end
+ # A hash of the manifests for self.
+ attr_reader :manifests
- protected
+ def minikey(env)
+ env.root.root
+ end
# Raises an error if self is already active (and hence, configurations
# should not be modified)
def check_configurable
raise "path configurations are disabled when active" if active?
\ No newline at end of file