I'm building a CLI using Ruby and Thor and I would like to print on the screen the command usage if no option is passed.
Something on the line of the pseudo code bellow:
Class Test < Thor
desc 'test', 'test'
options :run_command
def run_command
if options.empty?
# Print Usage
end
end
end
Im currently using the following hack (and I'm not proud of it! =P):
Class Test < Thor
desc 'test', 'test'
options :run_command
def run_command
if options.empty?
puts `my_test_command help run_command`
end
end
end
What would be the proper way to do this?
You can use command_help to display the help information for the command:
require 'thor'
class Test < Thor
desc 'run_command --from=FROM', 'test usage help'
option :from
def run_command
unless options[:from]
Test.command_help(Thor::Base.shell.new, 'run_command')
return
end
puts "Called command from #{options[:from]}"
end
end
Test.start
and then running with no options:
$ ruby example.rb run_command
Usage:
example.rb run_command --from=FROM
Options:
[--from=FROM]
test usage help
and running with the option:
$ ruby example.rb run_command --from=somewhere
Called command from somewhere
Related
In my main thor file, I call this code
script.rb
# this works
current_dir = Dir.getwd
# this changes directory into the tasks
Dir.chdir(#{pwd}/tasks) {
IO.popen("thor #{ARGV * ' '}") do |io|
while (line = io.gets) do
puts line
end
io.close
end
}
tasks/example.rb
require 'thor'
class Git < Thor
include Thor::Actions
desc 'test', 'test'
def test
puts Dir.getwd # this is showing my tasks folder
end
end
Inside example.rb How can I get access to the Dir.getwd value of the script.rb and not of the example.rb (this is wrong since it is running inside the Dir.chdir).
I tried global variables and such but it doesn't seem to be working.
I have a ruby script (Guardfile) that executes a rake command.
guard :shell do
watch(%r{^manifests\/.+\.pp$}) do |m|
spec = `rake spec`
retval = $?.to_i
case retval
when 0
if spec.length > 0 then
puts spec
n "#{m[0]} Tests Failed!", 'Rake Spec', :pending
else
puts spec
n "#{m[0]} Tests Passed!", 'Rake Spec', :pending
end
end
end
When I run a 'rake spec' from the command line, outputs are colorized.
How could I make it so the output of the ruby script is also colorized?
From command line:
From ruby script:
Update
I was able to sort-of work around the problem by using script
bash command preserve color when piping
spec = `script -q /dev/null rake spec`
This still has the downside of not scrolling the text in real time. While it does preserve the colors, it does not output anything until the very end.
Is there a more native way to do this that will allow for scrolling?
First, rake spec --color won't work, because you're passing --color to rake, and not rspec.
Jay Mitchell's suggestion for color should work - by putting this in your .rspec file:
--color
As for having "live" output, guard-shell has an eager command for this:
https://github.com/guard/guard-shell/blob/master/lib/guard/shell.rb#L37-L51
Unfortunately, guard-shell has 2 important shortcomings:
it doesn't give you access to the exit code
it doesn't properly report failures in Guard (which causes other tasks to run)
So the eager method of Guard::Shell is useless for our needs here.
Instead, the following should work:
# a version of Guard::Shell's 'eager()' which returns the result
class InPty
require 'pty'
def self.run(command)
PTY.spawn(command) do |r, w, pid|
begin
$stdout.puts
r.each {|line| $stdout.print line }
rescue Errno::EIO
end
Process.wait(pid)
end
$?.success?
rescue PTY::ChildExited
end
end
# A hack so that Guard::Shell properly throws :task_has_failed
class ProperGuardPluginFailure
def to_s
throw :task_has_failed
end
end
guard :shell, any_return: true do
watch(%r{^manifests\/.+\.pp$}) do |m|
ok = InPty.run('rake spec')
status, type = ok ? ['Passed', :success] : ['Failed', :failed]
n "#{m[0]} Tests #{status}!", 'Rake Spec', type
ok ? nil : ProperGuardPluginFailure.new
end
end
The above looks ideal for a new guard plugin - good idea?
I am unfamiliar with Guardfiles. Can you use gems? The colorize gem is great.
https://github.com/fazibear/colorize
Install it:
$ sudo gem install colorize
Use it:
require 'colorize'
puts "Tests failed!".red
puts "Tests passed!".green
Is it possible to get the location of the file which requires another file in Ruby?
I have a project where I spawn some processes and I would love to be able, in the code, to determine which file is the parent of the required file. This is nice when debugging.
Example:
#initial.rb:
require "./my_file.rb"
fork do
require "./my_file2.rb"
end
-
#my_file.rb:
puts "Required from file: #{?????}"
-
#my_file2.rb:
require "./my_file.rb"
I would expect to get something like:
#=> Required from file: /path/to/initial.rb
#=> Required from file: /path/to/my_file2.rb
Based on Jacobs answer I ended with this redefinition of require_relative and require:
alias :old_require_relative :require_relative
def require_relative(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require_relative arg
end
alias :old_require :require
def require(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require arg
end
In a test test scenario with the following load sequence:
test.rb
+- test1.rb
+- test1_a.rb
+ test2.rb
The following calls
require './test1'
require './test2'
or
require_relative 'test1'
require_relative 'test2'
result in:
test.rb requires ./test1
C:/Temp/test1.rb requires test1_a
test.rb requires ./test2
You could also include the line of the requirement in the output.
You should never need to do this, but you can examine the call stack from Kernel#caller. You'll have to filter out require methods (especially if you use any libraries that override require).
Given a Ruby program using Thor, how can I implement a method that gets called when an argument that looks like a flag is called.
For example, if I run this on the command line:
mycmd --version
I would like to execute the code:
desc 'version', 'Print version number'
def version
puts "mycmd version #{Mycmd::VERSION}"
end
You can make a "top level" default task, which examines its arguments and outputs the correct thing:
class MyThing < Thor
desc "meta", "Information about the task itself"
argument :name
def meta
if name == "--version"
puts "v 1.1.1"
elsif name == "--author"
puts "meagar"
end
end
default_task :meta
end
When calling thor commands on the command line, the methods are namespaced by their module/class structure, e.g.
class App < Thor
desc 'hello', 'prints hello'
def hello
puts 'hello'
end
end
would be run with the command
thor app:hello
However, if you make that self executable by putting
App.start
at the bottom you can run the command like:
app hello
Is there any way to namespace those commands? So that you could call, for example
app say:hello
app say:goodbye
Another way of doing this is to use register:
class CLI < Thor
register(SubTask, 'sub', 'sub <command>', 'Description.')
end
class SubTask < Thor
desc "bar", "..."
def bar()
# ...
end
end
CLI.start
Now - assuming your executable is called foo - you can call:
$ foo sub bar
In the current thor version (0.15.0.rc2) there is a bug though, which causes the help texts to skip the namespace of sub commands:
$ foo sub
Tasks:
foo help [COMMAND] # Describe subcommands or one specific subcommand
foo bar #
You can fix that by overriding self.banner and explicitly setting the namespace.
class SubTask < Thor
namespace :sub
def bar ...
def self.banner(task, namespace = true, subcommand = false)
"#{basename} #{task.formatted_usage(self, true, subcommand)}"
end
end
The second parameter of formatted_usage is the only difference to the original implemtation of banner. You can also do this once and have other sub command thor classes inherit from SubTask. Now you get:
$ foo sub
Tasks:
foo sub help [COMMAND] # Describe subcommands or one specific subcommand
foo sub bar #
Hope that helps.
This is one way with App as the default namespace (quite hacky though):
#!/usr/bin/env ruby
require "rubygems"
require "thor"
class Say < Thor
# ./app say:hello
desc 'hello', 'prints hello'
def hello
puts 'hello'
end
end
class App < Thor
# ./app nothing
desc 'nothing', 'does nothing'
def nothing
puts 'doing nothing'
end
end
begin
parts = ARGV[0].split(':')
namespace = Kernel.const_get(parts[0].capitalize)
parts.shift
ARGV[0] = parts.join
namespace.start
rescue
App.start
end
Or, also not ideal:
define_method 'say:hello'