I completely agree that it would a drastic change, very much not backwards compatible. So something like this should not be taken lightly. I just thought it was worth opening a dialogue because it seems to me that there is a big self inconsistency with the way AR deals with in memory objects. Attributes are modified only in memory until you call save, but associations are saved to the database immediately (there is no way to do an in memory change). That inconsistency cost our small company (3 developers) nearly a hundred hours of head scratching to get around (which is very expensive for a company of our size).
I propose that as part of Rails 6, we change the way association modifications are handle to have them only be in memory until save is called. On Thursday, July 7, 2016 at 2:25:07 PM UTC-7, Jason FB wrote: > > > It's an interesting idea, but my instinct says it's a huge change. We have > spent 12 years learning that associated relations are mutated immediately > (same behavior exists when you append to an association-- the db is touched > immediately), and a change like this could be massively costly across the > planet's ROR installations. > > Then again, I do find the black magic of AR sometimes difficult to reckon > with, especially when you save objects and their related objects also need > to get saved--- how can you know which order saves & callbacks happen? > (This is a HUGE problem in the Spree/Solidus codebases and has led to > hundreds of thousands of hours of wasted productivity due to the black > magic of AR) > > So, while I support all of this being cleaned up and better documented, I > think it's kind of like a Rails 6 thing in my mind since it seems like a > huge change. > > -Jason > > > On Jul 7, 2016, at 4:32 PM, Jeremy Mickelson <[email protected] > <javascript:>> wrote: > > Any thoughts on this proposal? > > On Thursday, February 18, 2016 at 11:26:18 AM UTC-8, Jeremy Mickelson > wrote: >> >> I agree complete that `delete` and `destroy` should remove objects from >> the database immediately. The situation that I'm talking about is not >> really related to the deletion of individual objects, its about how >> associations behave. >> >> If I have a model with a has many, and I alter the array that represents >> that association, it's not clear that some of those changes are in memory >> and some are immediately persisted to the database. I would expect the >> parent model to keep internal state representing either the objects to >> delete on save or a copy of the original array so it can diff at save time >> and detect the changes. >> >> For example: >> >> ``` ruby >> f = Filter.first >> f.destroy # happens immediately >> >> c = CommuncationSetting.first >> the_filters = c.filters # contains 3 items >> new_filters = the_filters.select { |f| /* only keep 2 of them */ } >> c.filters = new_filters # right now active record deletes the remove >> filter immediately >> c.save! # I propose that it should wait to delete the removed filter >> until here >> ``` >> >> >> On Thursday, February 18, 2016 at 12:16:08 PM UTC-7, Geoff Harcourt wrote: >>> >>> One way you could handle this would be to add a virtual attribute to >>> your model with `attr_accessor` called `marked_for_deletion`. You could >>> then use that flag as a temporary change to your model without deleting it, >>> and then delete those objects in the final DB transaction after the user >>> approves the proposed changes. An advantage of this approach is that if >>> your user abandons their approval that none of your changes have been >>> persisted to the database (the virtual attribute is lost as soon as the >>> model is no longer being referenced). >>> >>> Another approach you could use would be the “soft delete”, where deleted >>> models aren’t removed from the database, but are rather marked with a flag >>> or a deleted_at timestamp. If you adopted that strategy, you would avoid >>> having the records disappear from your database, and you could unwind the >>> action fairly easily. >>> >>> In a non-soft delete scenario, I think calling `#delete` or `#destroy` >>> on a model and not having it be deleted immediately would be unexpected >>> behavior. >>> >>> On February 18, 2016 at 2:01:55 PM, Jeremy Mickelson ( >>> [email protected]) wrote: >>> >>> In our specific project we have an object called CommunicationSetting >>> that defines an automated email that a client is setting up. That setting >>> has many different child objects, Filters for example, which would >>> exclude or include people from the recipient list. In this example we would >>> like the client to be able to test how changes to their filters will affect >>> the recipient list. So we would like to take their proposed changes (which >>> might include additions, modification, and deletions), modify the objects >>> in memory, get the list and return it to the UI so the user can review it. >>> If the user likes the changes, they can hit save to persist the object to >>> the database, or if they don’t like it the can abandon their changes and >>> leave the objects in the database as it. >>> >>> If deletions could be deferred until save time, than running these types >>> of experiments become very trivial. The transaction workaround is >>> plausible, but the whole point of having ActiveRecord objects in memory is >>> the ability to modify them without persisting. Right now the behavior is >>> inconsistent. Additions and modification to objects in a relation are >>> performed in memory only, while deletions are immediately persisted to the >>> database. I think that the inconsistency in behavior is the biggest problem. >>> >>> If I had something like this: >>> >>> c = CommunicationSetting.find(1) >>> the_filters = c.filters # => [#<Filter:0x007f9abe7c3408>, >>> #<Filter:0x007f9abe7b2b30>] >>> >>> Then I changed the_filters modifying one, removing one, and adding a >>> new one, then executed >>> >>> c.filters = the_filters >>> >>> The modification and addition would be in memory only, while the >>> deletion is persisted to the database immediately. This seems very >>> inconsistent and counter intuitive. >>> >>> On Wednesday, February 17, 2016 at 2:04:32 PM UTC-7, Nicholas >>> Firth-McCoy wrote: >>> >>> Could you run your code within a transaction and call the existing >>>> `destroy` method, and then rollback the transaction in the case that you >>>> don't want the deletion to persist? >>>> >>>> Can you share some real world examples showing why you'd need to be >>>> able to soft delete the associated records? There might be other, better >>>> workarounds. >>>> >>>> My guess is that this would be a complicated feature to add, but I'm >>>> not too familiar with the parts of ActiveRecord that this would touch. >>>> >>> >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Ruby on Rails: Core" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to [email protected]. >>> To post to this group, send email to [email protected]. >>> Visit this group at https://groups.google.com/group/rubyonrails-core. >>> For more options, visit https://groups.google.com/d/optout. >>> >>> > -- > You received this message because you are subscribed to the Google Groups > "Ruby on Rails: Core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected] <javascript:>. > To post to this group, send email to [email protected] > <javascript:>. > Visit this group at https://groups.google.com/group/rubyonrails-core. > For more options, visit https://groups.google.com/d/optout. > > > ---- > > Jason Fleetwood-Boldt > [email protected] <javascript:> > http://www.jasonfleetwoodboldt.com/writing > > If you'd like to reply by encrypted email you can find my public key on > jasonfleetwoodboldt.com (more about setting GPG: https://gpgtools.org) > > -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/rubyonrails-core. For more options, visit https://groups.google.com/d/optout.
