Rails 3.0 Model without Validation does not get saved - ruby-on-rails

Looks like I messed up my code in a very subtle way... suddenly one of my models doesn't save anymore.
First thing I tried was to see if the same commands work in the Rails console, and to my surprise it showed the record(s) not being valid.
To narrow-down the problem, I commented-out all validations and tried again -- but it still did not work!
m = MyModel.find 123
=> record which looks OK to me
m.valid?
=> false
m.errors
=> #<OrderedHash {}> # An EMPTY hash??? Is that a Rails bug?
Has anybody seen something like this before?

Answering my own question.
Apparently one of my before_validation methods returned false - ugh - stupid mistake!!
Here's what the ActiveRecord documentation has to say on that topic:
before_validation* returning statements
If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false. If ActiveRecord::Validations#save! is called it will raise a ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
Yeah, it works as advertised!! :)
See also: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Related

before_validation not being called?

I'm having a weird issue for which I can't find a logical explanation.
I'm investigating a bug and put some logging in place (through Rollbar) so I can see the evolution some instances of one of my models.
Here is what I got:
class Connexion < ActiveRecord::Base
before_validation :save_info_in_rollbar
after_save :save_info_in_rollbar
def save_info_in_rollbar
Rollbar.log("debug", "Connexion save", :connexion_id => self.id, :connexion_details => self.attributes)
end
end
Now I am getting loads of data in rollbar (pretty much 2 rows for every time a connexion is created/updated). But the weird thing is the following: for some connexions (=> exactly the ones with faulty data which I am investigating), I am getting no data at all!
I don't get how it's possible for a connexion to be created and persisted to the DB, and not have any trace of the before_validation logging.
It looks like the callback is not called, but unless I'm mistaken, it's supposed to be the first one in the callback order => what could prevent it from being called?
EDIT >> Copy and paste from a reply, might be relevant:
There are 3 cases in which a connexion is created or updated, and thoses cases are :
.connexions.create()
connexion.attr = "value"; connexion.save!
connexion.update_attributes(attr: "value")
The only cases in which the callback won’t be run are:
Explicitly skipping validations (e.g. with save(validate: false))
Using an update method that skips Ruby-land (either partially or entirely, see each method’s linked docs) and just runs the SQL directly (e.g. update_columns, update_attribute, update_all).
But: I might be missing a case. Also, I’m assuming there isn’t a bug in ActiveRecord/ActiveModel causing this.
Sorry about the stupid question guys, the explanation was that we were having 2 apps working on the same database, and the modification was made by the other app (which of course was not sending the Rollbar updates).
Sometimes the toughest issues have the most simple answers haha
Firstly, you don't need self in instance methods, as the scope of the method is instance.
Secondly, you need to check, how are you saving the data to the database. You can skip callbacks in Rails: Rails 3 skip validations and callbacks
Thirdly, double check the data.

Rails create method not adding record to the table, but works for the IRB

