Hi I'm using trollop to parse my command line options in ruby, I have four mutually exclusive options, and one option is always required.
I'm stuck trying to figure out how to ensure only one of the four options is passed. If called with more than one option I want the usage help (educate?) shown.
I see from the trollop source there's something called conflicts
http://www.rubydoc.info/gems/trollop/2.1.2/Trollop/Parser#conflicts-instance_method
that sounds like it does what I want (?) but I can't figure out how to use it correctly.
My current stanza is effectively this
require 'trollop'
opts = Trollop::options do
opt :last, "last"
opt :first, "first"
opt :file, "filename",
:type => String
opt :date, "date to read",
:type => Date
end
Trollop::die :file, "must exist" unless File.exist?(opts[:file]) if opts[:file]
thank you
You can add the conflicts as a line in your do block like this:
require 'trollop'
opts = Trollop::options(ARGV) do
opt :last, "last"
opt :first, "first"
opt :file, "filename", :type => String
opt :date, "date to read", :type => Date
conflicts :last, :first
end
puts "Your Options Are: "
puts opts
Then you get the following output:
ruby test_options.rb --last Last --first First
Error: --last conflicts with --first.
Try --help for help.
Related
For example, when I type ruby file.rb -a "water the plants" in command line
I want this line to be added on a hash. Such as a to-do list.
So it will look something like item1: water the plants
Here's what I did so far:
require 'optparse'
option_parser = OptionParser.new do |opts|
opts.on '-a', '--add',
end
Thanks in advance!
Look a bit more closely at the examples in the docs for OptionParser.
To accept a value for an argument, you have to specify it in the second argument to opts.on, something like this:
require 'optparse'
option_parser = OptionParser.new do |opts|
opts.on '-a', '--add val' do |value|
puts value
end
end.parse!
To make it a required argument, just change that val to capitalized VAL (it can be any word, I'm just using "val" as an example).
Calling it, you can see how it works:
ruby file.rb -a "water the plants"
# => "water the plants"
ruby file.rb -a "water the plants" "do the dishes"
# => "water the plants"
ruby file.rb -a "water the plants" -a "do the dishes"
# => water the plants
# => do the dishes
As you can see, to pass multiple values, you need to include the -a flag multiple times. The block is called for each value individually.
What is the correct way to view the output of the puts statements below? My apologies for such a simple question.... Im a little rusty on ruby. github repo
require 'active_support'
require 'active_support/core_ext'
require 'indicators'
my_data = Indicators::Data.new(Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-25', :end_date => '2012-08-30').output)
puts my_data.to_s #expected to see Open,High,Low,Close for AAPL
temp=my_data.calc(:type => :sma, :params => 3)
puts temp.to_s #expected to see an RSI value for each data point from the data above
Maybe check out the awesome_print gem.
It provides the .ai method which can be called on anything.
An example:
my_obj = { a: "b" }
my_obj_as_string = my_obj.ai
puts my_obj_as_string
# ... this will print
# {
# :a => "b"
# }
# except the result is colored.
You can shorten all this into a single step with ap(my_obj).
There's also a way to return objects as HTML. It's the my_obj.ai(html: true) option.
Just use .inspect method instead of .to_s if you want to see internal properties of objects.
Say I have the following class:
class Buyer < ActiveRecord::Base
attr_accesible :first_name, :last_name
and the following in a CSV file:
First Name,Last Name
John,Doe
Jane,Doe
I want to save the contents of the CSV into the database. I have the following in a Rake file:
namespace :migration do
desc "Migrate CSV data"
task :import, [:model, :file_path] => :environment do |t, args|
require 'csv'
model = args.model.constantize
path = args.file_path
CSV.foreach(path, :headers => true,
:converters => :all,
:header_converters => lambda { |h| h.downcase.gsub(' ', '_') }
) do |row|
model.create!(row.to_hash)
end
end
end
I am getting an undefined method 'downcase' for nil:NilClass. If I exclude the header converters then I get unknown attribute 'First Name'. What's the correct syntax for converting a header from, say, First Name to first_name?
After doing some research here in my desktop, it seems to me the error is for something else.
First I put the data in my "a.txt" file as below :
First Name,Last Name
John,Doe
Jane,Doe
Now I ran the code, which is saved in my so.rb file.
so.rb
require 'csv'
CSV.foreach("C:\\Users\\arup\\a.txt",
:headers => true,
:converters => :all,
:header_converters => lambda { |h| h.downcase.gsub(' ', '_') }
) do |row|
p row
end
Now running the :
C:\Users\arup>ruby -v so.rb
ruby 1.9.3p448 (2013-06-27) [i386-mingw32]
#<CSV::Row "first_name":"John" "last_name":"Doe">
#<CSV::Row "first_name":"Jane" "last_name":"Doe">
So everything is working now. Now let me reproduce the error :
I put the data in my "a.txt" file as below ( just added a , after the last column) :
First Name,Last Name,
John,Doe
Jane,Doe
Now I ran the code, which is saved in my so.rb file, again.
C:\Users\arup>ruby -v so.rb
ruby 1.9.3p448 (2013-06-27) [i386-mingw32]
so.rb:5:in `block in <main>': undefined method `downcase' for nil:NilClass (NoMethodError)
It seems, in your header row, there is blank column value which is causing the error. Thus if you have a control to the source CSV file, check there the same. Or do some change in your code, to handle the error as below :
require 'csv'
CSV.foreach("C:\\Users\\arup\\a.txt",
:headers => true,
:converters => :all,
:header_converters => lambda { |h| h.downcase.gsub(' ', '_') unless h.nil? }
) do |row|
p row
end
A more general answer, but if you have code that you need to process as text, and sometimes you might get a nil in there, then call to_s on the object. This will turn nil into an empty string. eg
h.to_s.downcase.gsub(' ', '_')
This will never blow up, whatever h is, because every class in ruby has the to_s method, and it always returns a string (unless you've overridden it to do something else, which would be unadvisable).
Passing :symbol to :header_converters will automatically convert to strings to snake case as well.
options = {:headers => true,
:header_converters => :symbol}
CSV.foreach(filepath, options) ...
#<CSV::Row first_name:"John" last_name:"Doe">
#<CSV::Row first_name:"Jane" last_name:"Doe">
I have a model with a Time attribute. I want to check that time can not be empty (better choice probably would be to check that input is time but i have no ideas how to deal with that). I tried this validation:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
class Meeting < ActiveRecord::Base
validates :meeting_time,
:presence => { :message => "can't be empty!" }
end
Then i tried to check this in spec and this fails (empty time is ok but it should not be). What do i do wrong?
Rspec:
# id :integer not null, primary key
# school_class_id :integer
# meeting_time :time
require 'spec_helper'
describe Meeting do
before(:each) do
#class = FactoryGirl.create( :school_class )
#attr_meeting = {
:meeting_theme => 'Text is here',
:meeting_date => "#{Date.today}",
:meeting_time => "#{Time.now}",
:meeting_room => '1234'
}
end
describe "Validations" do
describe "Rejection" do
it "should reject blank time" do
wrong_attr = #attr_meeting.merge( :meeting_time => " " )
#class.meetings.build( wrong_attr ).should_not be_valid
end
end
end
end
Error:
Meeting Validations Rejection should reject blank time
Failure/Error: #class.meetings.build( wrong_attr ).should_not be_valid
expected valid? to return false, got true
In your example, you assign " " to the meeting_time, not nil or "". But Time object somehow can be successfuly generated from non-empty, even blank string. Setting meeting_time to "" or nil should solve yout problem.
Maybe I don't fully understand something, but I think it's not very logical and predictable behaviour. Need to take a look into the sources.
I find my self doing this often:
optparse = OptionParser.new do |opts|
options[:directory] = "/tmp/"
opts.on('-d','--dir DIR', String, 'Directory to put the output in.') do |x|
raise "No such directory" unless File.directory?(x)
options[:directory] = x
end
end
It would be nicer if I could specify Dir or Pathname instead of String. Is there a pattern or my Ruby-esque way of doing this?
You can configure OptionParser to accept (for instance) a Pathname
require 'optparse'
require 'pathname'
OptionParser.accept(Pathname) do |pn|
begin
Pathname.new(pn) if pn
# code to verify existence
rescue ArgumentError
raise OptionParser::InvalidArgument, s
end
end
Then you can change your code to
opts.on('-d','--dir DIR',Pathname, 'Directory to put the output in.') do |x|
If you are looking for a Ruby-esque way of doing that, I would recommend to give Trollop a try.
Since version 1.1o you can use the :io type which accepts a filename, URI, or the strings stdin or -.
require 'trollop'
opts = Trollop::options do
opt :source, "Source file (or URI) to print",
:type => :io,
:required => true
end
opts[:source].each { |l| puts "> #{l.chomp}" }
If you need the pathname, then it is not what you are looking for. But if you are looking to read the file, then it is a powerful way to abstract it.