Stock quote gem - retrieval for nonexistent ticker yields nomethod error - ruby

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.

Related

How do I tack a string onto a variable and evaluated the entire thing as a variable in Ruby?

I have the following Ruby code:
module BigTime
FOO1_MONEY_PIT = 500
FOO2_MONEY_PIT = 501
class LoseMoney
##SiteName = 'FOO1'
#site_num = ##SiteName_MONEY_PIT
def other_unimportant_stuff
whatever
end
end
end
So, what I'm trying to do here is set the SiteName and then use SiteName and combine it with the string _MONEY_PIT so I can access FOO1_MONEY_PIT and store its contents (500 in this case) in #site_num. Of course, the above code doesn't work, but there must be a way I can do this?
Thanks!!
If you want to dynamically get the value of a constant, you can use Module#const_get:
module BigTime
FOO1_MONEY_PIT = 500
FOO2_MONEY_PIT = 501
class LoseMoney
##SiteName = 'FOO1'
#site_num = BigTime.const_get(:"#{##SiteName}_MONEY_PIT")
end
end
Do not, under any circumstance, use Kernel#eval for this. Kernel#eval is extremely dangerous in any context where there is even the slightest possibility that an attacker may be able to control parts of the argument.
For example, if a user can choose the name of the site, and they name their site require 'fileutils'; FileUtils.rm_rf('/'), then Ruby will happily evaluate that code, just like you told it to!
Kernel#eval is very dangerous and you should not get into the habit of just throwing an eval at a problem. It is a very specialized tool that should only be employed when there is no other option (spoiler alert: there almost always is another option), and only after a thorough security review.
Please note that dynamically constructing variable names is already a code smell by itself, regardless of whether you use eval or not. It pretty much always points to a design flaw somewhere. In general, you can almost guaranteed replace the multiple variables with a data structure. E.g. in this case something like this:
module BigTime
MONEY_PITS = {
'FOO1' => 500,
'FOO2' => 501,
}.freeze
class LoseMoney
##SiteName = 'FOO1'
#site_num = MONEY_PITS[##SiteName]
end
end
You can refactor this as to use a Hash for your name lookups, and a getter method to retrieve it for easy testing/validation. For example:
module BigTime
MONEY_PITS = { FOO1: 500, FOO2: 501 }
MONEY_PIT_SUFFIX = '_MONEY_PIT'
class LoseMoney
##site = :FOO1
def initialize
site_name
end
def site_name
#site_name ||= '%d%s' % [MONEY_PITS[##site], MONEY_PIT_SUFFIX]
end
end
end
BigTime::LoseMoney.new.site_name
#=> "500_MONEY_PIT"

Can you pass a block of code that returns an error to a method?

I often find myself dealing with these kind of scenarios:
require 'nokogiri'
require "open-uri"
url = "https://www.random_website.com/contains_info_I_want_to_parse"
nokodoc = Nokogiri::HTML(open(url))
# Let's say one of the following line breaks the ruby script
# because the element I'm searching doesn't contain an attribute.
a = nokodoc.search('#element-1').attribute('href').text
b = nokodoc.search('#element-2').attribute('href').text.gsub("a", "A")
c = nokodoc.search('#element-3 h1').attribute('style').text.strip
What happens is that I'll be creating about 30 variables all searching for different elements in a page, and I'll be looping that code over multiple pages. However, a few of these pages may have an ever-so-slightly different layout and won't have one of those div. This will break my code (because you can't call .attribute or .gsub on nil for example). But I can never guess which line before-hand.
My go-to solution is usually surround each line with:
begin
line #n
rescue
puts "line #n caused an error"
end
I'd like to be able to do something like:
url = "https://www.random_website.com/contains_info_I_want_to_parse"
nokodoc = Nokogiri::HTML(open(url))
catch_error(a, nokodoc.search('#element-1').attribute('href').text)
catch_error(b, nokodoc.search('#element-2').attribute('href').text.gsub("a", "A"))
catch_error(c, nokodoc.search('#element-3 h1').attribute('style').text.strip)
def catch_error(variable_name, code)
begin
variable_name = code
rescue
puts "Code in #{variable_name} caused an error"
end
variable_name
end
I know that putting & before each new method works:
nokodoc.search('#element-1')&.attribute('href')&.text
But I want to be able to display the error with a 'puts' in my terminal to see when my code gives an error.
Is it possible?
You can't pass your code as a regular argument to a method because it'll be evaluated (and raise an exception) before it gets passed to your catch_error method. You could pass it as a block--something like
a = catch_error('element_1 href text') do
nokodoc.search('#element-1').attribute('href').text
end
def catch_error(error_description)
yield
rescue
puts "#{error_description} caused an error"
end
Note that you can't pass a to the method as variable_name: it hasn't been defined anywhere before calling that method, so you'll get an undefined local variable or method error. Even if you define a earlier, it won't work correctly. If your code works without raising an exception, the method will return the right value but the value won't get stored anywhere outside the method scope. If there is an exception, variable_name will have whatever value a had before the method (nil if you defined it without setting it), so your error message would output something like Code in caused an error. That's why I added an error_description parameter.
You could also try logging the message and backtrace if you didn't want to have to specify an error description every time.
a = catch_error(nokodoc) do |doc|
doc.search('#element-1').attribute('href').text
end
def catch_error(doc)
yield doc
rescue => ex
puts doc.title # Or something else that identifies the document
puts ex.message
puts ex.backtrace.join("\n")
end
I made one additional change here: passing the document in as a parameter so that rescue could easily log something that identifies the document, in case that's important.

