I'm new to Thor (and to Ruby) and I'm considering using it within a build script, as it's said it can be a replacement to Rake (thus to Make). However after a short trial, I'm confused about the error status it returns. I quickly went through the wiki but haven't seen any mention about that.
With just the first "Simple Example", test.thor
:
class Test < Thor
desc "example", "an example task"
def example
puts "I'm a thor task!"
end
end
version #:
eruve>thor version
Thor 0.18.1
I tried the following, an erroneous command on purpose:
eruve>ruby --version; thor test:example badarg; echo exit status: $?
ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin10.8.0]
ERROR: thor example was called with arguments ["badarg"]
Usage: "thor test:example".
exit status: 0
So, there was an error but it exits with status 0 nonetheless... meaning I'd rather not use it in a (non-ruby) script, or else the script would continue running even though it should terminate. Subsequent errors might be difficult to analyse.
I must be missing something, hence my questions:
Is there an easy way to get a non-zero status by default in case of an error (config file, etc.)?
If not, what am I supposed to do to get it right?
Thank you.
I know this has been answered already but I think this is a better answer so I thought I'd contribute it anyway.
Thor has a method you can use to change the behavior so errors cause non-zero exit codes. It's not documented very well (IMHO).
class Test < Thor
def self.exit_on_failure?
true
end
desc "example", "an example task"
def example
puts "I'm a thor task!"
end
end
The default for this is inexplicably false
. I don't know why anyone would want it to behave like that personally. Thor issue 244 addresses this as well.
Update:
As of Thor 1.0.0, you will get a deprecation warning if you do not provide your own exit_on_failure?
method. This was to deal with the confusing default behavior.
Based on bundler's solution (many thanks @fontno), and more investigation on my side, here is a hack in order to have it working with a normal shell. WARNING: it's not elegant, prints out the exception stack crap, but I think that works (please don't hesitate to tell me otherwise).
class Thorough < Thor
ENV["THOR_DEBUG"] = "1"
check_unknown_options!
private
def subcommand(*_) super subcommand(*_)
rescue Thor::Error => e
$stderr.puts e.message
exit 1
end
end
class Test < Thor#ough
desc "example", "an example task"
def example
puts "I'm a thor task!"
end
end
Written as above, it has the same behavior as before (I believe). Now, after removing the #
from Thor#ough
, it should exit with status 1 if Thor has raised an Error
, thus allowing some control from e.g. a non-ruby shell.
eruve>thor test:example badarg; echo $?
/Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:482:in `handle_argument_error': ERROR: thor example was called with arguments ["badarg"] (Thor::InvocationError)
Usage: "thor test:example".
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:35:in `rescue in run'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:21:in `run'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/runner.rb:36:in `method_missing'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:29:in `run'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:128:in `run'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/bin/thor:6:in `<top (required)>'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `load'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `<main>'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `eval'
from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `<main>'
1
eruve>thor test:example; echo $?
I'm a thor task!
0
eruve>thor test:example badarg 2>/dev/null; echo $?
1
Cheers. PS: I wonder, are there many gotchas like this in Thor? If it's an expected behavior, its purpose/philosophy is incompatible with the needs of my project... hacks are not a reliable solution.
Good question. I also noticed this when looking a thor
to use for a project. As far as I can tell this is expected behavior. This pull request for bundler
has an interesting solution that may be appropriate for you.
They enabled the thor debug flag so they could catch the error and set the appropriate exit status
# bin/bundle
Bundler.with_friendly_errors {
# Set debug flag so we can rescue Thor::error's
# and set the correct exit code.
ENV["THOR_DEBUG"] = "1"
Bundler::CLI.start
}
# friendly_errors
rescue Thor::UndefinedCommandError => e
Bundler.ui.error e.message
exit 15
rescue Thor::Error => e
Bundler.ui.error e.message
exit 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With