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>?
Related
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"
I have the following Ruby script:
begin
puts "What is the password? "
the_pass = ask("") { |q| q.echo = "*" }
end while the_pass == nil || the_pass == "\n" || the_pass == ""
And it fails when I hit Enter:
undefined method default_external' for REXML::Encoding:Module
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:621:in
say'
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:914:in
get_response'
/Library/Ruby/Gems/1.8/gems/highline-1.6.19/lib/highline.rb:259:in
ask'
Looks like it fails when validating the input for the_pass, but I cannot understand the error, how are they related?
Thanks
This is bad error handling in the HighLine gem for Ruby < 1.9.
The offending line (identified by your error message) is:
statement.force_encoding(Encoding.default_external) if defined?(Encoding) && Encoding.default_external
You can handle this by either:
Removing any include REXML commands in your script. This will keep REXML::Encoding from being associated with Encoding.
Adding the following line somewhere early in your script:
REXML::Encoding.instance_eval { def default_external; false; end }
This line will prevent the missing method error and will prevent HighLine from trying to force encoding where it shouldn't.
I've written a simple Jekyll plugin to pull in my tweets using the twitter gem (see below). I'd like to keep the ruby script for the plugin on my open Github site, but following recent changes to the twitter API, the gem now requires authentication credentials.
require 'twitter' # Twitter API
require 'redcarpet' # Formatting links
module Jekyll
class TwitterFeed < Liquid::Tag
def initialize(tag_name, text, tokens)
super
input = text.split(/, */ )
#user = input[0]
#count = input[1]
if input[1] == nil
#count = 3
end
end
def render(context)
# Initialize a redcarpet markdown renderer to autolink urls
# Could use octokit instead to get GFM
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
:autolink => true,
:space_after_headers => true)
## Attempt to load credentials externally here:
require '~/.twitter_auth.rb'
out = "<ul>"
tweets = #client.user_timeline(#user)
for i in 0 ... #count.to_i
out = out + "<li>" + markdown.render(tweets[i].text) +
" <a href=\"http://twitter.com/" + #user + "/statuses/" +
tweets[i].id.to_s + "\">" + tweets[i].created_at.strftime("%I:%M %Y/%m/%d") +
"</a> " + "</li>"
end
out + "</ul>"
end
end
end
Liquid::Template.register_tag('twitter_feed', Jekyll::TwitterFeed)
If I replace the line
require '~/.twitter_auth.rb'
where twitter_auth.rb contains something like:
require 'twitter'
#client = Twitter::Client.new(
:consumer_key => "CEoYXXXXXXXXXXX",
:consumer_secret => "apnHXXXXXXXXXXXXXXXXXXXXXXXX",
:oauth_token => "105XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
:oauth_token_secret => "BJ7AlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
)
If I place these contents directly into the script above, then my plugin script works just fine. But when I move them to an external file and try to read them in as shown, Jekyll fails to authenticate. The function seems to work just fine when I call it from irb, so I am not sure why it does not work during the Jekyll build.
I think that you may be confused about how require works. When you call require, first Ruby checks if the file has already been required, if so it just returns directly. If it hasn’t then the contents of the file are run, but not in the same scope as the require statement. In other words using require isn’t the same as replacing the require statement with the contents of the file (which is how, for example, C’s #include works).
In your case, when you require your ~/.twitter_auth.rb file, the #client instance variable is being created, but as an instance variable of the top level main object, not as an instance variable of the TwitterFeed instance where require is being called form.
You could do something like assign the Twitter::Client object to a constant that you could then reference from the render method:
MyClient = Twitter::Client.new{...
and then
require '~/twitter_auth.rb'
#client = MyClient
...
I only suggest this as an explanation of what’s happening with require, it’s not really a good technique.
A better option, I think, would be to keep your credentials in a simple data format in your home directory, then read them form your script and create the Twitter client with them. In this case Yaml would probably do the job.
First replace your ~/twitter_auth.rb with a ~/twitter_auth.yaml that looks soemthing like:
:consumer_key: "CEoYXXXXXXXXXXX"
:consumer_secret: "apnHXXXXXXXXXXXXXXXXXXXXXXXX"
:oauth_token: "105XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
:oauth_token_secret: "BJ7AlXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
Then where you have requre "~/twitter_auth.rb" in your class, replace with this (you’ll also need require 'yaml' at the top of the file):
#client = Twitter::Client.new(YAML.load_file("~/twitter_auth.yaml"))
I'm trying to read this ATOM Feed (http://ffffound.com/feed), but I'm unable to get to any of the values which are defined as part of the namespace e.g. media:content and media:thumbnail.
Do I need to make the parser aware of the namespaces?
Here's what I 've got:
require 'rss/2.0'
require 'open-uri'
source = "http://ffffound.com/feed"
content = ""
open(source) do |s| content = s.read end
rss = RSS::Parser.parse(content, false)
I believe you would have to use libxml-ruby for that.
gem 'libxml-ruby', '>= 0.8.3'
require 'xml'
xml = open("http://ffffound.com/feed")
parser = XML::Parser.string(xml, :options =>XML::Parser::Options::RECOVER)
doc = parser.parse
doc.find("channel").first.find("items").each do |item|
puts item.find("media:content").first
#and just guessing you want that url thingy
puts item.find("media:content").first.attributes.get_attribute("url").value
end
I hope that points you in the right direction.
I've written some ruby CGI scripts (using the Ruby CGI class) that I serve from my production server using lighttpd. I want to test them on my development server using thin. Basically, I want to drop all my CGI scripts in a directory and start thin in that directory. Then, any requests to http://localhost:3000/<script> should just execute <script> in the current directory and return the results. If thin has a built-in way of doing this, I can't find it. I would imagine the Rack config file for this is easy if you know what you're doing, but I don't.
Update:
This rackup file seems to work. I'm not sure if it's the best solution, but it should be fine for a development environment.
run(lambda do |env|
require 'rubygems'
require 'systemu'
script = env['REQUEST_PATH'][1..-1] + '.rb'
response = ''
err = ''
systemu(['ruby', script], 'stdout' => response, 'stderr' => err, 'env' => {
'foo' => 'bar' })
if err.length > 0
[ 500, {'Content-Type' => 'text/plain'}, err ]
else
idx = 0
status = -1
headers = {}
while true
line_end = response.index("\n", idx)
line = response[idx..line_end].strip
idx = line_end+1
if status < 0
if line =~ /(\d\d\d)/
status = $1.to_i
else
raise "Invalid status line: #{line}"
end
elsif line.empty?
break
else
name, value = line.split /: ?/
headers[name] = value
end
end
content = response[idx..-1]
[status, headers, content]
end
end)
I'm a little unclear as to why Rack is necessary at all. If you wrote the script using Ruby's built-in CGI module, you should be able to just tell thin to treat the directory as a cgi-bin,just like the Apache ScriptAlias directive, and Ruby CGI will take care of the rest. If thin can't do this, perhaps lighttpd would be a better solution.