Ruby variable value set to true - ruby-on-rails

I am new to Ruby/ Ruby on Rails. I wrote the below code to get a boolean value from api. The API call failed and it went to the rescue block. But for some reason, it set the value to true. I don't understand how that happened.
#is_attached = OnDeck.api.check_is_attached(#decision.application_number, current_user)
Api call / client wrapper Code
def check_is_attached(app_number, user)
begin
client.check_is_attached(app_number, user.auth_token)
rescue OnDeckException::MerchantApiException => e
Rails.logger.error("Caught exception #{e} while calling check_mbs_attached for app_number : #{app_number}")
end
end

The rails logger returns true on successfully logging:
[2] pry(main)> Rails.logger.error("Caught exception")
Caught exception
=> true
Because that is the last line executed in the method, that value is returned as ruby has implicit return.

Rails.logger.error(string)
returns true as can be seen:
2.3.1 :002 > Rails.logger.error('foo')
foo
=> true

Related

Rescue not capturing failure

I'm missing something here, but for some reason my begin then rescue Ruby code isn't capturing this error:
#<ActiveResource::ResourceInvalid: Failed. Response code = 422. Response message = Unprocessable Entity.>
This is my code:
begin
ShopifyAPI::CarrierService.create(with some arguments)
rescue StandardError => e
pp e
end
It doesn't ever capture it. In my rescue section I've tried the above but also:
rescue Exception => e
rescue ActiveResource::Errors => e
All with no luck. Where did I go astray?
Thanks
EDIT:
This is the full error, it not really anymore info, but here goes:
#<ShopifyAPI::CarrierService:0x0000000357a0a0
#attributes=
{"name"=>"XXXX",
"callback_url"=>
"https://XX-XX-XX-XX.c9users.io/receive_rate_request",
"format"=>"json",
"service_discovery"=>"true",
"carrier_service_type"=>"api"},
#errors=
#<ActiveResource::Errors:0x00000003578930
#base=#<ShopifyAPI::CarrierService:0x0000000357a0a0 ...>,
#messages={:base=>["you already have XXX set up for this shop"]}>,
#persisted=false,
#prefix_options={},
#remote_errors=
#<ActiveResource::ResourceInvalid: Failed. Response code = 422. Response message = Unprocessable Entity.>,
#validation_context=nil>
That's it!
Because it is not raising an exception, If you want to raise the exception when the response is false, you may have to use create with bang
begin
ShopifyAPI::CarrierService.create!(with some arguments)
rescue StandardError => e
pp e
end
According to the ActiveResource code (lib/active_resource/base.rb):
# <tt>404</tt> is just one of the HTTP error response codes that Active Resource will handle with its own exception. The
# following HTTP response codes will also result in these exceptions:
#
# * 200..399 - Valid response. No exceptions, other than these redirects:
# * 301, 302, 303, 307 - ActiveResource::Redirection
# * 400 - ActiveResource::BadRequest
# * 401 - ActiveResource::UnauthorizedAccess
# * 403 - ActiveResource::ForbiddenAccess
# * 404 - ActiveResource::ResourceNotFound
...
# * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors)
So it indicates that 422's are rescued by save on validation, which happens when .create is fired, and are bubbled up as validation errors instead.
Looking at lib/active_resource/validations.rb, you can see the ResourceInvalid exception is gobbled:
# Validate a resource and save (POST) it to the remote web service.
# If any local validations fail - the save (POST) will not be attempted.
def save_with_validation(options={})
perform_validation = options[:validate] != false
# clear the remote validations so they don't interfere with the local
# ones. Otherwise we get an endless loop and can never change the
# fields so as to make the resource valid.
#remote_errors = nil
if perform_validation && valid? || !perform_validation
save_without_validation
true
else
false
end
rescue ResourceInvalid => error
# cache the remote errors because every call to <tt>valid?</tt> clears
# all errors. We must keep a copy to add these back after local
# validations.
#remote_errors = error
load_remote_errors(#remote_errors, true)
false
end
So I wonder if it's logging that an exception happened, but is not actually raising an exception because it turns it in to a return false. It does say "local validations" in the comment, but sets remote_errors, so it's not perfectly clear where this code path is executed.

RSpec be_equal not working

I am writing some rspec examples for a plain old ruby class in my rails project and I am facing the following problem.
I have this constructor:
class Server
def initialize(host='localhost',options={:port => 443, :password => '', :vpncmd_bin_path => '/usr/local/bin/vpncmd', :timeout => 5})
#host = host
#port = options[:port].present? ? options[:port] : 443
#password = options[:password].present? ? options[:password] : ''
#vpncmd_bin_path = options[:vpncmd_bin_path].present? ? options[:vpncmd_bin_path] : '/usr/local/bin/vpncmd'
#timeout = options[:timeout].present? ? options[:timeout] : 5
#hubs = {}
#hub_cache_dirty = true
#hub_password_cache = {}
end
...
end
This test example:
it "should have a default constructor that takes no argument" do
s = SoftEther::Server.new()
expect(s.host).to be_equal('localhost')
expect(s.port).to be_equal('443')
expect(s.timeout).to be_equal(5)
expect(s.vpncmd_bin_path).to be_equal('/usr/local/bin/vpncmd')
expect(s.password).to be_equal('')
end
And rspec gives me the following result with Rails 4.2.6, jruby-9.0.5.0 and 3.4.4:
1) SoftEtherSever should have a default constructor that takes no argument
Failure/Error: expect(s.host).to be_equal('localhost')
expected `"localhost".equal?("localhost")` to return true, got false
# ./spec/poro/softether_spec.rb:19:in `block in (root)'
What did I do wrong?
equal? checks whether two instances are the same. But it returns false when two strings contains the same value but refers to different objects:
"foo".equals?("foo")
# => false
What you should really use is eq()
expect(s.host).to eq('localhost')
Just to add an edge case to Simone's answer:
If you were to freeze the strings in question, you would get the result you expected:
irb(main):001:0> 'test'.equal? 'test'
=> false
irb(main):002:0> 'test'.freeze.equal? 'test'.freeze
=> true
In Ruby 2.3, this can be done by adding
# frozen_string_literal: true
to the top of the Ruby file.
With that said, Simone is right. You should use the eq matcher unless you truly want to test that you are using the same exact object instance. Then using equal is in order.

