Closed noraj closed 1 month ago
My temporary workaround:
# dns duzdu
# since 0.0.2
dns.desc 'DNS unsecure zone dynamic update (DUZDU)'
dns.command :duzdu do |duzdu|
# stuff common to all duzdu's sub-commands, initializing a DUZDU instance
def self.duzdu_init(options)
parent_options = options.dig(GLI::Command::PARENT, GLI::Command::PARENT)
dns_opts = parent_options[:nameserver].nil? ? nil : { nameserver: [parent_options[:nameserver]] }
ADAssault::DNS::DUZDU.new(parent_options[:domain], dns_opts)
end
# dns duzdu add
duzdu.desc 'TODO'
duzdu.command :add do |add|
add.action do |_global_options, options, _args|
duz = duzdu_init(options)
puts duz.addv4
end
end
# dns duzdu delete
duzdu.desc 'TODO'
duzdu.command :delete do |delete|
delete.action do |_global_options, options, _args|
duz = duzdu_init(options)
puts duz.deletev4
end
end
# dns duzdu update
duzdu.desc 'TODO'
duzdu.command :update do |update|
update.action do |_global_options, _options, _args|
duz = duzdu_init(options)
puts duz.updatev4
end
end
# dns duzdu check
duzdu.desc 'TODO'
duzdu.command :check do |check|
check.action do |_global_options, options, _args|
duz = duzdu_init(options)
puts duz.checkv4
end
end
# dns duzdu get
duzdu.desc 'TODO'
duzdu.command :get do |get|
get.action do |_global_options, _options, _args|
duz = duzdu_init(options)
puts duz.getv4
end
end
end
Eh, that looks like a typo? I'm not sure that works.
the GLI DSL is best used to locate code in some other class to execute, i.e. you don't want to put your core logic directly inside the action
block. When you have a highly nested set of commands and subcommands, you might want to have a bunch of regular Ruby classes that manage your core logic and use conventional means of re-use like inheritance of mixins to manage duplication.
e.g.
# In e.g. lib/actions/duzdu
class DUZDUBaseAction
def initialize(options)
parent_options = options.dig(GLI::Command::PARENT, GLI::Command::PARENT)
dns_opts = parent_options[:nameserver].nil? ? nil : { nameserver: [parent_options[:nameserver]] }
@duzdu = ADAssault::DNS::DUZDU.new(parent_options[:domain], dns_opts)
end
def perform!
raise "subclass must implement"
end
end
class Add < DUZDUBaseAction
def perform!
@duzdu.addv4
end
end
class Delete < DUZDUBaseAction
def perform!
@duzdu.deletev4
end
end
# then, in your GLI "app"
dns.command :duzdu do |duzdu|
duzdu.command :add do |add|
add.action do |_global_options, options, _args|
Add.new(options).perform!
end
end
# dns duzdu delete
duzdu.desc 'TODO'
duzdu.command :delete do |delete|
delete.action do |_global_options, options, _args|
Delete.new(options).perform!
end
end
end
If you want to get really fancy, you can use Ruby's protocol for turning objects into procs:
# In e.g. lib/actions/duzdu
class DUZDUBaseAction
def initialize(options)
parent_options = options.dig(GLI::Command::PARENT, GLI::Command::PARENT)
dns_opts = parent_options[:nameserver].nil? ? nil : { nameserver: [parent_options[:nameserver]] }
@duzdu = ADAssault::DNS::DUZDU.new(parent_options[:domain], dns_opts)
end
def perform!
raise "subclass must implement"
end
def self.to_proc
->(_global, options, args) {
self.new(options)
}
end
end
class Add < DUZDUBaseAction
def perform!
@duzdu.addv4
end
end
class Delete < DUZDUBaseAction
def perform!
@duzdu.deletev4
end
end
# then, in your GLI "app"
dns.command :duzdu do |duzdu|
duzdu.command :add do |add|
add.action(&Add)
end
# dns duzdu delete
duzdu.desc 'TODO'
duzdu.command :delete do |delete|
delete.action(&Delete)
end
end
But in any case, you ideally want your action blocks deferring to other code if possible, especially when your app has a lot of commands and sub-commands
I have a question ❓ about this example:
https://github.com/davetron5000/gli/blob/19eae87ca23ab03237b3533833aa51170ea30e84/lib/gli/command.rb#L176
How is it possible to have a
|global,options,args|
for command? It sounds to me, it is possible only for action.The reason I was led to ask myself this question is because I want some code to be executed for several sub-commands. Because the code would have been always the same I felt bad about copy/pasting it in each sub-command.
So I thought about copying it on the command action block like that:
The issue is it will be executed only when there is no sub-command behind, e.g.
ada dns duzdu
. It is not executed when a sub-command exists, e.g.ada dns duzdu add
.For it to be called even when there is a sub-command I could have put it outside the action block (I don't even know if it would work), but then I don't have access to
options
.In fact, I would have needed something like pre but
pre
works only on app top-level, not a command level, e.g.duzdu.pre
. Somthing like thesetup
method of minitest.That's why when I saw
command
with a|global,options,args|
, I was thinking that could be useful for what I try to achieve.I think the better temporary workaround I have right now is putting the 3 lines of code in a function and calling that function in each sub-command action block.