How can I use a flag as a command with Thor - ruby

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

Related

Ruby - run Thor command without argument

I have to use this command to execute the script:
$ruby file.rb keyword --format oneline --no-country-code --api-key=API
Where, the format, no-country-code, api-key are thor options. The keyword is the argument in my method:
class TwitterTrendReader < Thor
method_option :'api-key', :required => true
method_option :format
method_option :'no-country-code', :type => :boolean
def execute (keyword)
#read file then display the results matching `keyword`
end
default_task :execute
end
The problem is the keyword is optional, if I run the command without the keyword, the script should print all entries in the file, otherwise, it only display the entries matching the keyword.
so I have this code:
if ARGV.empty?
TwitterTrendReader.start ''
else
TwitterTrendReader.start ARGV
end
It only works when I specify a keyword but without keyword, I get this:
$ruby s3493188_p3.rb --api-key="abC9hsk9"
ERROR: "s3493188_p3.rb execute" was called with no arguments
Usage: "s3493188_p3.rb [keyword] --format oneline --no-country-code --api-key=API-KEY"
So please tell me what is the proper way that I could make the argument optional. Thank you!
Your current implementation of def execute (keyword) is of arity 1 (that said, it declares one mandatory parameter.) Make the parameter optional, if you want to have an ability to omit it.
Change
def execute (keyword)
#read file then display the results matching `keyword`
end
to:
def execute (keyword = nil)
if keyword.nil?
# NO KEYWORD PASSED
else
# NORMAL PROCESSING
end
end

Thor : How can I get my Thor task to display help when I have defined an argument?

I'm converting a small project to use Thor, and being rather absent-minded I want to document the available tasks using built-in help.
But if I define a task with an argument the task-level help reverts to help for the class - which means no task description or details of the expected parameters are displayed.
I want to be be able to have a method I can call with an argument and not a parameter so it can be used like this
$ thor broke:foo hello
in a.thor broke:foo arg1=hello
I've boiled the problem down to the following thorfile which works as I want except the broken help output. I've stripped out any other parameters as they make no difference to the problem.
The first task ok:foo will display help normally, the second task broke:foo is rather less helpful :
class Ok < Thor
desc "foo", "ok test2"
def foo
puts "in a.thor ok:foo\n"
end
end
class Broke < Thor
argument :arg1, :type=>:string, :desc => "arg1"
desc "foo", "broke test1"
def foo
puts "in a.thor broke:foo arg1=#{self.arg1}\n"
end
end
Requesting help for the ok:foo task method gives :
$ thor help ok:foo
Usage:
thor ok:foo
ok test
Requesting help for the broke:foo task is rather less helpful :
$ thor help broke:foo
Tasks:
thor broke:foo ARG1 # broke test1
thor broke:help ARG1 [TASK] # Describe available tasks or one specific task
How can I define an argument and get the proper task help displayed ?
You have small mistake in your thorfile.
You have argument instead of method_option.
Here correct version:
class Ok < Thor
desc "foo", "ok test2"
def foo
puts "in a.thor ok:foo\n"
end
end
class Broke < Thor
method_option :arg1, :type=>:string, :desc => "arg1"
desc "foo", "broke test1"
def foo
puts "in a.thor broke:foo arg1=#{self.arg1}\n"
end
end
Wiki about method_option on github.com

Namespacing thor commands in a standalone ruby executable

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'

Writing a DSL like Thor gem in Ruby?

I'm trying to figure out how the Thor gem creates a DSL like this (first example from their README)
class App < Thor # [1]
map "-L" => :list # [2]
desc "install APP_NAME", "install one of the available apps" # [3]
method_options :force => :boolean, :alias => :string # [4]
def install(name)
user_alias = options[:alias]
if options.force?
# do something
end
# other code
end
desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
def list(search="")
# list everything
end
end
Specifically, how does it know which method to map the desc and method_options call to?
desc is pretty easy to implement, the trick is to use Module.method_added:
class DescMethods
def self.desc(m)
#last_message = m
end
def self.method_added(m)
puts "#{m} described as #{#last_message}"
end
end
any class that inherits from DescMethods will have a desc method like Thor. For each method a message will be printed with the method name and description. For example:
class Test < DescMethods
desc 'Hello world'
def test
end
end
when this class is defined the string "test described as Hello world" will be printed.

Passing arguments to an Rspec SpecTask

Rake allows for the following syntax:
task :my_task, :arg1, :arg2 do |t, args|
puts "Args were: #{args}"
end
I'd like to be able to do the same, but with RSpecs SpecTask.
The following unfortunately fails:
desc "Run example with argument"
SpecTask.new('my_task'), :datafile do |t, args|
t.spec_files = FileList['*_spec.rb -datafile=#{args}']
t.spec_opts = ["-c -f specdoc"]
end
Is it possible to achieve this with a SpecTask, or is there an alternative approach?
if rspec doesn't support the args variable, you could pass it in as a command line parameter and/or a variable from another location.
rake datafile=somevalue
#datafile = ENV["datafile"]
desc "Run example with argument"
SpecTask.new :my_task do |t|
t.spec_files = FileList["*._spec.rb -datafile=#{#datafile}"]
#... etc
end

Resources