class ChefCLI::CommandsMap
end
c.builtin(“documented-cmd”, :DocumentedCmd, desc: “A short description”)
# You can add a description that will show up in ‘chef -h` output (recommended):
c.builtin(“weird-command”, :WeirdoClass, require_path: “chef-cli/command/this_is_cray”)
# Set the require path explicitly:
c.builtin(“my-command”, :MyCommand)
# The “require path” is inferred to be “chef-cli/command/my_command”
# assigns `chef my-command` to the class ChefCLI::Command::MyCommand.
ChefCLI.commands do |c|
ChefCLI.commands, like so:
You can configure a multiple commands at once in a block using
A “singleton-ish” instance of this class is stored as ChefCLI.commands_map.
## Adding new commands globally:
4. It’s not actually that much work to maintain the mapping.
their own complications and tradeoffs and don’t fully solve the problem.
3. Other ways to mitigate the performance issue (loading deps lazily) have
commands, which is actually a common thing to do.
command name, but then you have to do a lot of work to list all of the
2. You can workaround the above by having a convention mapping filename to
slow, and CLI apps need to be fast.
code, including dependencies that are installed by rubygems, etc. This gets
1. Performance. As the CLI suite grows, you have to load more and more
decided against it here:
and metaprogramming. We’ve implemented this approach in the past and
In ruby it’s more typical to handle this sort of thing using conventions
those commands are defined and the classes that implement the commands.
CommandsMap maintains a mapping of subcommand names to the files where
def builtin(name, constant_name, require_path: NULL_ARG, desc: "", hidden: false)
def builtin(name, constant_name, require_path: NULL_ARG, desc: "", hidden: false) if null?(require_path) snake_case_path = name.tr("-", "_") require_path = "chef-cli/command/#{snake_case_path}" end command_specs[name] = CommandSpec.new(name, constant_name, require_path, desc, hidden) end
def command_names
def command_names command_specs.keys end
def have_command?(name)
def have_command?(name) command_specs.key?(name) end
def initialize
def initialize @command_specs = {} end
def instantiate(name)
def instantiate(name) spec_for(name).instantiate end
def null?(argument)
def null?(argument) argument.equal?(NULL_ARG) end
def spec_for(name)
def spec_for(name) command_specs[name] end