Test nested File.open

I need to test a file open operation. I am able to test the first operation but not the second.
File.open("#{TemplateFile.fixture_path}/#{#template_file}") do |input_file|
template = ERB.new(input_file.read)
File.open("#{#project_name}/#{#destination_file}", 'w') do |output_file|
output_file.puts template.result binding
end
end
end
I am using this code:
module Pod
describe TemplateFile do
it "opens the template" do
dict = {"README.md.erb" => "README.md"}
File.expects(:open).with("#{TemplateFile.fixture_path}/README.md.erb")
File.expects(:open).with("Sample/README.md.erb", 'w')
TemplateFile.new(dict, "Sample")
end
end
end
But I am getting an error:
unsatisfied expectations:
- expected exactly once, not yet invoked: File.open('/README.md.erb', 'w')
satisfied expectations:
- expected exactly once, invoked once: File.open('/lib/pod/command/../../../fixtures/README.md.erb')
It seems that Mocha is not geeting the second File.open.
The reason is because expects verifies the call would happen but doesn't actually let it go through. So what's in the block doesn't get run.
However, beyond just telling you why it's not working, I also wanted to point out what you are doing is probably not what you want to do.
What you likely want to do do is:
template = ERB.new(File.read("#{TemplateFile.fixture_path}/#{#template_file}"))
File.open("#{#project_name}/#{#destination_file}", 'w') do |output_file|
output_file.puts template.result binding
end
You don't need that nesting.
Then when testing what you want to do to verify your the correct file is read is:
File.expects(:read).with("#{TemplateFile.fixture_path}/README.md.erb").returns(some_known_fixture)
The returns part says when it does get this read method with the specified argument I want you to return this known thing so that template will have a good value for the rest of the code.

What is "return can't jump across threads" error mean?

I have a pair of Puppet custom functions, one of which, namely am_func_cluster, returns a hash of array of currently running instances (reading a file as input) and the second one call that function, iterate over the array and returns the first successful one that listening to port 22. Here is the 2nd function:
module Puppet::Parser::Functions
newfunction(:am_func_head, :type => :rvalue ) do |args|
Puppet::Parser::Functions.function('am_func_cluster')
mCls = function_am_func_cluster(['/opt/running-inst.txt'])
cls = args[0].to_sym if args[0].is_a? String
require 'socket'
require 'timeout'
mCls[cls].each do |dns|
begin
Timeout::timeout(1) { TCPSocket.new(dns, 22)
return (dns if mCls.key?(cls)) || 'undefined'
}
break
rescue SocketError
rescue Timeout::Error
end
end
end
end
upon running, it returns this error:
Error: Could not retrieve catalog from remote server: Error 400 on
SERVER: return can't jump across threads at
/etc/puppet/manifests/nodes.pp:19 on node ip-10-0-9-130.xxx
It works just fine, if I comment out the begin ... end bit in the script. Google didn't favor much in this case, so far. Does anyone one know what that error means or what am I doing wrong. Still don't find myself very efficient understanding the errors returned by Ruby. Any pointer much appreciated. Cheers!!
As it says, you cannot use return there. Use break to escape from the timeout block:
require "timeout"
Timeout.timeout(3){break "foo"}
# => "foo"
and you should not use break outside of it.
As a general tip, it is easy to confuse return, break, and next. If one of them does not work, try another.