Can I automatically re-run a method if a timeout error occurs?

We have an application that makes hundreds of API calls to external services. Sometimes some calls take too much time to respond.
I am using the rake_timeout gem to find time consuming process, so, Timeout::Error will be thrown whenever some request is taking too long to respond. I am rescuing this error and doing a retry on that method:
def new
#make_one_external_service_call = exteral_api_fetch1(params[:id])
#make_second_external_call = exteral_api_fetch1(#make_one_external_service_call)
#Below code will be repeated in every method
tries = 0
rescue Timeout::Error => e
tries += 1
retry if tries <= 3
logger.error e.message
end
This lets the method fully re-run it. This is very verbose and I am repeating it every time.
Is there any way to do this so that, if the Timeout:Error occurrs, it will retry that method automatically three times?
I have a little module for that:
# in lib/retryable.rb
module Retryable
# Options:
# * :tries - Number of tries to perform. Defaults to 1. If you want to retry once you must set tries to 2.
# * :on - The Exception on which a retry will be performed. Defaults to Exception, which retries on any Exception.
# * :log - The log level to log the exception. Defaults to nil.
#
# If you work with something like ActiveRecord#find_or_create_by_foo, remember to put that call in a uncached { } block. That
# forces subsequent finds to hit the database again.
#
# Example
# =======
# retryable(:tries => 2, :on => OpenURI::HTTPError) do
# # your code here
# end
#
def retryable(options = {}, &block)
opts = { :tries => 1, :on => Exception }.merge(options)
retry_exception, retries = opts[:on], opts[:tries]
begin
return yield
rescue retry_exception => e
logger.send(opts[:log], e.message) if opts[:log]
retry if (retries -= 1) > 0
end
yield
end
end
and than in your model:
extend Retryable
def new
retryable(:tries => 3, :on => Timeout::Error, :log =>:error) do
#make_one_external_service_call = exteral_api_fetch1(params[:id])
#make_second_external_call = exteral_api_fetch1(#make_one_external_service_call)
end
...
end
You could do something like this:
module Foo
def self.retryable(options = {})
retry_times = options[:times] || 10
try_exception = options[:on] || Exception
yield if block_given?
rescue *try_exception => e
retry if (retry_times -= 1) > 0
raise e
end
end
Foo.retryable(on: Timeout::Error, times: 5) do
# your code here
end
You can even pass multiple exceptions to "catch":
Foo.retryable(on: [Timeout::Error, StandardError]) do
# your code here
end
I think what you need is the retryable gem.
With the gem, you can write your method like below
def new
retryable :on => Timeout::Error, :times => 3 do
#make_one_external_service_call = exteral_api_fetch1(params[:id])
#make_second_external_call = exteral_api_fetch1(#make_one_external_service_call)
end
end
Please read the documentation for more information on how to use the gem and the other options it provides
you could just write a helper-method for that:
class TimeoutHelper
def call_and_retry(tries=3)
yield
rescue Timeout::Error => e
tries -= 1
retry if tries > 0
Rails.logger.error e.message
end
end
(completely untested) and call it via
TimeoutHelper.call_and_retry { [your code] }

