davetron5000 / gli

Make awesome command-line applications the easy way
http://davetron5000.github.io/gli
Apache License 2.0
1.26k stars 102 forks source link

How to call a command with another command #313

Closed ttscoff closed 2 years ago

ttscoff commented 2 years ago

I dug through the code to try to answer this myself, but haven't quite figured it out yet. I want to call a command with specific flags set from within another command. Is there a method for that?

Basically I want a subcommand that executes another command with a bunch of flags and switches but all preset so that app command has the same effect as app othercommand --flag=val --switch --flag2=val2 --no-switch, etc.

davetron5000 commented 2 years ago

an inelgeant way would be to use exec which replaces the current process with whatever you give it:

exec("#{$0} othercommand --flag=val --switch --flag2=val2 --no-switch")

Another option would be to access the object for that command (via the commands method that should be available wherever you mixed-in App [but note this is hierarchical so you'd have to walk the tree of commands]), and then execute its action block:

options = {
  flag: val,
  switch: true,
  flag2: val,
  other_switch: false,
}
command.action.(global_options, options,args)

A third option would be to store othercommand's action block as some global variable or variable somewhere and use that:

othercommand_action = ->(global_options, options, args) {
  # whatever the command does
}

command :command do |c|
  options = {
    flag: val,
    switch: true,
    flag2: val,
    other_switch: false,
  }
  c.action do |g,o,a|
    othercommand_action.(g,options,a)
  end
end

command :othercommand do |c|
  c.action do |g,o,a|
    othercommand_action.(g,o,a)
  end
end
ttscoff commented 2 years ago

Option 2 is exactly what I was hoping for. Will try it out with option 3 as a fallback. Thanks.

ttscoff commented 2 years ago

I might actually need a bit more explanation of how to call the action for option 2. My ruby skills are showing their limits.

I can retrieve the command in question using commands[:tag], and printing out the object shows me all of its flags and methods, including @action as a Proc. However, commands[:tag].action.inspect is nil, and running commands[:tag].action.(g, o, a) gives me an error about calling call on nil. Is there some other method by which I need to call the command?

davetron5000 commented 2 years ago

No, you are correct. It looks like I told you wrong. #action is what sets the action block and can't retrieve it.

There is a private method called get_action that will return the action proc (e.g. with command.send(:get_action)).

Calling private methods isn't advised, but it's unlikely to change TBH so it could be done safely. Note that it may return a generated error handler if the command didn't have an action:

https://github.com/davetron5000/gli/blob/8edc77ab3b7971cefa7a133ddd211efc50bb4a1f/lib/gli/command_support.rb#L159-L165

To know if that will happen you can call the public has_action? method:

https://github.com/davetron5000/gli/blob/8edc77ab3b7971cefa7a133ddd211efc50bb4a1f/lib/gli/command_support.rb#L144-L146

ttscoff commented 2 years ago

Works perfectly, thank you.

(Also, GLI is one of my favorite things. Just in general. It's awesome and I'd hate to create CLIs without it. So thank you for the amazing contribution!)

davetron5000 commented 2 years ago

Awesome and thanks for the kind words!