How to add errors to Rails model? - ruby-on-rails

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

Related

raise ActiveRecord::Rollback in after_save doesn't rollback the save

Inside the after_save method, I have:
def sync
status = Timeout::timeout(1) {
sleep(2)
}
rescue StandardError => e
puts "inside rescue"
errors[:base] << e.message.to_s
ApsLogger.fatal(e)
raise ActiveRecord::Rollback
# raise ActiveRecord::RecordInvalid.new(self)
end
I see inside rescue gets printed out, but the record is still being modified in the DB.
I tried with raise ActiveRecord::RecordInvalid.new(self), still doesn't work.
I expect to see the record not modified in the DB, and an error message show up in the UI.

DRY code in rescue => e in various model files in 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.

RSpec having trouble stubbing method called with inline rescue

I'm trying test a method on a controller:
def a_method(list)
#users = []
list.each do |x|
if user=User.find(x) rescue nil
#users << user
end
end
end
In my Rspec example I have :
it "should do something" do
User.stub :find => 'user'
controller.a_method([1,2,3,4])
assigns[:users].should == ['user','user','user','user']
end
Problem:
it always rescues the find method user=User.find(x) rescue nil, even though I've stubbed it out.
If I remove the rescue nil it works fine.
Any ideas?
The if conditional doesn't accept a statement modifier such as rescue half way into the statement.
You can put the rescue at the end of the complete if statement as in:
if user=User.find(x)
#users << user
end rescue nil
why don't you use find_by_id instead of find?
find_by_id returns nil if the id does not exists instead of throwing the exception, it's almost the same you are doing but a little faster I guess and cleaner to read it

validation for models in ruby on rails

I have a model:
class HelloRails < ActiveRecord::Base
attr_accessible :filename, :filevalidate
include In4systemsModelCommon
validates :filename, presence: true
def update
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
end
end
In the view I have a text_field called as :filename. When I click the submit button it calls this update method in model to call the stored proc to execute. Now validations are not working.
I dont want to accept nil for filename. How can I do this?
It doesn't validate because you are executing sql directly:
ActiveRecord::Base.connection.execute(sql)
Validations are only run when you use the "normal" ActiveRecord methods like save and update_attributes. To run validations manually you can call valid? on your object.
Model:
def update
return false unless self.valid? # validation failed: return false
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
true
end
Then in your controller you have to check wether #object.update returns true or false and display errors in your view if necessary.
Controller:
# This is just an example. I don't know your code.
def update
#object = HelloRails.find(params[:id])
if #object.update
redirect_to somewhere
else
render :action => 'edit'
end
end

Making an object that evaluates to false in an if statement

So I want to do this because I think it is the most idiomatic way to do errors.
For example:
User < ActiveRecord::Base
def add_email?
...
#in the case that it does have an error
MyErrorObjectThatEvaluatesToFalse.new("The email is already taken")
end
def is_valid_user?
...
MyErrorObjectThatEvaluatesToFalse.new("Username is not set")
...
end
end
...
SomeController
...
if error = user.add_email?
render_error_msg(error.message) and return
elsif error = user.is_valid_user?
render_error_msg(error.message) and return
end
...
end
I've tried one of the solutions below, but it doesn't have the functionality that I would like:
class A
def ==(comp)
false
end
end
a = A.new
if a
puts "'a' evaluated to true"
else
puts "'a' evaluated to false"
end
#=> 'a' evaluated to true
Is there a way to do something like this or has some else found a way to handle errors that is better than the current rails way of indirectly getting the message with a combination of user.valid? and user.errors?
Thanks!
I would not recommend this as a method of validation, however to define a class that returns false on a comparator:
class A
def ==(comp)
false
end
end
A.new == "a" #false
A.new == true #false
A.new == false #false
A.new == nil #false
I would recommend using rails' built in validations.
class User < ActiveRecord::Base
validates :username, :presence => true
end
user = User.new
user.errors #["username must not be blank"]

Resources