How to test for a time span parameter?

I'm struggling with the following issue:
$ rails c
Loading development environment (Rails 3.2.1)
irb(main):001:0> 2.class
=> Fixnum
irb(main):002:0> Fixnum === 2
=> true
irb(main):003:0> 2.hours.class
=> Fixnum
irb(main):004:0> Fixnum === 2.hours
=> false
irb(main):005:0>
I'd like to test whether some specified parameter is a symbol or a time span. The way I thought would be the natural way in Ruby/Rails is:
case param
when :today then
# do this...
when Fixnum then
# do that...
else
raise ArgumentError ...
end
As far as I can tell from ActiveSupport's source code === is not overridden for Fixnum. So, what am I doing wrong?
You can see from the console, that 2.hours can't be Fixnum, because it's #inspect method is overridden. It just pretends to be Fixnum, but it is ActiveSupport::Duration (see the source)
> 2.hours
=> 7200 seconds
> 2.hours.to_i
=> 7200
Better check if it responds to some methods. If it responds to to_i, it is not a symbol.
> :today.respond_to?(:to_i)
=> false
> 2.hours.respond_to?(:to_i)
=> true

How to check for specific rescue clause for error handling in Rails 3.x?

I have the following code:
begin
site = RedirectFollower.new(url).resolve
rescue => e
puts e.to_s
return false
end
Which throws errors like:
the scheme http does not accept registry part: www.officedepot.com;
the scheme http does not accept registry part: ww2.google.com/something;
Operation timed out - connect(2)
How can I add in another rescue for all errors that are like the scheme http does not accept registry part?
Since I want to do something other than just printing the error and returning false in that case.
That depends.
I see the three exception descriptions are different. Are the Exception types different as well?
If So you could write your code like this:
begin
site = RedirectFollower.new(url).resolve
rescue ExceptionType1 => e
#do something with exception that throws 'scheme http does not...'
else
#do something with other exceptions
end
If the exception types are the same then you'll still have a single rescue block but will decide what to do based on a regular expression. Perhaps something like:
begin
site = RedirectFollower.new(url).resolve
rescue Exception => e
if e.message =~ /the scheme http does not accept registry part/
#do something with it
end
end
Does this help?
Check what is exception class in case of 'the scheme http does not accept registry part' ( you can do this by puts e.class). I assume that this will be other than 'Operation timed out - connect(2)'
then:
begin
.
rescue YourExceptionClass => e
.
rescue => e
.
end
Important Note: Rescue with wildcard, defaults to StandardError. It will not rescue every error.
For example, SignalException: SIGTERMwill not be rescued with rescue => error. You will have to specifically use rescue SignalException => e.

Resources