lib/linecook/cookbook.rb in linecook-1.2.1 vs lib/linecook/cookbook.rb in linecook-2.0.0

- old
+ new

@@ -1,160 +1,178 @@ -require 'linecook/utils' -require 'lazydoc' - module Linecook class Cookbook class << self - def config_file(project_dir='.') - Dir.glob(File.join(project_dir, '{C,c}ookbook')).first + attr_writer :default_path_map + def default_path_map + @default_path_map ||= { + :attributes => ['attributes'], + :files => ['files'], + :templates => ['templates'], + :recipes => ['recipes'] + } end - - def setup(config={}, project_dir='.') - unless config.kind_of?(Hash) - config = Utils.load_config(config) - end - - config[PATHS_KEY] ||= [project_dir] - config[GEMS_KEY] ||= gems - - new(config, project_dir) + + attr_writer :default_file_name + def default_file_name + @default_file_name ||= 'cookbook.yml' end - - def init(project_dir='.') - setup config_file(project_dir), project_dir - end - - def gems + + def gemspecs(latest=true) return [] unless Object.const_defined?(:Gem) - - Gem.source_index.latest_specs.select do |spec| - config_file(spec.full_gem_path) != nil - end.collect do |spec| - spec.name + + index = Gem.source_index + specs = latest ? index.latest_specs : index.gems.values + + specs.select do |spec| + cookbook_file = File.expand_path(default_file_name, spec.full_gem_path) + File.exists?(cookbook_file) end end end - - MANIFEST_KEY = 'manifest' - PATHS_KEY = 'paths' - GEMS_KEY = 'gems' - REWRITE_KEY = 'rewrite' - - PATTERNS = [ - ['attributes', '**/*{.rb,.yaml,.yml,.json}', true], - ['files', '**/*'], - ['recipes', '**/*.rb', true], - ['templates', '**/*'] - ] - - attr_reader :project_dir - attr_reader :config - - def initialize(config={}, project_dir='.') - @project_dir = project_dir - @config = config + + attr_reader :registry + + def initialize(*project_dirs) + @registry = {} + project_dirs.each do |dir| + case dir + when String then add(dir) + when Hash then add('.', dir) + else add(*dir) + end + end end - - def paths - Utils.arrayify config[PATHS_KEY] + + # Returns an array of directories comprising the path for type. + def path(type) + registry[type] || [] end - - def gems - Utils.arrayify config[GEMS_KEY] - end - - def rewrites - config[REWRITE_KEY] - end - - def overrides - config[MANIFEST_KEY] - end - - def full_gem_paths - return [] if gems.empty? - specs = latest_specs - - gems.collect do |name| - spec = specs[name] or raise "no such gem: #{name.inspect}" - spec.full_gem_path + + def add(dir, path_map=nil) + resolve_path_map(dir, path_map).each_pair do |type, paths| + (registry[type] ||= []).concat(paths) end end - - def rewrite(manifest) - replacements = {} - - rewrites.each_pair do |pattern, substitution| - manifest.keys.each do |key| - replacement = key.sub(pattern, substitution) - next if key == replacement - raise "multiple replacements for: #{key}" if replacements.has_key?(key) - - replacements[key] = replacement + + def rm(dir, path_map=nil) + resolve_path_map(dir, path_map).each_pair do |type, paths| + if current = registry[type] + current = current - paths + if current.empty? + registry.delete(type) + else + registry[type] = current + end end - end if rewrites - - replacements.each do |key, replacement| - manifest[replacement] = manifest.delete(key) end - - manifest end - - def glob(*paths) - manifest = Hash.new {|hash, key| hash[key] = {} } - - paths.each do |path| - PATTERNS.each do |(type, glob, chomp_extname)| - resource_dir = File.expand_path(File.join(path, type), project_dir) - pattern = File.join(resource_dir, glob) - - Dir.glob(pattern).each do |full_path| - next unless File.file?(full_path) - - name = relative_path(resource_dir, full_path) - name.chomp!(File.extname(full_path)) if chomp_extname - - manifest[type][name] = full_path + + # Same as find but returns nil if no file can be found. + def _find_(type, filename, extnames=nil) + if type.nil? || filename.nil? + return nil + end + + if absolute?(filename) + return File.exists?(filename) ? filename : nil + end + + path(type).each do |dir| + each_full_path(dir, filename, extnames) do |full_path| + if File.exists?(full_path) && subpath?(dir, full_path) + return full_path end end end - - manifest + + nil end - - def manifest - manifest = glob(*(full_gem_paths + paths)) - - if overrides - manifest = Utils.deep_merge(manifest, overrides) + + # Searches for a file by expanding filename vs each directory in the path + # for type. The first existing full path is returned. If an array of + # extnames is provided, then each extname is tried for each directory, + # much as with Kernal.require. Absolute paths that exists are returned + # directly. Raises an error if the file cannot be found. + def find(type, source_name, extnames=nil) + _find_(type, source_name, extnames) or begin + case + when type.nil? + raise "could not find file: #{source_name.inspect} (nil type specified)" + when source_name.nil? + raise "could not find file: #{source_name.inspect}" + when absolute?(source_name) + raise "no such file: #{source_name.inspect}" + else + try_string = try_extnames?(source_name, extnames) ? " (tried #{extnames.join(', ')})" : nil + raise "could not find file: #{source_name.inspect}#{try_string}" + end end - - manifest.each_key do |key| - manifest[key] = rewrite manifest[key] + end + + protected + + if Dir.pwd[0] == ?/ + def absolute?(path) + path && path[0] == ?/ end - - manifest + else + def absolute?(path) + path && path =~ /^[A-z]:\// + end end - - def merge(config={}) - duplicate = dup - dup.config.merge!(config) - dup + + def subpath?(dir, full_path) + full_path.index(dir) == 0 end - - private - - def relative_path(dir, path) # :nodoc: - start = dir.length + 1 - path[start, path.length - start] + + def try_extnames?(path, extnames) + extnames && File.extname(path).empty? end - - def latest_specs # :nodoc: - latest = {} - Gem.source_index.latest_specs.each do |spec| - latest[spec.name] = spec + + def each_full_path(dir, path, extnames=nil) + full_path = File.expand_path(path, dir) + yield full_path + + if try_extnames?(path, extnames) + extnames.each do |extname| + full_path = File.expand_path("#{path}#{extname}", dir) + yield full_path + end end - latest + end + + def resolve_path_map(dir, path_map=nil) + path_map ||= self.class.default_file_name + + case path_map + when Hash + expand_path_map(dir, path_map) + when String + cookbook_file = File.expand_path(path_map, dir) + path_map = File.exists?(cookbook_file) ? YAML.load_file(cookbook_file) : nil + path_map ||= self.class.default_path_map + + unless path_map.kind_of?(Hash) + raise "could not load path map: #{cookbook_file.inspect} (does not load a Hash)" + end + + expand_path_map(dir, path_map) + else + raise "could not resolve path map: #{path_map.inspect} (must be String, Hash, or nil)" + end + end + + def expand_path_map(dir, path_map) + results = Hash.new {|hash, key| hash[key] = [] } + + path_map.each_pair do |type, paths| + unless paths.kind_of?(Array) + paths = [paths] + end + paths.each do |path| + results[type.to_sym] << File.expand_path(path, dir) + end + end + results end end end \ No newline at end of file