I have a statement that looks something like this
if not #user.verified? or #user.credit_card.expired?
# do stuff
end
The problem is the case where the #user does not yet have a credit card because expired? will raise an exception because of the nil value.
Is there a succinct way to catch the error inline without having to resort to extra if statements?
To avoid this exception you can use Rails 2.3/3 built-in method: try
if not #user.verified? or #user.credit_card.try(:expired?)
# do stuff
end
The andand gem allows for a guarded method invocation if and only if the object is not null.
Your code snippet would look like this:
if not #user.verified? or #user.credit_card.andand.expired?
# do stuff
end
You can install it with gem install andand and you're good to go.
Website: http://andand.rubyforge.org/
The code
#user.credit_card.expired?
Would be called a violation of the Law of Demeter. That law states you're allowed to call any method you like on #user, but not on #user.credit_card.
Related
I have a function that I want to test raises an exception on an input, but that exception also carries some more information than just a plain message, and I want to test that too. So I did something like this as seen in the rspec documentation:
it 'raises the correct exception' do
expect { my_call }.to raise_error do |error|
expect(error.some_field).to eq('some data')
end
end
This works great, however it runs afoul of the RSpec/MultipleExpectations cop:
RSpec/MultipleExpectations: Example has too many expectations [2/1]
From what I can tell it is impossible to use raise_error in block form like this without more than one expect, so what gives? Is there some way to somehow save the raised exception outside the example so I can spec it normally, without doing something horrible involving rescue in the specs? Or should I use a custom raise_custom_error matcher?
Rubocop by default I think enables the warning that you see which says to only have one expect in each it block. You can disable this in rubocop.yml by adding this:
# Disables "Too many expectations."
RSpec/MultipleExpectations:
Enabled: false
Or if you only want to disable it for your specific spec you can do so by adding comments like this, note you can disable any rubocop rule this way by using the rule name in comments:
# rubocop:disable RSpec/MultipleExpectations
it 'raises the correct exception' do
expect { my_call }.to raise_error do |error|
expect(error.some_field).to eq('some data')
end
end
# rubocop:enable RSpec/MultipleExpectations
it 'does something else' do
expect(true).to be true
end
For more rubocop syntax options see this answer
Sometimes Chef fails on the next code (this code not in block or any resource):
tmp = title.force_encoding("ISO-8859-1").encode("UTF-8")
title = tmp.encode('ISO8859-1').force_encoding('UTF-8')
error that I get:
NoMethodError
undefined method `force_encoding' for nil:NilClass
My question is how best practice to ignore any code error and to continue to run rest recipes, thanks
You didn't show where title comes from so they best I can say is to put your code in a ruby_block resource and use the ignore_failure property. You can also use normal Ruby rescue blocks for imperative code but be aware of how that interacts (or rather, doesn't) with resources, see https://coderanger.net/two-pass/ for some details on the loading process Chef uses.
In short, I want to raise an exception via a stubbed method, but only if the object that has the stubbed method has a particular state.
Mail::Message.any_instance.stub(:deliver) do
if to == "notarealemailaddress!##!##"
raise Exception, "SMTP Error"
else
return true
end
end
This doesn't work, because the context inside the stub block is: RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2.
How do I get access to the stubbed object?
using ruby 2, rspec 2.
The actual scenario is I have an app that sounds out thousands of emails in batches and I have code that catches SMTP exceptions, logs the batch, and proceeds. So I want to test sending several batches, where one of the batches in the middle throws an exception.
It looks like this is solved in the latest(currently alpha) version of Rspec v3:
https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643
it "passes the instance as the first arg of the implementation block" do
instance = klass.new
expect { |b|
klass.any_instance.should_receive(:bees).with(:sup, &b)
instance.bees(:sup)
}.to yield_with_args(instance, :sup)
end
I believe you specify the arguments using the with method, so in your case it would be something along the lines of:
Mail::Message.any_instance.stub(:deliver).with(to: "notarealemailaddress!##!##") do
raise Exception, "SMTP Error"
end
There's full documentation here:
https://www.relishapp.com/rspec/rspec-mocks/v/2-3/docs/method-stubs
Ok, here's how you can get this behavior fairly easily without upgrading:
class Rspec::Mocks::MessageExpectation
# pulling in behavior from rspec v3 that I really really really need, ok?
# when upgrading to v3, delete me!
def invoke_with_orig_object(parent_stub, *args, &block)
raise "Delete me. I was just stubbed to pull in behavior from RSpec v3 before it was production ready to fix a bug! But now I see you are using Rspec v3. See this commit: https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643" if RSpec::Version::STRING > "2.99.0.pre"
args.unshift(#method_double.object)
invoke_without_orig_object(parent_stub, *args, &block)
end
alias_method_chain :invoke, :orig_object
end
Drop that at the bottom of your spec file. You'll notice I even add a check to raise an error once RSpec is upgraded. boom!
What’s the best way to rescue exceptions from Net::HTTP?
Exceptions thrown are described in Ruby’s socket.c, like Errno::ETIMEDOUT, Errno::ECONNRESET, and Errno::ECONNREFUSED. The base class to all of these is SystemCallError, but it feels weird to write code like the following because SystemCallError seems so far removed from making an HTTP call:
begin
response = Net::HTTP.get_response(uri)
response.code == "200"
rescue SystemCallError
false
end
Is it just me? Is there a better way to handle this beyond fixing Net::HTTP to handle the Errno exceptions that would likely pop up and encapsulate them in a parent HttpRequestException?
I agree it is an absolute pain to handle all the potential exceptions. Look at this to see an example:
Working with Net::HTTP can be a pain. It's got about 40 different ways
to do any one task, and about 50 exceptions it can throw.
Just for the love of google, here's what I've got for the "right way"
of catching any exception that Net::HTTP can throw at you:
begin
response = Net::HTTP.post_form(...) # or any Net::HTTP call
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
...
end
Why not just rescue Exception => e? That's a bad habit to get into, as
it hides any problems in your actual code (like SyntaxErrors, whiny
nils, etc). Of course, this would all be much easier if the possible
errors had a common ancestor.
The issues I've been seeing in dealing with Net::HTTP have made me
wonder if it wouldn't be worth it to write a new HTTP client library.
One that was easier to mock out in tests, and didn't have all these
ugly little facets.
What I've done, and seen most people do, is move away from Net::HTTP and move to 3rd party HTTP libraries such as:
httparty and faraday
I experienced the same problem, and after a lot of research, I realized the best way to to handle all exceptions Net::HTTP methods would throw is to rescue from StandardError.
As pointed by Mike Lewis's answer, Tammer Saleh blog post proposes rescuing from a lot exceptions, but it is still flaw. There are some exceptions he does not rescue from, like Errno::EHOSTUNREACH, Errno::ECONNREFUSED, and possible some socket exceptions.
So, as I found out in tenderlove's translation of an old ruby-dev thread, the best solution is rescuing from StandardError, unfortunately:
begin
response = Net::HTTP.get_response(uri)
rescue StandardError
false
end
It is awful, but if you want your system does not break because of these other exceptions, use this approach.
Another approach is to aggregate all these exceptions in a constant, and then re-use this constant, e.g.:
ALL_NET_HTTP_ERRORS = [
Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
]
begin
your_http_logic()
rescue *ALL_NET_HTTP_ERRORS
…
end
It is far more maintainable and cleaner.
However, word of warning. I've copied the possible exceptions list from the aforementioned Tammer Saleh's blog post, and I know that his list is incomplete. For instance, Net::HTTP.get(URI("wow")) raises Errno::ECONNREFUSED which is not listed. Also, I wouldn't be surprised if the list should be modified for different Ruby versions.
For this reason, I recommend sticking to rescue StandardError in most cases. In order to avoid catching too much, move as much as possible outside the begin-rescue-end block, preferably leave only a call to one of the Net::HTTP methods.
Your intuition on this is right, for the most robust solution, I'd probably rescue each one individually (or in small groups) and take the appropriate action, like trying the connection again, or abandoning the request all together. I like to avoid using a very high-level/generic rescue because it might catch exceptions that I'm not prepared for or didn't expect.
What’s the best way to rescue exceptions from Net::HTTP?
Exceptions thrown are described in Ruby’s socket.c, like Errno::ETIMEDOUT, Errno::ECONNRESET, and Errno::ECONNREFUSED. The base class to all of these is SystemCallError, but it feels weird to write code like the following because SystemCallError seems so far removed from making an HTTP call:
begin
response = Net::HTTP.get_response(uri)
response.code == "200"
rescue SystemCallError
false
end
Is it just me? Is there a better way to handle this beyond fixing Net::HTTP to handle the Errno exceptions that would likely pop up and encapsulate them in a parent HttpRequestException?
I agree it is an absolute pain to handle all the potential exceptions. Look at this to see an example:
Working with Net::HTTP can be a pain. It's got about 40 different ways
to do any one task, and about 50 exceptions it can throw.
Just for the love of google, here's what I've got for the "right way"
of catching any exception that Net::HTTP can throw at you:
begin
response = Net::HTTP.post_form(...) # or any Net::HTTP call
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
...
end
Why not just rescue Exception => e? That's a bad habit to get into, as
it hides any problems in your actual code (like SyntaxErrors, whiny
nils, etc). Of course, this would all be much easier if the possible
errors had a common ancestor.
The issues I've been seeing in dealing with Net::HTTP have made me
wonder if it wouldn't be worth it to write a new HTTP client library.
One that was easier to mock out in tests, and didn't have all these
ugly little facets.
What I've done, and seen most people do, is move away from Net::HTTP and move to 3rd party HTTP libraries such as:
httparty and faraday
I experienced the same problem, and after a lot of research, I realized the best way to to handle all exceptions Net::HTTP methods would throw is to rescue from StandardError.
As pointed by Mike Lewis's answer, Tammer Saleh blog post proposes rescuing from a lot exceptions, but it is still flaw. There are some exceptions he does not rescue from, like Errno::EHOSTUNREACH, Errno::ECONNREFUSED, and possible some socket exceptions.
So, as I found out in tenderlove's translation of an old ruby-dev thread, the best solution is rescuing from StandardError, unfortunately:
begin
response = Net::HTTP.get_response(uri)
rescue StandardError
false
end
It is awful, but if you want your system does not break because of these other exceptions, use this approach.
Another approach is to aggregate all these exceptions in a constant, and then re-use this constant, e.g.:
ALL_NET_HTTP_ERRORS = [
Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
]
begin
your_http_logic()
rescue *ALL_NET_HTTP_ERRORS
…
end
It is far more maintainable and cleaner.
However, word of warning. I've copied the possible exceptions list from the aforementioned Tammer Saleh's blog post, and I know that his list is incomplete. For instance, Net::HTTP.get(URI("wow")) raises Errno::ECONNREFUSED which is not listed. Also, I wouldn't be surprised if the list should be modified for different Ruby versions.
For this reason, I recommend sticking to rescue StandardError in most cases. In order to avoid catching too much, move as much as possible outside the begin-rescue-end block, preferably leave only a call to one of the Net::HTTP methods.
Your intuition on this is right, for the most robust solution, I'd probably rescue each one individually (or in small groups) and take the appropriate action, like trying the connection again, or abandoning the request all together. I like to avoid using a very high-level/generic rescue because it might catch exceptions that I'm not prepared for or didn't expect.