I have:
class Parent < ActiveRecord::Base
has_many :things
before_save :update_something
private
def update_something
self.update_column(:something, "something")
end
end
And
class Thing < ActiveRecord::Base
belongs_to :parent, autosave: true
end
I expect that when I save an instance of Thing that it's Parent should also be saved. I also expect that instance of Parent to have it's before_save callback called. This doesn't seem to be the case.
Any idea why this doesn't work and how I might remedy it?
Referring to the docs
If you set the :autosave option to true, Rails will save any loaded
members and destroy members that are marked for destruction whenever
you save the parent object.
I suggest you creating new after_save callback for Thing to update parent if you want to go Rails way.
But the OO way would be to create class that handles saving the object, such as:
class ThingUpdater
def initialize(thing)
#thing = thing
end
def call(params)
#thing.update_attributes(params)
#thing.parent.update_something
end
end
Thanks to this you will avoid callback hell - take also a look here
Related
I have these 2 models as follow
class Application < ActiveRecord::Base
has_many :commitments, class_name: "Commitment", \
:source => :application, dependent: :destroy
accepts_nested_attributes_for :commitments
after_create: update_case_code
end
class Commitment < ActiveRecord::Base
belongs_to :application
after_create: send_notification
def send_notification
ap self.application.case_code
end
end
class ApplicationsController < ApplicationController
def create
#application = Application.new(params)
#application.save
end
end
In my application_controller whenever i create a new Application record,a new record is also created in the Commitment and it tries to get the case_code from the application record but the after_create method of the application model hasnt been executed yet.
Is there any way to optimize this code so that it works properly?
Probably there is. Probably you can also use another callback on the application model which happens before, there are plenty of them. See Active Record Callbacks
However this is exactly the case, which other people call rails callback hell
The best practice here would be just creating a form object, which creates the data in the order you need and remove the callbacks
class ApplicationCommitmentForm
include ActiveModel::Model
attr_accessor ...
def submit
a = Application.create ..
a.update_case_code
a.commitments.create ...
end
end
See ActiveModel Form Objects
Btw you could also wrap the submit code into a transactions ensuring that either all records are created or in case of any errors nothing at all.
I have code like the following:
class MyModel < ActiveRecord::Base
has_many :associated_records
accepts_nested_attributes_for :associated_records
after_save :send_notification, if: :relevant_data_changed?
def relevant_data_changed?
return self.some_column_changed? || self.associated_records.changed?
end
def send_notification
# Do stuff
end
end
I know I can check whether a column that is directly on the model changed (as I did in the example), and I think you can similarly even check whether a single nested object changed if there is a has_one relationship with that object (via self.nested_model.changed? I believe), but I can't figure out how to check whether an array of objects has changed, such as associated_records in my example.
EDIT: For the record, I did try the suggested solution from here: Rails: if has_many relationship changed. But it does not work in the case where objects were only added or removed rather than actually changed.
Does anyone know of a way I can do this? Thanks.
Something like this should work:
class MyModel < ActiveRecord::Base
has_many :associated_records, after_save :force_save_associated_records
def force_save_associated_records
associated_records.map{|x| x.save! if x.changed?}
end
end
So after a bit more digging, it turns out that the proper way to do what I'm trying to do is with the after_add and after_remove callbacks.
How can I validate that has_many relations are not changed
In my example, it would be something like:
has_many :associated_records, after_add: :send_notification
# This gets called for every new record added, even if multiple are
# added at once
def send_notification(new_record)
# Do stuff with the new_record
end
I'm developing a Rails 3.2.13 application and I have two models:
class Invoice < ActiveRecord::Base
has_many :client_invoices, dependent: :nullify
...
end
class ClientInvoice < ActiveRecord::Base
belongs_to :invoice
...
end
I was wondering if there is a way to make the ClientInvoices to know when their parent Invoice is been destroyed and call a private method to update their status.
I've tried to perform this in the after_destroy callback of the Invoice, by looping the collection and changing the status of each ClientInvoice, but the collection is already empty there.
What is the best way to achieve this?
Thank you very much in advance!
before_destroy will work.
before_destroy :update_client_invoice_statue
private
def update_client_invoice_statue
client_invoices.each do |invoice|
#... code to update the status of record
end
end
Note : This before_destroy method should return true for proceeding the destruction of the object.
I have the following model:
class PhoneNumber < ActiveRecord::Base
has_many :personal_phone_numbers, :dependent => :destroy
has_many :people, :through => :personal_phone_numbers
end
I want to set up an observer to run an action in a delayed_job queue, which works for the most part, but with one exception. I want the before_destroy watcher to grab the people associated with the phone number, before it is destroyed, and it is on those people that the delayed job actually works.
The problem is, when a phone number is destroyed, it destroys the :personal_phone_numbers record first, and then triggers the observer when it attempts to destroy the phone number. At that point, it's too late.
Is there any way to observe the destroy action before dependent records are deleted?
While this isn't ideal, you could remove the :dependent => :destroy from the personal_phone_numbers relationship, and delete them manually in the observer after operating on them.
However, I think that this issue might be showing you a code smell. Why are you operating on people in an observer on phone number. It sounds like that logic is better handled in the join model.
Use alias_method to intercept the destroy call?
class PhoneNumber < ActiveRecord::Base
has_many :personal_phone_numbers, :dependent => :destroy
has_many :people, :through => :personal_phone_numbers
alias_method :old_destroy, :destroy
def destroy(*args)
*do your stuff here*
old_destroy(*args)
end
end
It sounds like your problem in a nutshell is that you want to gather and act on a collection of Person when a PersonalPhoneNumber is destroyed. This approach may fit the bill!
Here is an example of a custom callback to collect Person models. Here it's an instance method so we don't have to instantiate a PersonalPhoneNumberCallbacks object in the ActiveRecord model.
class PersonalPhoneNumberCallbacks
def self.after_destroy(personal_phone_number)
# Something like people = Person.find_by_personal_phone_number(personal_phone_number)
# Delayed Job Stuff on people
end
end
Next, add the callback do your ActiveRecord model:
class PersonalPhoneNumber < ActiveRecord::Base
after_destroy PictureFileCallbacks
end
Your after_destroy callback will have the model passed down and you can act on its data. After the callback chain is complete, it will be destroyed.
References
http://guides.rubyonrails.org/active_record_validations_callbacks.html#relational-callbacks
http://guides.rubyonrails.org/association_basics.html#association-callbacks
You can use a before_destroy callback in the model, then grab the data and do whatever operation you need to before destroy the parent. Something like this example should be what you are looking for:
class Example < ActiveRecord::Base
before_destroy :execute_random_method
private
def execute_random_method
...
end
handle_asynchronously :execute_random_method
A bit old but thought I'd share that rails now has the nice 'prepend' option for the before_destroy callback now. This goes follows the same line of thought with tomciopp had but allows you to define the before_destroy whereever in the class.
before_destroy :find_associated_people, prepend: true
def find_associated_people
# using phone number, find people
end
In Rails 3 is there a clean way to check if parent object is a new record but from the before_create callback of the child
class Parent
has_many :children
accepts_nested_attributes_for :children
end
class Child
before_create :on_before_create
def on_before_create
logger.debug self.parent.new_record?
logger.debug self.parent.id
logger.debug self.parent.id_was
end
end
I only want to execute code in on_before_create if the parent is not a new record
but at the point the child is created the parent record has been inserted into the db and so it has an id, id and id_was both return the same value,
new_record? is actually returning nothing, usually it returns a boolean
any ideas?
Use before_save rather than before_create. You'll need to check for create manually, but the callback is fired before any associated objects are saved.
class Parent < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
end
class Child < ActiveRecord::Base
before_save :on_before_save
belongs_to :parent
def on_before_save
# Don't run if this is not create.
return unless self.new_record?
self.parent.new_record? #=> boolean
end
end
Also, make sure you define the callbacks before the associations, as per the documentation:
In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won’t be inherited.