Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a command_missing hook #315

Merged
merged 2 commits into from
Jan 26, 2022

Conversation

camertron
Copy link
Contributor

@camertron camertron commented Jan 22, 2022

First off, thank you so much for GLI. I'm a huge fan ❤️

I'd like to propose adding a new hook called command_missing which works much like Ruby's method_missing method. command_missing is called whenever a top-level command can't be found, and expects the block it captures to return an instance of GLI::Command.

I'm the author of the Kuby project, a tool for deploying Rails apps. Kuby features a plugin system, and therein lies my use-case. I would like plugins to have the ability to specify their own sets of commands, eg. kuby plugin_name plugin_subcommand ... The problem is that the list of plugins isn't known until the config file is loaded, which happens in a GLI pre hook. I need to be able to defer defining commands until the config file has been loaded, but before all the CLI options have been parsed. With the changes in this PR, the following is now possible:

module Kuby
  class Commands
    extend GLI::App

    def self.load_kuby_config!(global_options)
      # loading stuff here
    end

    command_missing do |command_name, global_options|
      load_kuby_config!(global_options)

      # command_name is also the name of the plugin
      if plugin_klass = Kuby.plugins.find(command_name)
        if plugin_klass.respond_to?(:commands)
          desc "Run commands for the #{command_name} plugin."
          command command_name do |c|
            # the plugin now defines its own commands on c
            plugin_klass.commands(c)
          end
        end
      end
    end

    pre do |global_options, options, args|
      load_kuby_config!
      # more code here
    end
  end
end

One of the key changes here is that the command method now returns the command object, and since that's the last line of the command_missing block, it gets returned to the parser. It's a bit funky to be sure, but it works.

Let me know what you think!

Much like Ruby's `method_missing` method, `command_missing` is called whenever a top-level command can't be found (such a feature can be useful for dynamically defining commands). The `command_missing` method expects the block it captures to return an instance of `GLI::Command`.
@davetron5000
Copy link
Owner

Hey, thanks for the PR. Quick scan it looks good. Did you know about commands_from? It's a glorified require but that was added to allow GLI commands to have plugins. Though I guess if it's not run when the app loads it may not work? Can you see if that would work for your use case? If not, I'll give this a more detailed review

@camertron
Copy link
Contributor Author

camertron commented Jan 23, 2022

@davetron5000 oh cool I didn't know about commands_from 😄 I took a look at the docs and unfortunately I don't think it covers my use-case. Part of my particular problem is that load_kuby_config! depends on the global_options hash (because it contains the path to the Kuby config file), which AFAIK isn't available until GLI::GlobalOptionParser is finished doing its thing.

I also tried adding the commands inside the pre hook, but it looks like commands have to exist before pre is called.

@davetron5000
Copy link
Owner

OK, sounds good. Like I said, this looks great, so gonna merge and do a realease real quick

@davetron5000 davetron5000 merged commit 7fe92c4 into davetron5000:main Jan 26, 2022
@davetron5000
Copy link
Owner

Released as 2.21.0. Thanks!

@camertron
Copy link
Contributor Author

Sweet, thank you!

@camertron camertron deleted the command_missing_hook branch April 13, 2022 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants