Rails 3.0.x - why the other model is beeing updated? - ruby-on-rails

I am trying to figure out things in code written by someone else. There are two models, with simple has_many relation (A->B).
Now the problem is, that while saving the "B" model, also the "A" model is updated in the database. Now, there is no callbacks of any sort, there is no special relation conditions (:autosave, etc), and also there is no Observers in the code.
The question is, what could be other things, which define this kind of behaviour?
Update: I am trying to debug the save process to track anything, but I am getting overwhelmend by the ammount of calls to internal active_record methods, so this approchach is getting useless.
Bonus question: How can I dump current model callbacks (in case, they were added in some manner which resistant to my grepping skills).

It could have :touch => true. That auto updates the association.

This turned out to be very complicated, internal application bug with Marshaling. In the result records fetched from cache were marked as not persisted, which forced ActiveRecord to create duplicate record while saving related objects.

Related

Rails ActiveRecord - Updating polymorphic id

I have an app where I have three models. I have leads and customers which are very similar (but different enough that they warranted their own models). Then I have a polymorphic model called "Interactions" which has an interactionable_id and an interactionable_type (which would, of course, be either 'customer' or 'lead').
I recently tried to build a method that would migrate an interaction from a lead to a customer (when a lead is upgraded to being a customer).
I wanted to keep my code lean, and in order to keep my method simple, I decided to simply update the attributes of the interaction so that the interactionable_id and interactionable_type would change to the customer's id and to "Customer" respectively.
I tried many various methods of doing this, and the only thing that I could get to update was the interactionable_type. The interactionable_id would not change, no matter what I tried. I tried putting in a static number (rather than customer.id). I tried update_attributes and I tried to update the attributes individually and then save it. Also, I did have :interactionable_id in the attr_accessible list.
I got this to work because I decided to approach the problem slightly differently, but I could not find any answers out there as to exactly why this was happening.
I was just wondering if anyone knew why this might be. Is there some sort of a lock on polymorphic association ids? Why couldn't I change the association?
This should work
interaction.interactionable = customer
interaction.save

What does User.destroy_all or User.delete_all do?

I am working on a project that has the following cucumber step:
Given /^no registered users$/ do
User.delete_all
end
As a new RoR user this looks a little dangerous even though I'd be testing on our development database because our User table has actual data. What is the line of code doing?
Thanks!
delete_all is from activerecord library not from FactoryGirl.
And the difference between these two is :
delete_all(conditions = nil) public
Deletes the records matching conditions without instantiating the records first, and hence not calling the destroy method nor invoking callbacks.
This is a single SQL DELETE statement that goes straight to the database, much more efficient than destroy_all.
Be careful with relations though, in particular :dependent rules defined on associations are not honored.
Returns the number of rows affected.
destroy_all(conditions = nil) public
Destroys the records matching conditions by instantiating each record and calling its destroy method.
Each object’s callbacks are executed (including :dependent association options and before_destroy/after_destroy Observer methods).
Returns the collection of objects that were destroyed; each will be frozen, to reflect that no changes should be made (since they can’t be persisted).
Note
Instantiation, callback execution, and deletion of each record can be time consuming when you’re removing many records at once. It generates at least one SQL DELETE query per record . If you want to delete many rows quickly, without concern for their associations or callbacks, use delete_all instead.
delete_all is not from FactoryGirl, it is an active record command and it deletes the users from your database. If you are running this from cucumber then it should run against your test database, not development.
A better alternative is destroy_all since that version will run any associated callbacks. For example, if users have posts, and you have a before_destroy callback to remove posts if users are deleted.
Here's a link to more info about delete_all
delete_all will forceably remove records from the corresponding table without activating any rails callbacks.
destroy_all will remove the records but also call the model callbacks
Based on your example, it's probably deleting all users in order to allow the next Cucumber step to register new users. The ActiveRecord::Base#delete_all method says, in part:
Deletes the records matching conditions without instantiating the
records first, and hence not calling the destroy method nor invoking
callbacks. This is a single SQL DELETE statement that goes straight to
the database, much more efficient than destroy_all.
There are probably better ways to write that test, but the intent is clearly to remove the user records as efficiently as possible.
As for it being dangerous, your tests should be running against the test database, not the development or production databases. Since it's possible to misconfigure your testing framework to use the wrong database, you could certainly add a step or conditional that tests if Rails.env.test? is true. That's a fairly small price to pay for peace of mind.

Duplicate Records created by find_or_create_by_

I have an ActiveRecord object, Corporation, and the only call in my project to create instances of this object looks like:
corp = Corporation.find_or_create_by_eveid_and_user_id(self.corporation_eveid, self.account.user_id)
Yet somehow, after my application has been running happily for a couple of days, there are duplicate records -- record where the eveid and user_id have the same values. How is this possible? Is there something I could be doing wrong in the way I update these records that would cause this problem?
I ended up added a unique, composite index to the table. That should solve the problem, but I don't understand how it's occurring.
This is Rails 3.0.7.
find_or_create does not perform any locking and makes no attempt to prevent race conditions. It's just a convenience method. If race conditions are a problem, you will need to either:
Use a transaction and roll back if you find somebody else has written just before you
(Better if you're actually expecting a race condition), perform pessimistic locking. This is where you SELECT from the table acquiring an exclusive lock first, then perform the write and clear the lock. In MySQL InnoDB tables, this is SELECT ... FOR UPDATE. If you have no reference point to lock on (i.e. no foreign key or anything that already exists in the database), then you'll have to stick with (1).
EDIT | If you can add a UNIQUE constraint at the schema level, I'd advise doing so too, if this is a genuine integrity concern.
Is this in your seeds file? Your best bet would be to write validations in your model to prevent the existence of a Corporation with the same eveid and user_id.
It seems to me that you seeded this information using find_or_create, which worked. But then maybe later in the day or another day someone created another one with the same information using your GUI. Validations would prevent this.
I have not tested this code, but something like this may work for you.
validates :eveid, :uniqueness => { :scope => :user_id }

What is the best way to override Rails ActiveRecord destroy behavior?

I have an application where I would like to override the behavior of destroy for many of my models. The use case is that users may have a legitimate need to delete a particular record, but actually deleting the row from the database would destroy referential integrity that affects other related models. For example, a user of the system may want to delete a customer with whom they no longer do business, but transactions with that customer need to be maintained.
It seems I have at least two options:
Duplicate data into the necessarily models effectively denormalizing my data model so that deleted records won't affect related data.
Override the "destroy" behavior of ActiveRecord to do something like set a flag indicating the user "deleted" the record and use this flag to hide the record.
Am I missing a better way?
Option 1 seems like a horrible idea to me, though I'd love to hear arguments to the contrary.
Option 2 seems somewhat Rails-ish but I'm wondering the best way to handle it. Should I create my own parent class that inherits from ActiveRecord::Base, override the destroy method there, then inherit from that class in the models where I want this behavior? Should I also override finder behavior so records marked as deleted aren't returned by default?
If I did this, how would I handle dynamic finders? What about named scopes?
If you're not actually interested in seeing those records again, but only care that the children still exist when the parent is destroyed, the job is simple: add :dependent => :nullify to the has_many call to set references to the parent to NULL automatically upon destruction, and teach the view to deal with that reference being missing. However, this only works if you're okay with not ever seeing the row again, i.e. viewing those transactions shows "[NO LONGER EXISTS]" under company name.
If you do want to see that data again, it sounds like what you want has nothing to do with actually destroying records, which means that you will never need to refer to them again. Hiding seems to be the way to go.
Instead of overriding destroy, since you're not actually destroying the record, it seems significantly simpler to put your behavior in a hide method that triggers a flag, as you suggested.
From there, whenever you want to list these records and only include visible records, one simple solution is to include a visible scope that doesn't include hidden records, and not include it when you want to find that specific, hidden record again. Another path is to use default_scope to hide hidden records and use Model.with_exclusive_scope { find(id) } to pull up a hidden record, but I'd recommend against it, since it could be a serious gotcha for an incoming developer, and fundamentally changes what Model.all returns to not at all reflect what the method call suggests.
I understand the desire to make the controllers look like they're doing things the Rails way, but when you're not really doing things the Rails way, it's best to be explicit about it, especially when it's really not that much of a pain to do so.
I wrote a plugin for this exact purpose, called paranoia. I "borrowed" the idea from acts_as_paranoid and basically re-wrote AAP using much less code.
When you call destroy on a record, it doesn't actually delete it. Instead, it will set a deleted_at column in your database to the current time.
The README on the GitHub page should be helpful for installation & usage. If it isn't, then let me know and I'll see if I can fix that for you.

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

Resources