Closed logicminds closed 5 years ago
Yeah, the commands_from
method is a way to do that (it is a fancy wrapper around require
).
http://davetron5000.github.io/gli/rdoc/classes/GLI/App.html#method-i-commands_from
Let me know if that's sufficient or if you need more info on how it works.
I can't figure out what to do with the commands_from.
Do I create a class that uses include Gli::App
for the subcommand / decomposition piece?
Do I override desc and command inside the class?
Any example source code with examples you can toss my way?
Do you have your work in progress you could share?
There is a test app in this repo that uses it: https://github.com/davetron5000/gli/blob/gli-2/test/apps/todo/bin/todo#L36
But basically, if your bin/my_app
looks like so:
require "gli"
include GLI::App
commands_from "my_app/commands"
And then, assuming you are working in a Ruby Gem:
# lib/my_app/commands/list.rb
desc "List stuff"
command [ :list, :ls ] do |c|
# ... normal stuff you'd do
end
# lib/my_app/commands/new.rb
desc "Create a thing"
command [ :new ] do |c|
# ...
end
note that commands_from
uses the current Ruby path, so if you do this in a RubyGem, you'll have to run your app via bundle exec bin/my_app
while doing development. Once you bundle your app as a gem and distribute it, everything should work as normal (e.g. my_app
)
Let me know if that helps, but if not, and you can share your work in progress, I might be able to give more specific help on that.
In my bin file I have:
#!/usr/bin/env ruby
require 'crossbelt/cli'
cli = Crossbelt::Cli.new
exit cli.run(ARGV)
where the Cli class looks something like:
module Crossbelt
class Cli
include GLI::App
def initialize
initialize_gli
end
def spinner
@spinner ||= TTY::Spinner.new
end
def clear_screen
puts "\e[H\e[2J"
end
def client
@client ||= Crossbelt::Client.new
end
def initialize_gli
program_desc 'The Crossbelt command line app'
desc "Update Crossbelt Cloud Rig Info"
command :update do |update|
update.default_command :one_off
update.desc "One off manual update"
update.command :one_off do |one_off|
one_off.action do |global_options,options,args|
spinner.auto_spin unless ENV['GLI_DEBUG']
client.run_action -> do
require 'crossbelt/commands/update'
Crossbelt::Command::Update.new(options).run
end
spinner.stop unless ENV['GLI_DEBUG']
end
end
end
end
end
It is my understanding that if I use commands_from that it would go into my initialize_gli method.
def initialize_gli
program_desc 'The Crossbelt command line app'
commands_from "crossbelt/commands"
end
Then inside the update command file crossbelt/commands/update.rb I have
require 'crossbelt/command_action'
# crossbelt/commands/update.rb
update.command :one_off do |one_off|
one_off.action do |global_options,options,args|
spinner.auto_spin unless ENV['GLI_DEBUG']
client.run_action -> do
require 'crossbelt/commands/update'
Crossbelt::Command::Update.new(options).run
end
spinner.stop unless ENV['GLI_DEBUG']
end
end
end
end
module Crossbelt
module Command
class Update < Crossbelt::CommandAction
def initialize(opts = {})
@options = opts
end
def run
update_data
end
end
end
end
At the moment I am getting the following error:
be exe/cb
bundler: failed to load command: exe/cb (exe/cb)
NoMethodError: undefined method `desc' for main:Object
Also I started down another path and was trying to not use the DSL and instead subclass the GLI::Command class. Although this did not seem to make much sense since I would have to create a create a bunch of heavy weight subclassed command objects upfront vs the lightweight version the DSL creates now.
OK, you have a slightly unusual setup, but I see what you need to do.
commands_from
is really doing a require
underneath, so the files you are extracting your commands into need to have the same class structure.
Putting GLI's DSL into a class and into methods is probably not what you want. If you don't want to dump everything into bin/
you'll need to:
# bin/my_app
#!/usr/bin/env ruby
require "cli"
exit Crossbelt::Cli.run(ARGV)
# lib/crossbelt/cli.rb
module Crossbelt
module Cli
extend GLI::App
program_desc "My App"
commands_from "crossbelt/commands"
end
end
# lib/crossbelt/commands
module Crossbelt
module Cli
desc "Some command"
command :foo do |c|
# ...
end
end
end
This worked great. Thanks.
I have an app that has a plugin system and currently using GLI. Right now I just have one big file that uses the GLI DSL for all my commands. However, I would like to split this into multiple files where each command would have its own command set so that each plugin could set their own flags and switches without having to modify the main repository.
This would mean the CLI app file would call each plugin to build out the rest of the commands and subcommands.
Can this be done with GLI?