RuntimeError when running script? - ruby

So I have found this RUBY script, which looks for all PNG images in sub folders and folders and converts PNG images using TinyPNG API but for some reason I get runtime error
C:/Users/Vygantas/Desktop/tinypng.rb:14:in `':
Usage: ./tinypng.rb C:/Users/Vygantas/Desktop/product C:/Users/Vygantas/Desktop/product(RuntimeError)
#!/usr/bin/ruby -w
#
# tinypng.rb — Placed into the public domain by Daniel Reese.
#
require 'rubygems'
require 'json'
# Set API key.
apikey = "xxxxxxxxxxxxxxxx"
# Verify arguments.
ARGV.length == 2 or fail("Usage: ./tinypng.rb C:\Users\Vygantas\Desktop\product C:\Users\Vygantas\Desktop\product*emphasized text*")
src = ARGV[0]
dst = ARGV[1]
File.exist?(src) or fail("Input folder does not exist: " + src)
File.exist?(dst) or fail("Output folder does not exist: " + dst)
# Optimize each image in the source folder.
Dir.chdir(src)
Dir.glob('*.png') do |png_file|
puts "\nOptimizing #{png_file}"
# Optimize and deflate both images.
cmd = "curl -u api:#{apikey} --data-binary ##{png_file} 'https://api.tinypng.com/shrink'"
puts cmd
r = JSON.parse `#{cmd}`
if r['error']
puts "TinyPNG Error: #{r['message']} (#{r['error']})"
exit(1)
end
url = r['output']['url']
cmd = "curl '#{url}' -o #{dst}/#{png_file}"
puts cmd
`#{cmd}`
end
Dir.chdir("..")
puts 'Done'

