denisdefreyne / cri

A tool for building commandline applications
MIT License
120 stars 19 forks source link

Splitting commands up? #73

Closed rchady closed 6 years ago

rchady commented 6 years ago

Trying to split my command up in to separate files with the sub-commands each in their own file. However, this seems to not work as you can't pass in root_cmd when you require (so you could do the add_command in each file) and if you set a variable in the sub-command file it won't exist in the root_cmd file...

So, is it possible to create classes instead, or am I missing something else obvious?

denisdefreyne commented 6 years ago

Hi,

The approach that I use in Nanoc is the following.

To define a command, read it and call .define with the code and filename (the filename is useful because it ensures any back traces are sensible):

code = File.read(filename, encoding: 'UTF-8')
cmd = Cri::Command.define(code, filename)

I realised that .define used this way is not documented — will fix.

You can infer the name of the command from the filename, if you wish:

command_name = File.basename(filename, '.rb')
cmd.modify { name command_name }

You can stick that code in a helper function e.g. load_command, and then use it as follows to load subcommands:

root_cmd = load_command('commands/nanoc.rb')
root_cmd.add_cmd(load_command('commands/compile.rb'))
root_cmd.add_cmd(load_command('commands/view.rb'))
root_cmd.add_cmd(load_command('commands/deploy.rb'))

You can additionally use something like Dir[…] to remove the repetition, if you want.

Does this solve your concern?

denisdefreyne commented 6 years ago

The upcoming release of Cri will add .load_file, so you’ll be able to do

root_cmd = Cri::Command.load_file('commands/nanoc.rb')
root_cmd.add_command(Cri::Command.load_file('commands/comile.rb'))
root_cmd.add_command(Cri::Command.load_file('commands/view.rb'))
root_cmd.add_command(Cri::Command.load_file('commands/check.rb'))

It is a convenient wrapper around Cri::Command.define with a string containing code.

denisdefreyne commented 6 years ago

… and an infer_name option as well, e.g.

cmd = Cri::Command.load_file('commands/check.rb', infer_name: true)

cmd.name will be check.

denisdefreyne commented 6 years ago

This is now released in v2.14.0.

rchady commented 6 years ago

Perfect, thanks!