Ruby - run Thor command without argument - ruby

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

Related

How to add --help, -h flag to Thor command?

I created a Thor class inside a Ruby executable, and it correctly shows the help when using ./foo help bar.
To make it more intuitive (for the sanity of my users), I'd also like to support ./foo bar --help and ./foo bar -h. When I do that, I get:
ERROR: "foo bar" was called with arguments ["--help"]
Usage: "foo bar"
I could manually do method_option :help, ... and handle it inside the bar method, but I hope there would be an easier way to do that (redirecting that command to ./foo help bar).
Does anyone know a simple and easy way to do this?
Assuming Foo is your class that inherits from Thor, you can call the following somewhere before Foo.start:
help_commands = Thor::HELP_MAPPINGS + ["help"]
# => ["-h", "-?", "--help", "-D"]
if help_commands.any? { |cmd| ARGV.include? cmd }
help_commands.each do |cmd|
if match = ARGV.delete(cmd)
ARGV.unshift match
end
end
end
Rather than going into Thor and patching some method to have different ARGV-parsing behavior, this kind of cheats by moving any help commands to the front of the list.
You can achive this with class_option. If you set a class option this option is availiable for every method in your cli and you can just check if it is set and then call the help method.
Something like this:
class CLI < Thor
class_option :help, type: :boolean
desc "foo PARAM", "foo"
def foo(param)
handle_help_option(:foo)
# your logic
end
def handle_help_option(method_name)
help(method_name) if options[:help]
end
end
Building on what #max-pleaner listed. This will support subcommands also:
help_commands = Thor::HELP_MAPPINGS + ["help"]
(help_commands & ARGV).each do |cmd|
match = ARGV.delete(cmd)
ARGV.size > 1 ? ARGV.insert(-2, match) : ARGV.unshift(match)
end
To complement the answer from max pleaner, the following handles subcommands, because subcommands help is broken if the trick is applied to them.
Also, I choose to overload Thor start command.
def self.start(*args)
if (Thor::HELP_MAPPINGS & ARGV).any? and subcommands.grep(/^#{ARGV[0]}/).empty?
Thor::HELP_MAPPINGS.each do |cmd|
if match = ARGV.delete(cmd)
ARGV.unshift match
end
end
end
super
end

Thor - command line option not recognized in method

I have to use this command to run my ruby program:
$ruby filename.rb NAME --from="People" --yell
And I have the script like this:
require 'thor'
class CLI < Thor
desc "hello NAME", "say hello to NAME"
method_option :from, :required => true
method_option :yell, :type => :boolean
def self.hello(name)
output = []
output << "from: #{options[:from]}" if options[:from]
output << "Hello #{name}"
output = output.join("\n")
puts options[:yell] ? output.upcase : output
end
end
CLI.hello(ARGV)
When I run the code, I get the following output:
c:\RubyWorkplace\Assignment1>ruby testing.rb Jay --from="Ray"
FROM: #<THOR::OPTION:0X000000031D7998>
HELLO ["JAY", "--FROM=RAY"]
c:\RubyWorkplace\Assignment1>ruby testing.rb Jay --from="Ray" --yell
FROM: #<THOR::OPTION:0X0000000321E528>
HELLO ["JAY", "--FROM=RAY", "--YELL"]
It looks like :yell always works no matter I specify it or not, and options are all read as name input in the hello method.
I found and tried many ways from online tutorials but the problem wasn't solved. Please tell me what has been gone wrong. Thank you!
The problem is caused by I am calling CLI.hello ARGV in the script. when the program runs, it will call hello method and recognize all command line inputs as hello's parameter, which is an array.
One of the ways to fix this problem is making hello public by removing self, the call the script by start method.
require 'thor'
class CLI < Thor
desc "hello NAME", "say hello to NAME"
method_option :from, :required => true
method_option :yell, :type => :boolean
def hello(name)
#do something
end
end
CLI.start ARGV

My very simple custom Puppet type and provider does not work

I am reading about how to create custom types and providers in Puppet.
But I am getting the error:
Error: Could not autoload puppet/provider/createfile/ruby: undefined method `[]' for nil:NilClass
when running the below code:
mymodule/lib/puppet/type/filecreate.rb
require 'fileutils'
Puppet::Type.newtype(:filecreate) do
ensurable do
defaultvalues
defaultto :present
end
#doc = "Create a file."
newproperty(:name, :namevar => true) do
desc "The name of the file"
end
newproperty(:path) do
desc "The path of the file"
end
end
mymodule/lib/puppet/provider/filecreate/ruby.rb
require 'fileutils'
Puppet::Type.type(:filecreate).provide(:ruby) do
desc "create file.."
puts resource[:name] # this line does not seem to work, why?
puts resource[:path] # this line does not seem to work, why?
def create
puts "create file..."
puts resource[:name]
end
def destroy
puts ("destroy file...")
FileUtils.rm resource[:path]+resource[:name]
end
# Exit method never seems to be called
def exists?
puts "is method beeing called???"
File.exists?(resource[:path])
end
end
I guess the way of fetching the parameter values, puts resource[:name] not is correct. So how can I fetch the filename file.txt declared as the namevar for my custom type filecreate (see below)?
Also, method exists does not seem to be called. Why?
And my init.pp contains this simple code:
class myclass {
filecreate{'file.txt':
ensure => present,
path => '/home/myuser/',
}
}
Your puts calls do not work because you try and access an instance attribute (resource) on the class level. It makes no semantic sense to access the values in this context. Remove those calls.
Generally, it is better to use Puppet.debug instead of puts to collect this kind of information.
To find out where such errors come from, call puppet with the --trace option.

How can I use a flag as a command with Thor

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

Commands in ruby terminal application

I have just written my first terminal application in ruby. I use OptionParser to parse the options and their arguments. However I want to create commands. For example:
git add .
In the above line, add is the command which cannot occur anywhere else than immediately after the application. How do I create these.
I will appreciate if anyone could point me in the right direction. However, please do not reference any gems such as Commander. I already know about these. I want to understand how it is done.
The OptionParser's parse! takes an array of arguments. By default, it will take ARGV, but you can override this behaviour like so:
Basic Approach
def build_option_parser(command)
# depending on `command`, build your parser
OptionParser.new do |opt|
# ...
end
end
args = ARGV
command = args.shift # pick and remove the first option, do some validation...
#options = build_option_parser(command).parse!(args) # parse the rest of it
Advanced Approach
Instead of a build_option_parser method with a huge case-statement, consider an OO approach:
class AddCommand
attr_reader :options
def initialize(args)
#options = {}
#parser = OptionParser.new #...
#parser.parse(args)
end
end
class MyOptionParser
def initialize(command, args)
#parser = {
'add' => AddCommand,
'...' => DotsCommand
}[command.to_s].new(args)
end
def options
#parser.options
end
end
Alternatives
For sure, there exist tons of Rubygems (well, 20 in that list), which will take care of your problem. I'd like to mention Thor which powers, e.g. the rails command line tool.
You can retrieve the command with Array#shift prior invoking OptionParser.

Resources