Class: ChefCLI::CommandsMap
- Inherits:
-
Object
- Object
- ChefCLI::CommandsMap
- Defined in:
- lib/chef-cli/commands_map.rb
Overview
CommandsMap maintains a mapping of subcommand names to the files where those commands are defined and the classes that implement the commands.
In ruby it’s more typical to handle this sort of thing using conventions and metaprogramming. We’ve implemented this approach in the past and decided against it here:
-
Performance. As the CLI suite grows, you have to load more and more
code, including dependencies that are installed by rubygems, etc. This gets slow, and CLI apps need to be fast.
-
You can workaround the above by having a convention mapping filename to
command name, but then you have to do a lot of work to list all of the commands, which is actually a common thing to do.
-
Other ways to mitigate the performance issue (loading deps lazily) have
their own complications and tradeoffs and don’t fully solve the problem.
-
It’s not actually that much work to maintain the mapping.
## Adding new commands globally:
A “singleton-ish” instance of this class is stored as ChefCLI.commands_map. You can configure a multiple commands at once in a block using ChefCLI.commands, like so:
ChefCLI.commands do |c|
# assigns `chef my-command` to the class ChefCLI::Command::MyCommand.
# The "require path" is inferred to be "chef-cli/command/my_command"
c.builtin("my-command", :MyCommand)
# Set the require path explicitly:
c.builtin("weird-command", :WeirdoClass, require_path: "chef-cli/command/this_is_cray")
# You can add a description that will show up in `chef -h` output (recommended):
c.builtin("documented-cmd", :DocumentedCmd, desc: "A short description")
end
Defined Under Namespace
Classes: CommandSpec
Constant Summary collapse
- NULL_ARG =
Object.new
Instance Attribute Summary collapse
-
#command_specs ⇒ Object
readonly
Returns the value of attribute command_specs.
Instance Method Summary collapse
- #builtin(name, constant_name, require_path: NULL_ARG, desc: "", hidden: false) ⇒ Object
- #command_names ⇒ Object
- #have_command?(name) ⇒ Boolean
-
#initialize ⇒ CommandsMap
constructor
A new instance of CommandsMap.
- #instantiate(name) ⇒ Object
- #spec_for(name) ⇒ Object
Constructor Details
#initialize ⇒ CommandsMap
Returns a new instance of CommandsMap.
71 72 73 |
# File 'lib/chef-cli/commands_map.rb', line 71 def initialize @command_specs = {} end |
Instance Attribute Details
#command_specs ⇒ Object (readonly)
Returns the value of attribute command_specs.
69 70 71 |
# File 'lib/chef-cli/commands_map.rb', line 69 def command_specs @command_specs end |
Instance Method Details
#builtin(name, constant_name, require_path: NULL_ARG, desc: "", hidden: false) ⇒ Object
75 76 77 78 79 80 81 |
# File 'lib/chef-cli/commands_map.rb', line 75 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 |
#command_names ⇒ Object
91 92 93 |
# File 'lib/chef-cli/commands_map.rb', line 91 def command_names command_specs.keys end |
#have_command?(name) ⇒ Boolean
87 88 89 |
# File 'lib/chef-cli/commands_map.rb', line 87 def have_command?(name) command_specs.key?(name) end |
#instantiate(name) ⇒ Object
83 84 85 |
# File 'lib/chef-cli/commands_map.rb', line 83 def instantiate(name) spec_for(name).instantiate end |
#spec_for(name) ⇒ Object
95 96 97 |
# File 'lib/chef-cli/commands_map.rb', line 95 def spec_for(name) command_specs[name] end |