class Chef::CookbookLoader

This class is used by knife, cheffs and legacy chef-solo modes. It is not used by the server mode of chef-client or zolo/zero modes.

This class implements orchestration around producing a single cookbook_version for a cookbook or loading a Mash of all cookbook_versions, using the cookbook_version_loader class, and doing lazy-access and memoization to only load each cookbook once on demand.

This implements a key-value style each which makes it appear to be a Hash of String => CookbookVersion pairs where the String is the cookbook name. The use of Enumerable combined with the Hash-style each is likely not entirely sane.

This object is also passed and injected into the CookbookCollection object where it is converted to a Mash that looks almost exactly like the cookbook_by_name Mash in this object.

Attributes

repo_paths[R]

@return [Array<String>] the array of repo paths containing cookbook dirs

Public Class Methods

new(*repo_paths) click to toggle source

@param repo_paths [Array<String>] the array of repo paths containing cookbook dirs

# File lib/chef/cookbook_loader.rb, line 51
def initialize(*repo_paths)
  @repo_paths = repo_paths.flatten.map { |p| File.expand_path(p) }
  raise ArgumentError, "You must specify at least one cookbook repo path" if repo_paths.empty?
end

Public Instance Methods

[](cookbook) click to toggle source
# File lib/chef/cookbook_loader.rb, line 103
def [](cookbook)
  load_cookbook(cookbook)
end
Also aliased as: fetch
cookbook_exists?(cookbook_name)
Alias for: has_key?
cookbook_names() click to toggle source
# File lib/chef/cookbook_loader.rb, line 130
def cookbook_names
  cookbooks_by_name.keys.sort
end
cookbooks()
Alias for: values
cookbooks_by_name() click to toggle source

The primary function of this class is to build this Mash mapping cookbook names as a string to the CookbookVersion objects for them. Callers must call “load_cookbooks” first.

@return [Mash<String, Chef::CookbookVersion>]

# File lib/chef/cookbook_loader.rb, line 60
def cookbooks_by_name
  @cookbooks_by_name ||= Mash.new
end
each() { |cname, cookbooks_by_name| ... } click to toggle source
# File lib/chef/cookbook_loader.rb, line 116
def each
  cookbooks_by_name.keys.sort_by(&:to_s).each do |cname|
    yield(cname, cookbooks_by_name[cname])
  end
end
each_key(&block) click to toggle source
# File lib/chef/cookbook_loader.rb, line 122
def each_key(&block)
  cookbook_names.each(&block)
end
each_value(&block) click to toggle source
# File lib/chef/cookbook_loader.rb, line 126
def each_value(&block)
  values.each(&block)
end
fetch(cookbook)
Alias for: []
has_key?(cookbook_name) click to toggle source
# File lib/chef/cookbook_loader.rb, line 109
def has_key?(cookbook_name)
  not self[cookbook_name.to_sym].nil?
end
Also aliased as: cookbook_exists?, key?
key?(cookbook_name)
Alias for: has_key?
load_cookbook(cookbook_name) click to toggle source

Loads a single cookbook by its name.

@param [String] @return [Chef::CookbookVersion]

# File lib/chef/cookbook_loader.rb, line 86
def load_cookbook(cookbook_name)
  unless cookbook_version_loaders.key?(cookbook_name)
    raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook_name}; did you forget to add metadata to a cookbook? (https://docs.chef.io/config_rb_metadata.html)"
  end

  return cookbooks_by_name[cookbook_name] if cookbooks_by_name.key?(cookbook_name)

  loader = cookbook_version_loaders[cookbook_name]

  loader.load

  cookbook_version = loader.cookbook_version
  cookbooks_by_name[cookbook_name] = cookbook_version
  metadata[cookbook_name] = cookbook_version.metadata
  cookbook_version
end
load_cookbooks() click to toggle source

Loads all cookbooks across all repo_paths

@return [Mash<String, Chef::CookbookVersion>] the cookbooks_by_name Mash

# File lib/chef/cookbook_loader.rb, line 75
def load_cookbooks
  cookbook_version_loaders.each_key do |cookbook_name|
    load_cookbook(cookbook_name)
  end
  cookbooks_by_name
end
metadata() click to toggle source

This class also builds a mapping of cookbook names to their Metadata objects. Callers must call “load_cookbooks” first.

@return [Mash<String, Chef::Cookbook::Metadata>]

# File lib/chef/cookbook_loader.rb, line 68
def metadata
  @metadata ||= Mash.new
end
values() click to toggle source
# File lib/chef/cookbook_loader.rb, line 134
def values
  cookbooks_by_name.values
end
Also aliased as: cookbooks

Private Instance Methods

all_directories_in_repo_paths() click to toggle source
# File lib/chef/cookbook_loader.rb, line 152
def all_directories_in_repo_paths
  @all_directories_in_repo_paths ||=
    all_files_in_repo_paths.select { |path| File.directory?(path) }
end
all_files_in_repo_paths() click to toggle source
# File lib/chef/cookbook_loader.rb, line 157
def all_files_in_repo_paths
  @all_files_in_repo_paths ||=
    begin
      repo_paths.inject([]) do |all_children, repo_path|
        all_children + Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")]
      end
    end
end
chefignore(repo_path) click to toggle source

Helper method to lazily create and remember the chefignore object for a given repo_path.

@param [String] repo_path the full path to the cookbook directory of the repo @return [Chef::Cookbook::Chefignore] the chefignore object for the repo_path

# File lib/chef/cookbook_loader.rb, line 147
def chefignore(repo_path)
  @chefignores ||= {}
  @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path)
end
cookbook_version_loaders() click to toggle source

This method creates a Mash of the CookbookVersionLoaders for each cookbook.

@return [Mash<String, Cookbook::CookbookVersionLoader>]

# File lib/chef/cookbook_loader.rb, line 169
def cookbook_version_loaders
  @cookbook_version_loaders ||=
    begin
      mash = Mash.new
      all_directories_in_repo_paths.each do |cookbook_path|
        loader = Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore(File.dirname(cookbook_path)))
        cookbook_name = loader.cookbook_name
        if mash.key?(cookbook_name)
          raise Chef::Exceptions::CookbookMergingError, "Cookbook merging is no longer supported, the cookbook named #{cookbook_name} can only appear once in the cookbook_path"
        end
        mash[cookbook_name] = loader
      end
      mash
    end
end