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.
Related
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
Theory :- after create of a record in customer bill, i am sending two sets of data two different models. one set of data is sent to ledger and one set of data is sent to ledger_line_item. the complexity is that after sending of data i want the ledger_id to be stored in ledger_line_item. the code is as follows
code :-
class CustomerBill < ActiveRecord::Base
after_create :creating_ledger_line_items, :creating_ledger_items
def creating_ledger_items
CustomerLedger.create(:customer_id =>self.customer_id,/*rest of attributes*/)
end
def creating_ledger_line_items
CustomerLedgerLineItem.create(:customer_id =>self.customer_id,/*rest of attributes*/)
end
end
in ledger i have written
class CustomerLedger < ActiveRecord::Base
after_save :update_record_line_items
def update_record_line_items
a = CustomerLedgerLineItem.find_by_customer_id(self.customer_id)
a.update_attributes(:customer_ledger_id => self.id)
end
end
the above code works fine without error but the ledger_id is not been posted in ledger_line_items. i am not able to determine why this error is happening? is there any other way i can achieve my goal of posting ledger_id in ledger_line_items after a bill is created?
Guidance Required. Thanking you in advance.
You can change your models something as follows.:
I am assuming you have Customer Model.
class Customer < ActiveRecord::Base
has_one :customer_ledger
has_many :customer_ledger_line_items, :through => :customer_ledger
accepts_nested_attributes_for :customer_ledger
end
class CustomerLedger < ActiveRecord::Base
has_many :customer_ledger_line_items
accepts_nested_attributes_for :customer_ledger_line_items
end
class CustomerBill < ActiveRecord::Base
belongs_to :customer
after_create :creating_ledger_items, :creating_ledger_line_items
def creating_ledger_line_items
cl = self.customer.customer_ledger.build(your_attributes)
cl.save!
end
def creating_ledger_items
cli = self.customer.customer_ledger.customer_ledger_items.build(your_attributes)
cli.save!
end
end
In case you want to create the models on an *after_create* hook, I'll explain what's the problem.
When you create a model in rails, and you have hooks like *after_create*, *before_update*, etc. all the updates happens in a Transaction, so if any of them throws an exception, nothing is updated.
In this case, within a Transaction, you are trying to get the ID of a CustomerLedger that doesn't exists yet, because since everything is within a Transaction, the record is not saved to the database until the transaction is executed, and thats the reason that on CustomerLedger#update_record_line_items, self.id is always nil.
Using the nested attributes proposed by codeit is probably the best solution to your problem, but if you feel that nested attributes its an advance topic, you can do something like:
class CustomerBill < ActiveRecord::Base
after_create :created_leder_data
def create_ledger_data
customer_ledger = CustomerLedger.build(customer_id: self.customer_id, # Rest of attributes)
customer_ledger.customer_ledger_line_items.build(customer_id: self.customer_id, # Rest of attributes)
customer_ledger.save!
end
end
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
I have two models
class User < ActiveRecord::Base
has_many :posts
searchable do
text :post_titles
end
def post_titles
posts.map &:title
end
end
class Post < ActiveRecord::Base
belongs_to :user
end
the problem is that when I update title of the Post sunspot doesn't update index for related user and it is not searchable by new data. If I do User.index it solves problem but takes too much time. Are there any better solutions to update parent record index on child record change(like reindex just parent record and not all users)?
Sunspot provides an instance index() method, for indexing one record.
What I did was
class Post
belongs_to :user
after_save :update_user_index
private
def update_user_index
user.index
end
end
If you are running this in console, and want to see results immediately, call Sunspot.commit
got 2 models:
class User < ActiveRecord::Base
has_many :posts
end
and
class Post < ActiveRecord::Base
belongs_to :user
end
the Posts table has a column: u_hash. This is supposed to be a randomly generated identifying hash (for public viewing). What is the best way to generate this hash and how can I add it to the table? The idea is that all this will happen in the background and not be visible to the user (no hidden field in the form). The database used is MySQL if that could help me out somehow.
Thanks in advance!
J
You most likely need before_validation_on_create callback for your Post model. This callback is internally called by ActiveRecord functionality when you save a new Post record into database.
A good callback reference and a hint of the order callbacks are called in you can find here.
Here's a code, that explains why it is before_validation_on_create that you need to use:
class Post < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :u_hash
before_validation_on_create :generate_u_hash
def generate_u_hash
begin
new_u_hash = "random hash here"
end while Post.find_by_u_hash(new_u_hash)
self.u_hash = new_u_hash
end
end
This sounds like a job for ActiveRecord callbacks.
If your posts tables has a before_create callback, you can create and set a value automatically every time a new post instance is created.
e.g.:
class Post < ActiveRecord::Base
belongs_to :user
before_create :set_uhash_column
private
def set_uhash_column
#your code here - something like self.uhash = ...
end
end