Error traces in Erubis - ruby

By default, when an an Erubis template raises an error, you get something like this:
(erubis):32:in `evaluate': compile error (SyntaxError)
(erubis):30: syntax error, unexpected ')', expecting ']'
(erubis):32: unterminated string meets end of file
The line numbers refer to the template.
That's all well and good when you just have one template, but I'm batch-processing a bunch of template files. What's the best way to replace the above with a more usable error message, e.g. one that shows the path to the source file instead of (erubis):32?
I'd thought of rescuing, messing around with the exception object, and raising again, but I'm wondering if there's an easier way provided by the Erubis API (or some other one).

You can pass :filename parameter to Erubis.
eruby = Erubis::Eruby.new(string, :filename=>"file.rhtml")

I still suspect there might be a better way to do this using the Erubis API, but here's some code I wrote that seems to work:
def compile_template(template_path, template_str, context, &block)
begin
Erubis::Eruby.new(template_str).evaluate(context, &block)
rescue Exception => exc
trace_normalizer = lambda { |line| line.gsub(/^\(erubis\):/, template_path + ':') }
backtrace = exc.backtrace.collect(&trace_normalizer)
message = trace_normalizer.call(exc.message)
raise exc.class, message, backtrace
end
end

Related

run external program in Ruby IO.popen : rescue not working

I'm using the Tika jar to extract metadata from Microsoft Word doc files but in the case Tika encounters a problem my rescue is not catching the error, instead the scripts exits. I'm on windows 7 with MRI Ruby 1.9.3
I could adapt the doc file but I want to avoid having this problem with future files.
How can I capture this error ?
JARPATH = "jar/tika-app-1.6.jar"
def metadata
return #metadata if defined? #metadata
switch = '-m -j'
begin
command = %Q{java -Djava.awt.headless=true -jar #{JARPATH} #{switch} "#{#path}"}
output = IO.popen(command+" 2>&1") do |io|
io.read
end
if output.respond_to?(:to_str)
#metadata = JSON.parse(output)
else
#metadata = nil
end
rescue => e
puts e
puts e.backtrace
end
end
This is the output I get
c:/Ruby193/lib/ruby/gems/1.9.1/gems/json-1.8.2/lib/json/common.rb:155:in `parse': 757: unexpected token at 'Exception in thread "main" org.apache.tika.exception.TikaException: TIKA-198: Illegal IOExce
ption from org.apache.tika.parser.microsoft.OfficeParser#1006d75 (JSON::ParserError)
at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:250)
at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:244)
at org.apache.tika.parser.AutoDetectParser.parse(AutoDetectParser.java:121)
at org.apache.tika.cli.TikaCLI$OutputType.process(TikaCLI.java:143)
at org.apache.tika.cli.TikaCLI.process(TikaCLI.java:422)
at org.apache.tika.cli.TikaCLI.main(TikaCLI.java:113)
Caused by: java.io.IOException: Invalid header signature; read 0x04090000002DA5DB, expected 0xE11AB1A1E011CFD0 - Your file appears not to be a valid OLE2 document
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:140)
at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:115)
at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.<init>(NPOIFSFileSystem.java:204)
at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.<init>(NPOIFSFileSystem.java:163)
at org.apache.tika.parser.microsoft.OfficeParser.parse(OfficeParser.java:162)
at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:244)
... 5 more
'
from c:/Ruby193/lib/ruby/gems/1.9.1/gems/json-1.8.2/lib/json/common.rb:155:in `parse'
from C:/Users/.../tika.rb:37:in `metadata'
from C:/Users/.../index_helpers.rb:55:in `index_doc'
from index.rb:39:in `block in <main>'
from index.rb:20:in `each'
from index.rb:20:in `each_with_index'
from index.rb:20:in `<main>'
After your call to IO.popen you are passing the output from the child program to JSON.parse, regardless of whether it is valid. The exception you see is the json parser trying to parse the Java exception method, which is captured because you redirect stderr with 2>&1.
You need to check that the child process completed successfully before continuing. The simplest way is probably to use the $? special variable, which indicates the status of the last executed child process, after the call to popen. This variable is an instance if Process::Status. You could do something like this:
output = IO.popen(command+" 2>&1") do |io|
io.read
end
unless $?.success?
# Handle the error however you feel is best, e.g.
puts "Tika had an error, the message was:\n#{output}"
raise "Tika error"
end
For more control you could look at the Open3 module in the standard library. Since Tika is a Java program, another possibility might be to look into using JRuby and call it directly.

Syntax error in for loop (Ruby/RSpec)

While running the RSPEC test as shown below im getting this error:
Using Accessor#strict_set for specs
SyntaxError: /home/sam/projects/logstash.king-foo.dev/ansible/roles/logstash/spec/syslog.rb:6: syntax error, unexpected kEND
end
^
load at org/jruby/RubyKernel.java:1101
(root) at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:1
each at org/jruby/RubyArray.java:1613
load_spec_files at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896
load_spec_files at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896
run at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/command_line.rb:22
I tried messing around with the syntax but without success.
files = Dir['../configs/filter*.conf']
##configuration = String.new
files.sort.each.do |file|
##configuration << File.read(file)
end
describe "my first logstash rspec test", :if => RUBY_ENGINE == "jruby" do
extend LogStash::RSpec
config(##configuration)
... some code here ...
end
Does anybody know what i'm doing wrong?
Why do i get a syntax error for the end statement ander the ##configuration variable?
The error means there was an unexpected end in your code. Just simply replace the 3rd line with
files.sort.each do |file|
I optionally recommend you use { and } instead of do and end. The { and } are space-insensitive and you are less likely to receive an error than do and end.

Stock quote gem - retrieval for nonexistent ticker yields nomethod error

I am using the "stock quote" gem (https://github.com/tyrauber/stock_quote) to retrieve stock prices based on user input tickers. While I have a ticker list that is up-to-date, there are some circumstances where the search yields no results. I have this in my code to get the quote:
#companyname = StockQuote::Stock.quote(#ticker).company
#exchange = StockQuote::Stock.quote(#ticker).exchange
#price = StockQuote::Stock.quote(#ticker).last
And it yields this when #ticker = "AKO-A"
undefined method `attributes' for nil:NilClass
file: stock.rb location: block in parse line: 90
Is there anyway to avoid this nomethoderror by making my code more robust (if error then "blank")? Sorry, I am relatively new to ruby and would appreciate any help to point me in the right direction.
Yeah, the problem was definitely with the gem. It was assuming the symbol was accurate and wasn't properly parsing responses for bad symbols.
Sloppy. Rewrote the classes for cleaner code and greater stability. Added in a response_code instance method, which returns 200 or 404, depending upon the validity of the response. Also, a success? or failure? instance method. And, better spec coverage.
Version bumped, and pushed to rubygems.
This is a very common condition with Ruby code, and a common idiom to return nil on a failed search.
However this specific gem is a little flaky when it fails to get a good search result. You can protect yourself against it failing by using a begin ... rescue block.
begin
stock_quote = StockQuote::Stock.quote(#ticker)
rescue StandardError
stock_quote = nil
end
if stock_quote
#companyname = stock_quote.company
#exchange = stock_quote.exchange
#price = stock_quote.last
end
This might not be ideal program flow for you, so you may need to adapt this.
Note StandardError is what gets rescued by default, I didn't need to write that. You could also put NoMethodError in your situation, and usually you want to restrict rescuing exceptions to specific parts of code where you know how to recover from the error, and also only to the types of errors where you are confident that your handling code is doing the right thing.
Here is an example on how use rescue to get around the nonexistent stock symbol problem
require 'stock_quote'
class StockClass
def self.symbol_check(symbol)
StockQuote::Stock.quote(symbol).symbol
end
def self.price_by_symbol(symbol)
StockQuote::Stock.quote(symbol).latest_price
end
def self.write_price_by_symbol(symbol, price)
filename = "#{symbol}.csv"
todays_date = Time.now.strftime('%Y-%m-%d')
File.open(filename, "a") do |file|
file << "#{todays_date}, #{price}\n"
end
end
end
def stock_price_selector(*symbol_array)
symbol_array.each do |stock_name|
begin
stock_check = StockClass.symbol_check(stock_name)
rescue NoMethodError
puts "#{stock_name} is a bogus ticker symbol"
else
stock_price = StockClass.price_by_symbol(stock_name)
stock_written = StockClass.write_price_by_symbol(stock_name, stock_price)
end
end
end
stock_price_selector('AAPL', 'APPL', 'MSFT', 'GOOG')
This will skip the bogus symbol 'APPL' and work for the legtimate ticker symbols.

How do I check if a string is valid YAML?

I'd like to check if a string is valid YAML. I'd like to do this from within my Ruby code with a gem or library. I only have this begin/rescue clause, but it doesn't get rescued properly:
def valid_yaml_string?(config_text)
require 'open-uri'
file = open("https://github.com/TheNotary/the_notarys_linux_mint_postinstall_configuration")
hard_failing_bad_yaml = file.read
config_text = hard_failing_bad_yaml
begin
YAML.load config_text
return true
rescue
return false
end
end
I am unfortunately getting the terrible error of:
irb(main):089:0> valid_yaml_string?("b")
Psych::SyntaxError: (<unknown>): mapping values are not allowed in this context at line 6 column 19
from /home/kentos/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/psych.rb:203:in `parse'
from /home/kentos/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/psych.rb:203:in `parse_stream'
from /home/kentos/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/psych.rb:151:in `parse'
from /home/kentos/.rvm/rubies/ruby-1.9.3-p374/lib/ruby/1.9.1/psych.rb:127:in `load'
from (irb):83:in `valid_yaml_string?'
from (irb):89
from /home/kentos/.rvm/rubies/ruby-1.9.3-p374/bin/irb:12:in `<main>'
Using a cleaned-up version of your code:
require 'yaml'
require 'open-uri'
URL = "https://github.com/TheNotary/the_notarys_linux_mint_postinstall_configuration"
def valid_yaml_string?(yaml)
!!YAML.load(yaml)
rescue Exception => e
STDERR.puts e.message
return false
end
puts valid_yaml_string?(open(URL).read)
I get:
(<unknown>): mapping values are not allowed in this context at line 6 column 19
false
when I run it.
The reason is, the data you are getting from that URL isn't YAML at all, it's HTML:
open('https://github.com/TheNotary/the_notarys_linux_mint_postinstall_configuration').read[0, 100]
=> " \n\n\n<!DOCTYPE html>\n<html>\n <head prefix=\"og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog:"
If you only want a true/false response whether it's parsable YAML, remove this line:
STDERR.puts e.message
Unfortunately, going beyond that and determining if the string is a YAML string gets harder. You can do some sniffing, looking for some hints:
yaml[/^---/m]
will search for the YAML "document" marker, but a YAML file doesn't have to use those, nor do they have to be at the start of the file. We can add that in to tighten up the test:
!!YAML.load(yaml) && !!yaml[/^---/m]
But, even that leaves some holes, so adding in a test to see what the parser returns can help even more. YAML could return an Fixnum, String, an Array or a Hash, but if you already know what to expect, you can check to see what YAML wants to return. For instance:
YAML.load(({}).to_yaml).class
=> Hash
YAML.load(({}).to_yaml).instance_of?(Hash)
=> true
So, you could look for a Hash:
parsed_yaml = YAML.load(yaml)
!!yaml[/^---/m] && parsed_yaml.instance_of(Hash)
Replace Hash with whatever type you think you should get.
There might be even better ways to sniff it out, but those are what I'd try first.

How can I pass a hash into a class_eval'ed method with Ruby 1.8.7?

I'm trying to mock the OpenID handling in my cucumber tests. For this purpose I use the following method:
def set_result_of_openid_authentication(result_type, profile_data = nil)
ActionController::Base.class_eval "
def begin_open_id_authentication(identity_url, options = {})
yield [OpenIdAuthentication::Result.new('#{result_type}'.to_sym), identity_url, #{profile_data}]
end
"
end
# example usage
set_result_of_openid_authentication :successful, 'email' => 'dhofstet#example.com'
This works fine with Ruby 1.9.2, but with Ruby 1.8.7 I get the following compile error:
(eval):5:in `set_result_of_openid_authentication': compile error
(eval):3: syntax error, unexpected tIVAR, expecting kDO or '{' or '('
...identity_url, emaildhofstet#example.com]
For some reason the hash is not preserved... Is there some workaround to make it work with both Rubies?
Thanks.
It looks like the problem is that inside your class_eval the interpolated string #{profile_data} is coming through as emaildhofstet#example.com which is the to_s representation of a Hash in 1.8.7.
If you replace this with #{profile_data.inspect} then it should come come through as {'email' => 'dhofstet#example.com'} as required.

Resources