How do I run a Ruby script in the Terminal? - ruby

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"

Related

Ruby Scripts - Reference Gems

I am writing my first ruby script and am curious how to actually have gem referenced in the script. I am unable to test the code before hand because it reads form an email in /etc/aliases through a pipe.
Any one one with experiences with ruby scripts to advise?
P.S So many bugs because not tested or refactored
Sample Script
#!/usr/bin/env ruby
# Reading files
mail = File.open(ARGV[0])
lines = []
mail.each_with_index do |i,line|
line[i] = lines.#remove leading and trailing spaces
end
first_line = line[1].strip
if line[1] /^(256)/
phone_number = first_line.gsub("+", "")
else
phone_number = "256#{first_line.gsub(/^0+/,"")}"
end
message = line[2].strip
# Sending message
url = "http://xxxxxxxxxxx.com/api/v2/json/messages?token=XXXXXXXXXXXXXXXXXXXXXXXXXXX&to=#{phone_number}&from=XXXXXX&message=#{CGI.escape(message)}"
5.times do |i|
response = HTTParty.get(url)
body = JSON.parse(response.body)
if body["status"] == "Success"
break
end
end
Gems in question are CGI, Httparty, and Json parsing.
Using external gems can be done by calling the "require" method.
So to include them in your script, the first few lines could be something like this:
#!/usr/bin/env ruby
require "json"
require "cgi"
require "httparty"
#rest of your code...
I assume you have installed your gems with gem install <gemname>?

RuntimeError when running script?

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.

How do I create directory if none exists using File class in Ruby?

I have this statement:
File.open(some_path, 'w+') { |f| f.write(builder.to_html) }
Where
some_path = "somedir/some_subdir/some-file.html"
What I want to happen is, if there is no directory called somedir or some_subdir or both in the path, I want it to automagically create it.
How can I do that?
You can use FileUtils to recursively create parent directories, if they are not already present:
require 'fileutils'
dirname = File.dirname(some_path)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
Edit: Here is a solution using the core libraries only (reimplementing the wheel, not recommended)
dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"
1.upto(tokens.size) do |n|
dir = tokens[0...n]
Dir.mkdir(dir) unless Dir.exist?(dir)
end
For those looking for a way to create a directory if it doesn't exist, here's the simple solution:
require 'fileutils'
FileUtils.mkdir_p 'dir_name'
Based on Eureka's comment.
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
How about using Pathname?
require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
Based on others answers, nothing happened (didn't work). There was no error, and no directory created.
Here's what I needed to do:
require 'fileutils'
response = FileUtils.mkdir_p('dir_name')
I needed to create a variable to catch the response that FileUtils.mkdir_p('dir_name') sends back... then everything worked like a charm!
Along similar lines (and depending on your structure), this is how we solved where to store screenshots:
In our env setup (env.rb)
screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
FileUtils.mkdir_p(screenshotfolder)
end
Before do
#screenshotfolder = screenshotfolder
...
end
And in our hooks.rb
screenshotName = "#{#screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
#browser.take_screenshot(screenshotName) if scenario.failed?
embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?
The top answer's "core library" only solution was incomplete. If you want to only use core libraries, use the following:
target_dir = ""
Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end
# Splits name into pieces
tokens = target_dir.split(/\//)
# Start at '/'
new_dir = '/'
# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|
# Builds directory path one folder at a time from top to bottom
unless n == (tokens.size - 1)
new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
else
new_dir << "#{tokens[n].to_s}" # Innermost folder
end
# Creates directory as long as it doesn't already exist
Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end
I needed this solution because FileUtils' dependency gem rmagick prevented my Rails app from deploying on Amazon Web Services since rmagick depends on the package libmagickwand-dev (Ubuntu) / imagemagick (OSX) to work properly.

Moving a file containing a space in ruby using FileUtils

I'm using Mac OS X and I'm trying to write a little script that moves a file to a specific folder. I'm using the FileUtils API since I don't want to run system specific commands (system("mv a b").
The script looks something like this:
#!/usr/bin/env ruby
require 'rubygems'
require 'escape'
require 'fileutils'
absolut_input_filename = Escape.shell_single_word ARGV[0]
move_folder = Escape.shell_single_word "/move/to/folder"
FileUtils.mv absolut_input_filename, move_folder
As long as the input filename contains no space, everything works fine. But as soon as I put in a file with a space the error output is something like this:
./scripts/success /path/to/file\ with\ space
/usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1418:in `stat': No such file or directory - '/path/to/file with space' (Errno::ENOENT)
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1418:in `block in fu_each_src_dest'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1432:in `fu_each_src_dest0'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:1416:in `fu_each_src_dest'
from /usr/local/Cellar/ruby/1.9.2-p0/lib/ruby/1.9.1/fileutils.rb:504:in `mv'
from ./scripts/success:8:in `<main>'
For escaping I use the 'escape' gem in version 0.0.4 in which the shell_single_word looks like this:
def shell_single_word(str)
if str.empty?
"''"
elsif %r{\A[0-9A-Za-z+,./:=#_-]+\z} =~ str
str
else
result = ''
str.scan(/('+)|[^']+/) {
if $1
result << %q{\'} * $1.length
else
result << "'#{$&}'"
end
}
result
end
end
you can just not use escape
require 'fileutils'
absolut_input_filename = ARGV[0]
move_folder = "/move/to/folder"
FileUtils.mv absolut_input_filename, move_folder
I don't actually know from Ruby, so take this with a grain of salt, but I know the underlying OS primitives inside and out, and from C you can do this with rename(2). Therefore, from Ruby, you should be able to do this with File.rename, which requires no quoting at all. Try this:
#! /usr/bin/env ruby
tomove = ARGV[0]
target = "/path/to/target/folder"
File.rename(tomove, File.join(target, File.basename(tomove)))
Solved using soft links:
ln -s ~/Folder\ with\ spaces/foo/ ./foo
now i can use FileUtils commands without problems:
FileUtils.cp("bar.txt", "foo/foobar.txt")
Hope will help!

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