How to debug silent database errors (such as "save") in Rails? - ruby-on-rails

How can I get further information on errors that occur silently in Rails, such as #object.save?

Add bang so that an error is raised when validation fails.
#article.save!
# ActiveRecord::RecordInvalid: Validation failed: Title can't be blank...
Always use this method in preference to save if you don't expect validation to fail.

As per this post, something like the following works:
logger.debug #item.errors.full_messages

Sometimes AR silently fails for reasons other than validation errors. A couple of other things to check are:
AR callbacks that do not return true (eg before_save)
Invalid parent/ child records
#item.valid?, #item.errors.full_messages
#item.changes, #item.changed?
I have also included user456584's comment on checking for validation errors. And as Semyon said, #item.save! will at least raise an Exception even if it isn't particularly helpful.

Related

Do rails custom validations halt the save?

Specifically http://guides.rubyonrails.org/active_record_validations.html#custom-methods
I feel like this isn't answered/documented properly. If you look at the example code -> all it does is call errors.add which according to this http://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-add
doesn't do much other except add the message to the errors.
Upon testing, it does halt the save but I will leave this here for people to find.
An object is saved to the database only if it is valid. Using 'errors.add(:attribute_name, error_message)' associates an error with the object making it invalid, resulting in the object not being saved.
If an error exists on a field in an object (we'll call it "x"), then x.valid? is false. A failure of this validity check DOES prevent the object from saving. It will either return false if you call x.save (or create(x_params)), or raise an error if you call x.save! (or create!(x_params)). Raising errors is especially useful in the context of creating multiple record in a transaction, as that will break you out of the transaction and into your rescue block (assuming you allow for such).

Rails Active Record - How to do validation which calls method rather than throws error

Active Record validations throw an error when they fail. What I have in a model is
validate_format_of :field_which_cannot_have_spaces, :with => /^[^\s]+$/, :message => "Some error message"
What I want instead, is for a string replacement to substitute spaces for underscores (snake_case).
The advantages of using validation for me, are that it runs every time the field is changed unless save(validate: false), and that I don't need to repeat the replacement in the create and update controller methods.
Front end javascript solutions won't help if the user hacks the form... a rails solution is needed!
It sounds like you want a callback rather than a validation. This can run each time your object is modified.
So, to remove spaces from your field before the object is saved you can do:
before_save :remove_spaces_from_x
def remove_spaces_from_x
self.field_which_cannot_have_spaces.gsub!("\s","_")
end
Note also that validation do not always raise an error when they fail. If you use save! or create! then an error is raised but if you use the equivalent save or create then no error is raised, false is returned and the object's errors are populated with details of the validation failure.
Co-worker just told me to do the following in the model:
def field_which_cannot_have_spaces=(input_from_form)
super(input_from_form.gsub("\s","_"))
end
This will change the value as it is set.
"Validations are for informing the client there is a problem, and shouldn't be doing something other than throwing an error."
Hope this helps someone else...

Ruby get backtrace without an exception

I have a Ruby on Rails application where a validation is failing for one of my models. There are different entry points into the code base for where this validation could fail. I'm interested in figuring out where it's coming from. Since it is a simple validation method, there aren't any exceptions involved, I just return false from the method and the save fails.
Is it currently possible to also log the backtrace to figure out what service/route this validation originated from so I can see what caused the state to change for this object in order for it to fail validation?
You could try caller():
def foo2
puts caller
end
def foo
foo2 #line5
end
foo #line 7
Result:
test.rb:5:in `foo'
test.rb:7:in `<main>'
I'm not sure of a clever way to do it, but this will get the job done. You could wrap it in a nice little function even. I'm not sure if throwing exceptions and rescuing them will impact performance, but you probably wouldn't want to do something like this in production.
begin
throw
rescue
puts $!.backtrace
end

When / why would I ever want to raise an exception on a method?

I've noticed that some methods in rails use the ! operator to raise an exception if invalid. For example, User.create!
Why and when would I want to use something like this?
Thanks.
I could want exceptions for various reasons
I might want to make sure that the method succeeds but I don't want to do any actual error handling here. But I might be fine with the request blowing up (e.g producing an HTTP 500) if something fails
I might want to have some error handling elsewhere, e.g some way up in the call chain. Generally, it's way more verbose and nasty to pull some error state with you a long way towards error handling. Structured error handling, i.e., begin ... rescue ... end make that clearer.
Exceptions can have additional data attached (e.g. an error message)
If you have different error types, it often much clearer to represent those with different exception classes instead of some magic id values (as is common in C)
There are good reasons to not use exceptions but status flags when the error states are few in number and are fully handled directly after the call. But every technique has its place and time.
And btw, the save method internally calls save!, handles the potential exception and returns false if there is any. See the code for the implementation.
ActiveRecord will roll back a transaction if an exception is thrown while a transaction is active.
So the methods that throw exceptions are nice to ensure the database doesn't commit a transaction when an exceptional condition occurs. When you can "handle" the problem yourself -- or, if it isn't actually a problem -- then you can use the variant without the !, check the return value for error conditions, and handle them yourself.
For something specific like User.create:
You might use the User.create method to determine if a user-supplied username is not yet picked and provide a prompt for the user to select another name, if it is already in use.
You might use the User.create! method when finally submitting the request, so that your integrity checks can fail the create and rollback the transaction in case the user attempts bypassing your friendly name check.

Railstutorial.org Validating unique email

In section 6.2.4 of Ruby on Rails 3 Tutorial, Michael Hartl describes a caveat about checking uniqueness for email addresses: If two identical requests come close in time, request A can pass validation, then B pass validation, then A get saved, then B get saved, and you get two records with the same value. Each was valid at the time it was checked.
My question is not about the solution (put a unique constraint on the database so B's save won't work). It's about writing a test to prove the solution works. I tried writing my own, but whatever I came up with only turned out to mimic the regular, simple uniqueness tests.
Being completely new to rspec, my naive approach was to just write the scenario:
it 'should reject duplicate email addresses with caveat' do
A = User.new( #attr )
A.should be_valid # always valid
B = User.new( #attr )
B.should be_valid # always valid, as expected
A.save.should == true # save always works fine
B.save.should == false # this is the problem case
# B.should_not be_valid # ...same results as "save.should"
end
but this test passes/fails in exactly the same cases as the regular uniqueness test; the B.save.should == false passes when my code is written so that the regular uniqueness test passes and fails when the regular test fails.
So my question is "how can I write an rspec test that will verify I'm solving this problem?" If the answer turns out to be "it's complicated", is there a different Rails testing framework I should look at?
It's complicated. Race conditions are so nasty precisely because they are so difficult to reproduce. Internally, save goes something like this:
Validate.
Write to database.
So, to reproduce the timing problem, you'd need to arrange the two save calls to overlap like this (pseudo-Rails):
a.validate # first half of a.save
b.validate # first half of b.save
a.write_to_db # second half of a.save
b.write_to_db # second half of b.save
but you can't open up the save method and fiddle with its internals quite so easily.
But (and this is a big but), you can skip the validations entirely:
Note that save also has the ability to skip validations if passed :validate => false as argument. This technique should be used with caution.
So if you use
b.save(:validate => false)
you should get just the "write to database" half of b's save and send your data to the database without validation. That should trigger a constraint violation in the database and I'm pretty sure that will raise an ActiveRecord::StatementInvalid exception so I think you'll need to look for an exception rather than just a false return from save:
b.save(:validate => false).should raise_exception(ActiveRecord::StatementInvalid)
You can tighten that up to look for the specific exception message as well. I don't have anything handy to test this test with so try it out in the Rails console and adjust your spec appropriately.

Resources