class Chef::Knife::SubcommandLoader

Public Methods of a Subcommand Loader

load_commands - loads all available subcommands load_command(args) - loads subcommands for the given args list_commands(args) - lists all available subcommands,

optionally filtering by category

subcommand_files - returns an array of all subcommand files

that could be loaded

command_class_from(args) - returns the subcommand class for the

user-requested command

Attributes

chef_config_dir[R]

Public Class Methods

autogenerated_manifest?() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 67
def self.autogenerated_manifest?
  plugin_manifest? && plugin_manifest.key?(HashedCommandLoader::KEY)
end
for_config(chef_config_dir) click to toggle source

A small factory method. Eventually, this is the only place where SubcommandLoader should know about its subclasses, but to maintain backwards compatibility many of the instance methods in this base class contain default implementations of the functions sub classes should otherwise provide or directly instantiate the appropriate subclass

# File lib/chef/knife/core/subcommand_loader.rb, line 48
def self.for_config(chef_config_dir)
  if autogenerated_manifest?
    Chef::Log.trace("Using autogenerated hashed command manifest #{plugin_manifest_path}")
    Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
  else
    Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
  end
end
gem_glob_loader(chef_config_dir) click to toggle source

There are certain situations where we want to shortcut the loader selection in self.for_config and force using the GemGlobLoader

# File lib/chef/knife/core/subcommand_loader.rb, line 59
def self.gem_glob_loader(chef_config_dir)
  Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
end
generate_hash() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 79
def self.generate_hash
  output = if plugin_manifest?
             plugin_manifest
           else
             { Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY => {} }
           end
  output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_paths"] = Chef::Knife.subcommand_files
  output[Chef::Knife::SubcommandLoader::HashedCommandLoader::KEY]["plugins_by_category"] = Chef::Knife.subcommands_by_category
  output
end
new(chef_config_dir) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 98
def initialize(chef_config_dir)
  @chef_config_dir = chef_config_dir
end
plugin_manifest() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 71
def self.plugin_manifest
  Chef::JSONCompat.from_json(File.read(plugin_manifest_path))
end
plugin_manifest?() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 63
def self.plugin_manifest?
  plugin_manifest_path && File.exist?(plugin_manifest_path)
end
plugin_manifest_path() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 75
def self.plugin_manifest_path
  ChefConfig::PathHelper.home(".chef", "plugin_manifest.json")
end
write_hash(data) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 90
def self.write_hash(data)
  plugin_manifest_dir = File.expand_path("..", plugin_manifest_path)
  FileUtils.mkdir_p(plugin_manifest_dir) unless File.directory?(plugin_manifest_dir)
  File.open(plugin_manifest_path, "w") do |f|
    f.write(Chef::JSONCompat.to_json_pretty(data))
  end
end

Public Instance Methods

command_class_from(args) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 128
def command_class_from(args)
  cmd_words = positional_arguments(args)
  load_command(cmd_words)
  result = Chef::Knife.subcommands[find_longest_key(Chef::Knife.subcommands,
    cmd_words, "_")]
  result || Chef::Knife.subcommands[args.first.tr("-", "_")]
end
find_longest_key(hash, words, sep = "_") click to toggle source

Utility function for finding an element in a hash given an array of words and a separator. We find the the longest key in the hash composed of the given words joined by the separator.

# File lib/chef/knife/core/subcommand_loader.rb, line 166
def find_longest_key(hash, words, sep = "_")
  words = words.dup
  match = nil
  until match || words.empty?
    candidate = words.join(sep).tr("-", "_")
    if hash.key?(candidate)
      match = candidate
    else
      words.pop
    end
  end
  match
end
find_subcommands_via_dirglob() click to toggle source

This is shared between the custom_manifest_loader and the gem_glob_loader

# File lib/chef/knife/core/subcommand_loader.rb, line 145
def find_subcommands_via_dirglob
  # The "require paths" of the core knife subcommands bundled with chef
  files = Dir[File.join(ChefConfig::PathHelper.escape_glob_dir(File.expand_path("../../knife", __dir__)), "*.rb")]
  version_file_match = /#{Regexp.escape(File.join('chef', 'knife', 'version.rb'))}/
  subcommand_files = {}
  files.each do |knife_file|
    rel_path = knife_file[/#{KNIFE_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/, 1]
    # Exclude version.rb file for the gem. It's not a knife command, and  force-loading it later
    # because loaded via in subcommand files generates CLI warnings about its consts already having been defined
    next if knife_file =~ version_file_match

    subcommand_files[rel_path] = knife_file
  end
  subcommand_files
end
force_load() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 110
def force_load
  @loaded = false
  load_commands
end
guess_category(args) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 136
def guess_category(args)
  category_words = positional_arguments(args)
  category_words.map! { |w| w.split("-") }.flatten!
  find_longest_key(Chef::Knife.subcommands_by_category,
    category_words, " ")
end
list_commands(pref_cat = nil) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 119
def list_commands(pref_cat = nil)
  load_commands
  if pref_cat && Chef::Knife.subcommands_by_category.key?(pref_cat)
    { pref_cat => Chef::Knife.subcommands_by_category[pref_cat] }
  else
    Chef::Knife.subcommands_by_category
  end
end
load_command(_command_args) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 115
def load_command(_command_args)
  load_commands
end
load_commands() click to toggle source

Load all the sub-commands

# File lib/chef/knife/core/subcommand_loader.rb, line 103
def load_commands
  return true if @loaded

  subcommand_files.each { |subcommand| Kernel.load subcommand }
  @loaded = true
end
positional_arguments(args) click to toggle source

The positional arguments from the argument list provided by the users. Used to search for subcommands and categories.

@return [Array<String>]

# File lib/chef/knife/core/subcommand_loader.rb, line 186
def positional_arguments(args)
  args.select { |arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
end
site_subcommands() click to toggle source

Returns an Array of paths to knife commands located in chef_config_dir/plugins/knife/ and ~/.chef/plugins/knife/

# File lib/chef/knife/core/subcommand_loader.rb, line 192
def site_subcommands
  user_specific_files = []

  if chef_config_dir
    user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", ChefConfig::PathHelper.escape_glob_dir(chef_config_dir)))
  end

  # finally search ~/.chef/plugins/knife/*.rb
  ChefConfig::PathHelper.home(".chef", "plugins", "knife") do |p|
    user_specific_files.concat Dir.glob(File.join(ChefConfig::PathHelper.escape_glob_dir(p), "*.rb"))
  end

  user_specific_files
end