DRY code in rescue => e in various model files in Rails - ruby-on-rails

In sidekiq_batch.rb,
def sidekiq_status
begin
something
rescue => e
Rails.logger.error("\nRESCUENIL in sidekiq_status #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
In checkin.rb,
def attached_receipt_image
begin
something else
rescue => e
Rails.logger.error("\nRESCUENIL in attached_receipt_image #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
In barcode.rb,
def receipt_check?
begin
some code
rescue => e
Rails.logger.error("\nRESCUENIL in receipt_check #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
Need to DRY up the code. How can I write a common error-logging routine for all of these methods in my models?

You can write an abstraction for that, but you cannot return from there. You can write:
def with_log(name)
begin
yield
rescue => exc
Rails.logger.error("\nRESCUENIL in #{name} #{exc.class} #{exc.message} in #{exc.backtrace}")
false
end
end
with_log(:sidekiq_status) do
something
true # not needed if something returns a boolean with the success status
end or return
This true can also be moved to with_log, it depends on how you plan to use it.

Related

Rails - Rubocop - Begin + Rescue syntax

I have the following code:
def payload
begin
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
end
From brief reading of a few blogs I'm supposed to be using begin rescue and end to handle the error as I'm doing above, however I'm getting a redundant 'begin' rubocop warning.
Is begin only used when specifying a bit of code that may cause an error within a larger block? And is it therefore redundant here?
Thanks in advance
EDIT: And if I don't need it, is it written as
def payload
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
?
Do this when the begin would be the first thing in your method
def payload
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
Method bodies, block bodies, and lambda bodies are implicit exception blocks. You don't need to wrap the entire code of a method body, block body, or lambda body in a begin / rescue / else / ensure / end exception block, since it is already implicitly one. So, whenever you have something like
def foo
begin
rescue
end
end
or
foo do
begin
rescue
end
end
or
-> do
begin
rescue
end
end
you can replace it with just
def foo
rescue
end
or the equivalent for blocks and lambdas.

How to forward a block to a method that generates methods

In my current project, I am having the following repetitive pattern in some classes:
class MyClass
def method1(pars1, ...)
preamble
# implementation method1
rescue StandardError => e
recovery
end
def method2(pars2, ...)
preamble
# implementation method2
rescue StandardError => e
recovery
end
# more methods with the same pattern
end
So, I have been thinking about how to dry that repetive pattern. My goal is to have something like this:
class MyClass
define_operation :method1, pars1, ... do
# implementation method1
end
define_operation :method2, pars2, ... do
# implementation method2
end
# more methods with the same pattern but generated with define_wrapper_method
end
I have tried to implement a kind of metagenerator but I have had problems for forwarding the block that would receive the generator. This is more or less what I have tried:
def define_operation(op_name, *pars, &block)
define_method(op_name.to_s) do |*pars|
preamble
yield # how can I do here for getting the block? <-----
rescue StandardError => e
recovery
end
end
Unfortunately, I cannot find a way for forwarding block to the define_method method. Also, it is very possible that the parameters, whose number is variable, are being passed to define_method in a wrong way.
I would appreciate any clue, help, suggestion.
You do not need metaprogramming to achieve this. Just add a new method that wraps the common logic like below:
class MyClass
def method1(param1)
run_with_recovery(param1) do |param1|
# implementation method1
end
end
def method2(param1, param2)
run_with_recovery(param1, param2) do |param1, param2|
# implementation method2
end
end
private
def run_with_recovery(*params)
preamble
yield(*params)
rescue StandardError => e
recovery
end
end
Test it here: http://rubyfiddle.com/riddles/4b6e2
If you really wanted to do metaprogramming, this will work:
class MyClass
def self.define_operation(op_name)
define_method(op_name.to_s) do |*args|
begin
puts "preamble"
yield(args)
rescue StandardError => e
puts "recovery"
end
end
end
define_operation :method1 do |param1|
puts param1
end
define_operation :method2 do |param1, param2|
puts param1
puts param2
end
end
MyClass.new.method1("hi")
MyClass.new.method2("hi", "there")
Test this here: http://rubyfiddle.com/riddles/81b9d/2
If I understand correctly you are looking for something like:
class Operation
def self.op(name,&block)
define_method(name) do |*args|
op_wrap(block.arity,*args,&block)
end
end
def op_wrap(arity=0,*args)
if arity == args.size || (arrity < 0 && args.size >= arity.abs - 1)
begin
preamble
yield *args
rescue StandardError => e
recovery
end
else
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected #{arity < 0 ? (arity.abs - 1).to_s.+('+') : arity })"
end
end
def preamble
puts __method__
end
def recovery
puts __method__
end
end
So your usage would be
class MyClass < Operation
op :thing1 do |a,b,c|
puts "I got #{a},#{b},#{c}"
end
op :thing2 do |a,b|
raise StandardError
end
def thing3
thing1(1,2,3)
end
end
Additionally this offers you both options presented as you could still do
def thing4(m1,m2,m3)
#m1 = m1
op_wrap(1,'inside_wrapper') do |str|
# no need to yield because the m1,m2,m3 are in scope
# but you could yield other arguments
puts "#{str} in #{__method__}"
end
end
Allowing you to pre-process arguments and decide what to yield to the block
Examples
m = MyClass.new
m.thing1(4,5,6)
# preamble
# I got 4,5,6
#=> nil
m.thing2('A','B')
# preamble
# recovery
#=> nil
m.thing3
# preamble
# I got 1,2,3
#=> nil
m.thing1(12)
#=> #<ArgumentError: wrong number of arguments (given 1, expected 3)>

Basic Ruby. Why is this method returning nil?

Hi!
I am expecting #<PrettyThing:0x0055a958175348 #success="anything here">
but I am getting 'anything here' instead. Any idea why?
class Thing
attr_accessor :success
def execute
self.success = execute!
rescue
self.success = false
ensure
self
end
end
class PrettyThing < Thing
def execute!
'anything here'
end
end
p PrettyThing.new.execute # => 'anything here'
Ensure is a tricky thing. Normally, it doesn't return a value, and instead the return value from the last executed line of the main or rescue block is returned, unless there was an uncaught error, then the error is returned. But, if you explicitly return, then you will get the return value. This is a bit nonstandard and confusing though, because the intent of the ensure clause is for silent cleanup. It may be better to move your return value outside your begin/rescue block.
Try:
class Thing
attr_accessor :success
def execute
self.success = execute!
self
rescue
self.success = false
end
end
class PrettyThing < Thing
def execute!
'anything here'
end
end
p PrettyThing.new.execute # => <PrettyThing:0x0000000379ea48 #success="anything here">
The way you have it written, execute is returning the assignment result of self.success = execute!. By adding self, you return the instance of PrettyThing.
This is handy if you want to chain methods, like:
class Thing
attr_accessor :success
def execute
self.success = execute!
self
rescue
self.success = false
end
def foo
puts 'foo'
end
end
class PrettyThing < Thing
def execute!
'anything here'
end
end
p PrettyThing.new.execute.foo # => foo
Given your comment, I think I'd probably do it something more like:
class Thing
attr_accessor :success
alias success? success
def foo
puts 'foo'
end
end
class PrettyThing < Thing
def execute
#success = everything_worked
self
end
private
def everything_worked
# your logic goes here
# return true if all is good
# return false or nil if all is not good
true
end
end
pretty_thing = PrettyThing.new.execute
p pretty_thing.success? # => true
If everything_worked returns false or nil, then pretty_thing.success? will also return false or nil.

Ruby: Skip element in loop if an exception is raised

I have the following method:
def fetch_something
#fetch_something ||= array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError
next
end
results
end
end
To its purpose: It fetches a value for a given name that can raise an error, in which case it will ignore the name and try the next one.
While this works fine I get an error from Rubocop that says:
Lint/NextWithoutAccumulator: Use next with an accumulator argument in
a reduce.
Googling that error leads me to http://www.rubydoc.info/gems/rubocop/0.36.0/RuboCop/Cop/Lint/NextWithoutAccumulator where it says to not omit the accumulator, which would result in a method looking like this:
def fetch_something
#fetch_something ||= array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError
next(name)
end
results
end
end
The problem is, that this change breaks the otherwise working method. Any ideas on how to tackle that?
Update: Demonstrational example:
array_of_names = ['name1','name2','name3']
def fetch_value!(name)
# some code that raises an error if the name doesn't correspond to anything
end
fetch_something
# => {'name1' => {key1: 'value1', ...}, 'name3' => {key3: 'value3', ...}}
# 'name2' is missing since it wasn't found durcing the lookup
Using your example code, it seems that next(results) does fix the issue for me. I've used some test code that throws a KeyError instead of NoMethodError or RuntimeError, but the idea is still the same:
#array_of_names = ['name1','name2','name3']
def fetch_value!(name)
{'name1' => 'n1', 'name3' => 'n3'}.fetch(name)
end
def fetch_something
#fetch_something ||= #array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError, KeyError
next(results)
end
results
end
end
p fetch_something
This code outputs:
{"name1"=>"n1", "name3"=>"n3"}
Also, I agree with #Алексей Кузнецов that each_with_object is probably the way to go whenever your block code mutates the data structure you're trying to build. So my preferred implementation of fetch_something might be more like this:
def fetch_something
#fetch_something ||= #array_of_names.each_with_object({}) do |name, results|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError, KeyError
# error handling
end
end
end
Note that in my example the begin/end block is outside the assignment to results[name] in contrast to #Алексей Кузнецов's example which will assign nil to the hash every time an exception occurs.
Just use each_with_object
def fetch_something
#fetch_something ||= array_of_names.each_with_object({}) do |name, results|
results[name] ||= begin
fetch_value!(name)
rescue NoMethodError, RuntimeError
end
end
end

How to add errors to Rails model?

I tried to add an exception in the before_save method in a rails model, but in the view, no error message exists.
Model:
before_save do
doing_some_stuff
begin
File.open('some_file', 'w+') do |file|
if file.write(file_content)
return true
else
return false
end
end
rescue => e
self.errors.add(:base, e.message)
return false
end
View:
<%= #model.errors.any? %>
This is always false.
How do I add error messages to the model?
EDIT:
The problem was, I had a redirect after the update_attribute function instead of rendering the action again. Thx for help.
You should be performing this on validation, not on before_save. By the time you get to the before_save callback, the record is assumed to be valid.
validate do
doing_some_stuff
begin
File.open(some_file, 'w+') do |file|
if !file.write(file_content)
self.errors.add(:base, "Unable to write #{some_file}")
end
end
rescue => e
self.errors.add(:base, e.message)
end
end

Resources