I've looked everywhere for a similar error but couldn't find a solution, so in desperation I'm posting here.
My controller has this:
def add_upc
#upcs = Dvd.add_upc(params[:dogTag], params[:newUpc])
end
and in the Model we have:
def self.add_upc(dogTag, newUpc)
existingUpc = Dvd.find(dogTag).dvd_upc2title.find_by_upc(newUpc)
if existingUpc.nil?
createdUpc = Dvd.find(dogTag).dvd_upc2title.create(:upc => newUpc)
if createdUpc
upcs = createdUpc
else
upcs = 'Error: nothing was created'
end
end
end
I've set up a view page to see what's happening and I can see the object being created by createdUpc. I can also confirm that the parameters dogTag and newUpc are being passed correctly. Yet the record is not being added to the table.
Weirdly, this does work if I issue the Dvd.find(dogTag).dvd_upc2title.create(:upc => newUpc) command with the values substituted for the variables from the IRB.
Can't figure out why this is not working. I'm new to Rails so don't know what other error debugging I could use to figure out where the problem lies.
Ideas are welcome.
Thanks.
Edit:
Found the error thanks to RyanWilcox, it was the validation I had set up in the controller for UPC telling me that value already existed (even though UPCs are supposed to be unique. Is there a way to validate on a combination of 2 fields?
What I really like doing for situations like this ("why did this fail to save?") is using create! instead of create.
This will throw an exception on error, with the failed validation's text as the message of the exception. It makes problems like this obvious.

When an object returns false for `.valid?` , is there a way to find out why?

When an object returns false for .valid? , is there a way to find out why?
CardSignup.new(params[:card_signup]).valid?
=> false
Sounds great Rails..but why?
Sort of related, but here's an example. I'm doing this :
#card_signup.update_attributes("email"=>"asdfa#asfa.com")
=> false
But if I do this :
#card_signup.update_attribute("email", "asdfa#asfa.com")
=> true
Why would that work when I update the single attribute as opposed to update_attributes?
You should check the #card_signup.errors collection.
For your second question, the update_attribute method saves the record without validation procedure. On the opposite, update_attributes perform validations.
Single attribute updates don't go through the validation process.
If a constructed ActiveRecord object isn't valid, try accessing the errors method.
E.g.
c = CardSignup.new(params[:card_signup])
puts c.errors.to_a.inspect if !c.valid?
Yes, there's a method #card_signup.errors.

Why does this Ruby statement throw an exception? (Arrays/Bools)

I'm not a Ruby guy, I just play one on television. I have to modify someone's old Cron job to pull down some JSON and convert it into objects.
Here's the code
raw_json = Net::HTTP.get(URI.parse("url removed to protect the innocent"))
tags = ActiveSupport::JSON.decode(raw_json)
puts tags.count
tags.count will accurately trace as 5, but THAT LINE immediately causes a crash as follows:
5 #the accurate count!
rake aborted!
undefined method `count' for false:FalseClass
What is the dealio?
What is the contents of raw_json? What appears to be happening is that ActiveSupport::JSON#decode is returning false (hence undefined method 'count' for false:FalseClass). I think JSON#decode only returns false when given an empty string, which would mean HTTP#get is returning an empty string. Check on raw_json and see if it contains what you expect.
so I have no idea what is going on here, but JSON.decode should give you a hash, which doesn't have a count method. It does have a size method though
tags.size
if that doesn't work, try doing p tags, or puts tags.class.name to try and figure out what you are working with
Apparently tags is false , which may mean that your Net::HTTP.get failed (I guess your URL is wrong).
Try to print tags to see what it is. (I guess anyway, that you should use a valid URI)
The problem is that:
>> ActiveSupport::JSON::decode("")
=> false
>> ActiveSupport::JSON::decode("false")
=> false
This is a very strange behavior.

Check if record was just destroyed in rails

So there is
record.new_record?
To check if something is new
I need to check if something is on it's way out.
record = some_magic
record.destroy
record.is_destroyed? # => true
Something like that. I know destroying freezes the object, so frozen? sort of works, but is there something explicitly for this task?
Just do it:
record.destroyed?
Details are here ActiveRecord::Persistence
You can do this.
Record.exists?(record.id)
However that will do a hit on the database which isn't always necessary. The only other solution I know is to do a callback as theIV mentioned.
attr_accessor :destroyed
after_destroy :mark_as_destroyed
def mark_as_destroyed
self.destroyed = true
end
And then check record.destroyed.
This is coming very soon. In the latest Riding Rails post, it says this:
And finally, it's not necessarily
BugMash-related, but José Valim -
among dozens of other commits - added
model.destroyed?. This nifty method
will return true only if the instance
you're currently looking at has been
successfully destroyed.
So there you go. Coming soon!
destroying an object doesn't return anything other than a call to freeze (as far as I know) so I think frozen? is your best bet. Your other option is to rescue from ActiveRecord::RecordNotFound if you did something like record.reload.
I think Mike's tactic above could be best, or you could write a wrapper for these cases mentioned if you want to start 'making assumptions'.
Cheers.
While record.destroyed? works fine, and does return true or false, you can also DRY this up a little bit and create the if condition on the line you call destroy on in your controller.
record = Object.find(params[:id])
if record.destroy
... happy path
else
... sad path
end
Realize this post is a bit late in the game. But should anyone want to discuss this more, i'm game!
Side note: I also had an after_destroy validation on my model and while it worked, a separate method for something like this seems like overkill ;)
Without knowing more of the logic of your app, I think that frozen? is your best bet.
Failing that, you could certainly add a "destroyed" attribute to your models that you trigger in the callbacks and that could be checked against if you want a more precise solution.
The short answer is:
record.destroyed?
# or...
Record.exists?(record) # also very fast!
You'd think that record.destroyed? is better because it doesn't send an extra database request but actually Record.exists? is so extremely fast, that this typically isn't a reason to prefer one over the other.
Don't use the return value of record.destroy, which will always return a frozen instance of the record, whether it's deleted or not, from right before you tried to deleted it. See here: https://apidock.com/rails/v5.2.3/ActiveRecord/Persistence/destroy
# Assuming no issues when destroying the record...
x = record_one.destroy
x.destroyed? # Would return false! (even though the record no longer exists in the db)
Record.exists?(x) # Would correctly return false
# vs.
record_two.destroy
record_two.destroyed? # Would correctly return true
Record.exists?(record_two) # Would correctly return false
If you're in a controller, destroy! will throw a ActiveRecord::RecordNotDestroyed, which you can catch, based on before_destroy callbacks.
def destroy
render json: #record.destroy!
rescue ActiveRecord::RecordNotDestroyed
# #record will work fine down here, it still exists.
render json: { errors: ["Record not destroyed"] }
end

Resources