devnote-dev / cling Goto Github PK
View Code? Open in Web Editor NEWA modular, non-macro-based command line interface library
License: Mozilla Public License 2.0
A modular, non-macro-based command line interface library
License: Mozilla Public License 2.0
The current flow of a command's execution runs through the pre_run
hook -> run
main method -> post_run
hook. However, if pre_run
or run
are interrupted or stopped, post_run
is never called.
Lines 61 to 66 in 0dcfb17
This means that post_run
is effectively limited to pre_run
and run
working successfully. So in cases where cleanup needs to be performed before an application exits, your only option is to handle it in pre_run
, run
or the on_error
hook method. That leaves 2 possible options for post_run
: to call this method after pre_run
/run
but not directly require them to exit successfully, or to remove it entirely.
Despite my large advocacy for Cling and its structure, I have never used post_run
for any existing CLI applications. It was mostly inspired from Cobra's PostRun
methods which operates on a similar premise: postRun
is deferred and will always run regardless of the state of the PreRun
and Run
methods. Assuming none of the methods fail, PostRunE
/PostRun
is called just before execution ends.
Cling does not differentiate between erroneous and non-erroneous execution methods like Cobra does with "E" suffixed methods because exceptions can be raised at any point of the program making everything erroneous. That means post_run
could effectively be changed to always be called as the final part of execution, with any exceptions from it or previous hook methods still being funneled to the on_error
hook method.
While this sounds like a clear-cut solution, there are some undefined semantics as to what precedence post_run
has in regards to exception handling (or rather, ignoring) which up until this point, has been preceded by the on_error
hook method. Would this enforce a stricter exception handling structure? What affects would this impose on existing applications? And most importantly, is it worth it?
Example:
Options:
-i, --input-path Specify the path to the input file (required)
-o, --output-path Specify the path to the output directory (required) (default: out)
I think having "(required)" makes sense in the first case, but not the second one.
Alternatively, it would make sense if passing -o
without a value would cause an error with the same message as when -i
is missing:
Unhandled exception: Missing required argument for option 'i' (Cling::ExecutionError)
For pre_run
hook methods, there is the option to stop command execution to the main run
method if the value returned is false
. However, there are no checks in place to determine the actual exit code for this, the Executor
simply returns which results in a 0 exit code:
Lines 52 to 54 in 279db9a
This is obviously not ideal and even misleading for people designing applications that rely on the exit code for debugging or informational purposes. There are already ways to get around this, for example, raising a specific exception that gets funneled to the on_error
hook method which then terminates the program with the appropriate exit code. But I think there should be a better way of doing this, even if it's just a shorthand method built into the Command
class.
Given the following code example, the on_invalid_option
hook method is executed from Foo
not Bar
:
require "cling"
class Bar < Cling::Command
def setup : Nil
@name = "bar"
add_option "baz", type: :single
end
def run(arguments : Cling::Arguments, options : Cling::Options) : Nil
end
def on_invalid_option(message : String)
puts "Uh oh"
end
end
class Foo < Cling::Command
def setup : Nil
@name = "foo"
add_command Bar.new
end
def run(arguments : Cling::Arguments, options : Cling::Options) : Nil
end
end
Foo.new.execute ARGV
$ crystal test.cr bar --baz
Unhandled exception: Missing required argument for option 'baz' (Cling::CommandError)
from lib/cling/src/cling/command.cr:223:7 in 'on_invalid_option'
from lib/cling/src/cling/executor.cr:48:7 in 'handle'
from lib/cling/src/cling/command.cr:182:7 in 'execute'
from test.cr:29:1 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:115:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:101:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:127:3 in 'main'
from /lib/x86_64-linux-gnu/libc.so.6 in '??'
from /lib/x86_64-linux-gnu/libc.so.6 in '__libc_start_main'
from /home/devonte/.cache/crystal/crystal-run-test.tmp in '_start'
from ???
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.