How do I get ruby to print a full backtrace instead of a truncated one?

When I get exceptions, it is often from deep within the call stack. When this happens, more often than not, the actual offending line of code is hidden from me:
tmp.rb:7:in `t': undefined method `bar' for nil:NilClass (NoMethodError)
from tmp.rb:10:in `s'
from tmp.rb:13:in `r'
from tmp.rb:16:in `q'
from tmp.rb:19:in `p'
from tmp.rb:22:in `o'
from tmp.rb:25:in `n'
from tmp.rb:28:in `m'
from tmp.rb:31:in `l'
... 8 levels...
from tmp.rb:58:in `c'
from tmp.rb:61:in `b'
from tmp.rb:64:in `a'
from tmp.rb:67
That "... 8 levels..." truncation is causing me a great deal of trouble. I'm not having much success googling for this one: How do I tell ruby that I want dumps to include the full stack?
Exception#backtrace has the entire stack in it:
def do_division_by_zero; 5 / 0; end
begin
do_division_by_zero
rescue => exception
puts exception.backtrace
raise # always reraise
end
(Inspired by Peter Cooper's Ruby Inside blog)
You could also do this if you'd like a simple one-liner:
puts caller
This produces the error description and nice clean, indented stacktrace:
begin
# Some exception throwing code
rescue => e
puts "Error during processing: #{$!}"
puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
end
IRB has a setting for this awful "feature", which you can customize.
Create a file called ~/.irbrc that includes the following line:
IRB.conf[:BACK_TRACE_LIMIT] = 100
This will allow you to see 100 stack frames in irb, at least. I haven't been able to find an equivalent setting for the non-interactive runtime.
Detailed information about IRB customization can be found in the Pickaxe book.
One liner for callstack:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace; end
One liner for callstack without all the gems's:
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//); end
One liner for callstack without all the gems's and relative to current directory
begin; Whatever.you.want; rescue => e; puts e.message; puts; puts e.backtrace.grep_v(/\/gems\//).map { |l| l.gsub(`pwd`.strip + '/', '') }; end
This mimics the official Ruby trace, if that's important to you.
begin
0/0 # or some other nonsense
rescue => e
puts e.backtrace.join("\n\t")
.sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
end
Amusingly, it doesn't handle 'unhandled exception' properly, reporting it as 'RuntimeError', but the location is correct.
Almost everybody answered this. My version of printing any rails exception into logs would be:
begin
some_statement
rescue => e
puts "Exception Occurred #{e}. Message: #{e.message}. Backtrace: \n #{e.backtrace.join("\n")}"
Rails.logger.error "Exception Occurred #{e}. Message: #{e.message}. Backtrace: \n #{e.backtrace.join("\n")}"
end
I was getting these errors when trying to load my test environment (via rake test or autotest) and the IRB suggestions didn't help. I ended up wrapping my entire test/test_helper.rb in a begin/rescue block and that fixed things up.
begin
class ActiveSupport::TestCase
#awesome stuff
end
rescue => e
puts e.backtrace
end
[examine all threads backtraces to find the culprit]
Even fully expanded call stack can still hide the actual offending line of code from you when you use more than one thread!
Example: One thread is iterating ruby Hash, other thread is trying to modify it. BOOM! Exception! And the problem with the stack trace you get while trying to modify 'busy' hash is that it shows you chain of functions down to the place where you're trying to modify hash, but it does NOT show who's currently iterating it in parallel (who owns it)! Here's the way to figure that out by printing stack trace for ALL currently running threads. Here's how you do this:
# This solution was found in comment by #thedarkone on https://github.com/rails/rails/issues/24627
rescue Object => boom
thread_count = 0
Thread.list.each do |t|
thread_count += 1
err_msg += "--- thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace begin \n"
# Lets see if we are able to pin down the culprit
# by collecting backtrace for all existing threads:
err_msg += t.backtrace.join("\n")
err_msg += "\n---thread #{thread_count} of total #{Thread.list.size} #{t.object_id} backtrace end \n"
end
# and just print it somewhere you like:
$stderr.puts(err_msg)
raise # always reraise
end
The above code snippet is useful even just for educational purposes as it can show you (like x-ray) how many threads you actually have (versus how many you thought you have - quite often those two are different numbers ;)
You can also use backtrace Ruby gem (I'm the author):
require 'backtrace'
begin
# do something dangerous
rescue StandardError => e
puts Backtrace.new(e)
end

Resources