Closed mrjcleaver closed 3 years ago
You could do this in your app by walking a pre-configured directory and doing a commands_from
for each of those.
So, suppose you told your users that if they put commands in ~/.my_app/commands
, you'd load them as custom commands.
Suppose a user created this set-up:
~/.my_app/commands/foo.rb
~/.my_app/commands/other_commands/bar.rb
~/.my_app/commands/yet/more/deep/commands/baz.rb
Your app could do something like:
Dir.glob(File.join(ENV['HOME'],"~/.my_app","commands")) do |file_or_dir|
if File.directory?(file_or_dir)
commands_from file_or_dir
end
end
That would basically load whatever it found at runtime. Could be very slow for big locations, but might do what you are looking for.
I was wanting the same structure and went about it a different way. I just noticed this issue and wanted to comment in case someone else needed help.
My method lazy loads the command files given the current command and looks for plugins or relative commands within the same gem and external gems.
There is a bit more to my code than what I provided but essentially it just finds the commands relative to the current command name and evals them within the current context. I don't know if eval_instance was the best choice here, but it works and is fast. Debugging which command file is currently evaluated is still an issue because of the eval. I am also loading commands from third party plugins because I wanted the ability for someone to provide a subcommand at any level if needed. However, they are not allowed to override "core" commands.
The internal files and external files are scoped to the directory structure of the current command.
module GLI
class Command
# loads the command file in the context of the this command
def load_sub_commands
(internal_files + external_files).map do |file|
# evals the file under the command instance of the parent command
instance_eval(File.read(file))
file
end
end
end
# lib/cli/cu/volt.rb
desc 'Shows a list of compute units voltages and tables'
command :volt do |volt|
volt.default_command :help
volt.load_sub_commands
end
# lib/cli/cu/volt/core.rb
desc 'Shows a list of compute unit voltages'
command :core do |core|
core.action do |_global_options, options, _args|
client.run_action -> do
require 'crossbelt/commands/compute_unit/volt'
Crossbelt::Command::ComputeUnit::Volt.new(options).core_voltage
end
end
end
File structure.
lib/crossbelt/cli
├── cu
│ ├── clocks.rb
│ ├── cost.rb
│ ├── info.rb
│ ├── list.rb
│ ├── mem.rb
│ ├── power.rb
│ ├── profit.rb
│ ├── profitcost.rb
│ ├── volt
│ │ ├── core.rb
│ │ └── table.rb
│ └── volt.rb
├── cu.rb
I would be happy to contribute some of this code to make this part of GLI but I still have some minor issues to work out and static values specific to my app. I believe the only issue right now is the -h
flag doesn't seem to respect the current scope for deeper level subcommands.
closing as kinda old. I think command_from
is a fine solution to this.
Hi, I was thinking to have contributors add to my cli by them contributing configuration files.
I was wondering if there was a way for me to avoid having to continually add to the main config file by having GLI pull in the descriptors as distributed across a folder structure.
I'd assume the folder structure had filenames that corresponded to subcommands, such that the presence of the filename is enough to make the subcommand available.
Thanks, Martin.