As you might see in the code, line 14 (as printed on script execution):
ARGV.length == 2 or fail("Usage: ./tinypng.rb
C:\...\product C:\...\product*emphasized text*")
That said, a script requires two parameters to run. Let me guess: you did not pass two parameters. Those are btw source and destination folders.

Related

How do I run a Ruby script in the Terminal?

I'm attempting to run a Ruby script (linked below) that was shared by DHH to convert a number of .dcp Leica Q camera profiles to Leica M10 camera profiles.
I'm just not sure how to run it. I understand it needs to be run in Terminal but that's about it.
I have all Leica Q camera profiles in a single folder on the desktop... Now what?
I've downloaded the DCP tool that's mentioned in the comments of the script.
Here's a link to the GitHub repo: https://gist.github.com/dhh/d3c8cf9309b662047257b7e583c3f595#file-dcp-converter-rb-L8
I know this might be pretty basic but any help would be greatly appreciated!
Here's the actual script:
#!/usr/bin/env ruby
# Requires that you have ./bin/dcpTool from https://sourceforge.net/projects/dcptool/
require 'rubygems'
require 'bundler/setup'
require 'nokogiri'
input_camera_model = ARGV[0] || "LEICA Q (Typ 116)"
output_camera_model = ARGV[1] || "LEICA M10"
input_dir = ARGV[2] || "./input"
output_dir = ARGV[3] || "./output"
def convert_profile_name(profile_name, input_camera_model, output_camera_model)
File.basename(profile_name.gsub(/#{input_camera_model.gsub(/\(/, "\\(").gsub(/\)/, "\\)")}/, output_camera_model), ".dcp")
end
def replace_camera_model(xml_profile_filename, output_camera_model)
profile_doc = Nokogiri::XML(File.read(xml_profile_filename))
profile_doc.xpath('//UniqueCameraModelRestriction').first.content = output_camera_model
File.open(xml_profile_filename, "w+") { |file| file.write(profile_doc.to_xml) }
end
Dir.entries(input_dir).reject { |file| file =~ /^(\.|\.\.)$/ }.each do |existing_profile|
converted_profile = convert_profile_name(existing_profile, input_camera_model, output_camera_model)
existing_dcp_filename = File.join(input_dir, existing_profile)
xml_filename = "#{File.join(output_dir, converted_profile)}.xml"
decompile_command = "./bin/dcpTool -d '#{existing_dcp_filename}' '#{xml_filename}'"
puts "Decompiling #{existing_dcp_filename} into XML"
`#{decompile_command}`
puts "Replacing camera model: #{input_camera_model} -> #{output_camera_model}"
replace_camera_model(xml_filename, output_camera_model)
converted_dcp_filename = "#{File.join(output_dir, converted_profile)}.dcp"
recompile_command = "./bin/dcpTool -c '#{xml_filename}' '#{converted_dcp_filename}'"
puts "Recompiling XML into #{converted_dcp_filename}"
`#{recompile_command}`
File.delete(xml_filename)
puts
end```
Easiest way:
Create an "input" and an "output" directory in the same location as this script.
Place all of your files in "input"
In the terminal navigate to this location
type ruby dcp-converter.rb.
Note: You may have to run gem install bundler nokogiri first.
If you have a different model than the one shown you may have to pass additional arguments e.g. ruby dcp-converter.rb "LEICA Q (Typ 202)"
The argument order would be ruby dcp-converter.rb [input_model] [output_model] [input_directory] [output_directory]
The defaults are
[input_model] = "LEICA Q (Typ 116)"
[output_model]="LEICA M10"
[input_directory]="./input"
[output_directory]="./output"

Sending mail using mailx shell command in Ruby

I need some help with my ruby script. I have code like this:
#!/usr/bin/ruby
require 'rubygems'
require 'json'
require 'date'
system 'curl https://xxx/yyy/zzz --cacert xxx.pem --cert xxx.pem --key xxx.pem >> hosts.txt'
document = JSON.load File.new("hosts.txt")
file = JSON.load File.new("admins.txt")
new_file = File.open("newfile.txt", "w")
personal_data = file['admins'].group_by { |e| e.delete('name') }
dupa = []
document['results'].map do |h|
dupa << h.merge(personal_data[h['name']].first) if personal_data[h['name']]
end
#puts dupa
dupa.each do |a|
if a["global_status_label"] != "OK"
last_report = a["last_report"].to_s
last_report = last_report[0..9]
date_last_report = Date.parse last_report
current_Date = Date.today
difference_dates = (current_Date - date_last_report).to_i
if difference_dates > 5
new_file.puts "#{a['name']}\t #{difference_dates}"
end
end
end
new_file.close
Everything works fine, but I need to send mail with some informations using only shell command mailx/mail. I know how to execute shell commands in Ruby and if I wanted to send only a mail it would not be a problem, but doing thay way I have to create new file with data new_file.puts "#{a['name']}\t #{difference_dates}" and then send this in email. Is there any possibility to put data like a['name'] or difference_dates or anything elese to the text of email? I think abou something like that:
system 'echo "Your server a['name'] is not responding since difference_dates, fix it!" |mailx -s "Warning" anything#anything.com'
is it possible and correct?

Parse multiple command line options in Ruby using OptionParser

I've just started using OptionParser for Ruby and I wanted to use flags that would use more than just one argument.
For instance, I would like to be able to run:
script --move src dst
Note how src and dst are not separated using a coma.
My initial idea was:
opts.on("-m src dst", "--move src dst ", "move file from SRCto DST") do |src|
# do something
end
But this is not working. I assume that this is not the right approach. But how could this be done?
The example under the "Complete Example" section of the OptionParser details how a list of items can be accepted.
Here is a sample program based on that example. The third parameter Array in opts.on indicates that input src, dst should be used to create an array. To run this sample, you need to do gem install trollop.
# test.rb
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on("-m src, dst", "--move src, dst", Array, "Move from src to dst") do |list|
options[:src] = list[0]
options[:dst] = list[1]
end
end.parse!
puts options # It's a hash of parsed options
Sample run:
> ruby test.rb -m from,to
{:src=>"src", :dst=>"dst"}
>ruby test.rb -h
Usage: test [options]
-m, --move src, dst Move from src to dst
The above script forces one to separate the options using comma.
As indicated by "Really Cheap Command-Line Option Parsing in Ruby", there seems to be a gem, trollop, that can be quite easy to use for command-line parsing.
A sample program based on Trollop is given below, which allows usage of spaces for specifying options with multiple values
# test.rb
require "trollop"
opts = Trollop::options do
banner "Command line parsing using Trollop"
opt :move, "--move src dst', Move from src to dst", :short => "-m", :long => "--move", :type => :strings
end
# An array of option values
p opts.move
Sample run:
>ruby test.rb -m hello world
["hello", "world"]
>ruby test.rb -h
Command line parsing using Trollop
-m, --move=<s+> '--move src dst', Move from src to dst
-h, --help Show this message
There is a subtle difference between the help output between the two approaches. Trollop produces help text where --move=<s+> does not indicate clearly that it needs accepts two values, so I had to repeat the command syntax description.
OptionParser doesn't support that; It could be patched to do so, but I'm not sure it's worth the trouble.
Consider this code:
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-m', '--move') { |o| options[:move] = o }
end.parse!
from_name, to_name = ARGV
puts "Should move: #{ options.key?(:move) }"
puts "From: #{ from_name }"
puts "To: #{ to_name }"
Saving it and running it with various combinations of the parameters returns:
> ruby test.rb --move from to
Should move: true
From: from
To: to
> ruby test.rb from to
Should move: false
From:
To:
If the code is supposed to move files by default then don't bother with the --move flag, simply use:
test.rb from to
and consider removing the OptionParser block entirely.
If the code is supposed to normally copy with the option to move, then --move becomes more sensible to act as a flag that moving is desired.
ruby test.rb --move from to
I'd have code that tests for options[:move] and run the code to move instead of copy at that point.
In either case, the filenames shouldn't be tied to the flag, they should be supplied separately and retrieved from ARGV after OptionParser has finished parsing the command-line and removing entries it's handled.

Executing program from command line

I have done a program that sends requests to a url and saves them in a file. The program is this, and is working perfectly:
require 'open-uri'
n = gets.to_i
out = gets.chomp
output = File.open( out, "w" )
for i in 1..n
response = open('http://slowapi.com/delay/10').read
output << (response +"\n")
puts response
end
output.close
I want to modify it so that I can execute it from command line. I must run it like this:
fle --test abc -n 300 -f output
What must I do?
Something like this should do the trick:
#!/usr/bin/env ruby
require 'open-uri'
require 'optparse'
# Prepare the parser
options = {}
oparser = OptionParser.new do |opts|
opts.banner = "Usage: fle [options]"
opts.on('-t', '--test [STRING]', 'Test string') { |v| options[:test] = v }
opts.on('-n', '--count COUNT', 'Number of times to send request') { |v| options[:count] = v.to_i }
opts.on('-f', '--file FILE', 'Output file', :REQUIRED) { |v| options[:out_file] = v }
end
# Parse our options
oparser.parse! ARGV
# Check if required options have been filled, print help and exit otherwise.
if options[:count].nil? || options[:out_file].nil?
$stderr.puts oparser.help
exit 1
end
File::open(options[:out_file], 'w') do |output|
options[:count].times do
response = open('http://slowapi.com/delay/10').read
output.puts response # Puts the response into the file
puts response # Puts the response to $stdout
end
end
Here's a more idiomatic way of writing your code:
require 'open-uri'
n = gets.to_i
out = gets.chomp
File.open(out, 'w') do |fo|
n.times do
response = open('http://slowapi.com/delay/10').read
fo.puts response
puts response
end
end
This uses File.open with a block, which allows Ruby to close the file once the block exits. It's a much better practice than assigning the file handle to a variable and use that to close the file later.
How to handle passing in variables from the command-line as options is handled in the other answers.
The first step would be to save you program in a file, add #!/usr/bin/env ruby at the top and chmod +x yourfilename to be able to execute your file.
Now you are able to run your script from the command line.
Secondly, you need to modify your script a little bit to pick up command line arguments. In Ruby, the command line arguments are stored inside ARGV, so something like
ARGV.each do|a|
puts "Argument: #{a}"
end
allows you to retrieve command line arguments.

How do I create a ruby app that I can run commands on

I am building a little tool in ruby for creating directories and files based on commands that I issue it from the command line. I would like for this to work on Mac, Windows, and Linux.
I am of course new to ruby and I know how to right a simple script and call it to run from the command line. What I would like to do is be able to navigate anywhere on my system call the name of the app and pass args so that I can have it create files and directories based in my current location in the command line.
example $> myapp -create mydirectoryname
So what is the best way to do this. Could you guys point me to a resource that walks me through this? Thanks so much.
-Matthew
If you want something standard, See Getoptlong
require 'getoptlong'
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ],
[ '--name', GetoptLong::OPTIONAL_ARGUMENT ]
)
dir = nil
name = nil
repetitions = 1
opts.each do |opt, arg|
case opt
when '--help'
puts "Help here..."
when '--repeat'
repetitions = arg.to_i
when '--name'
if arg == ''
name = 'John'
else
name = arg
end
end
end
if ARGV.length != 1
puts "Missing dir argument (try --help)"
exit 0
end
dir = ARGV.shift
Dir.chdir(dir)
for i in (1..repetitions)
print "Hello"
if name
print ", #{name}"
end
puts
end
Example command line:
hello -n 6 --name -- /tmp
I personally like trollop, it is not included in the standard library.
Once you have the command line stuff going, see FileUtils module to create the directory:
require 'fileutils'
FileUtils.mkdir("dir")
Getoptlong mentioned by duncan is part of ruby core, but there are much nicer external libraries that let you do it in a cleaner/easier way.
I recommend you look at Choice. The examples given there should be enough to get you going.

Resources