I have a model, say Question, with fields time_asked and time_answered. I want to be able to set time_asked when the question is served, and time_answered when a response is posted. Is this possible and how might I do it?
You can use active record callbacks to do so.
Suppose you have a separate class Response and it belongs to Question.
So your code for time_asked will look like:
class Question < ActiveRecord::Base
has_many :responses
before_create :fill_time_asked
private
def fill_time_asked
time_asked = Time.now
end
end
response.rb:
class Response < ActiveRecord::Base
belongs_to :question
before_create :fill_time_answered
private
def fill_time_answered
question.time_aswered = Time.now
end
end
Another way to do it is to manually call proper methods, e.g. question.asked! or question.answered! from your controllers. However, the rails way is to keep business logic in your models and have thin controllers.
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.
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 two models
class Department < ActiveRecord::Base
has_many :checklists
attr_accessible :deadline
after_update :update_checklist
class Checklist < ActiveRecord::Base
belongs_to :department
attr_accessible :content, :category
Basically, the 'department' model has a virtual attribute called 'deadline', and it is in type of date. The actual value of 'deadline' is stored in another model 'checklist', in format of string.
Every time when 'deadline' is updated, I would like to check if there is an entry in 'checklist', and create (if not yet) or update (if already has an entry).
I was thinking this way
def deadline=(deadline)
#cl = Checklist.find_or_create_by_department_id_and_category(self.id, 'deadline')
#cl.update_attributes(:content => deadline.to_s)
#cl.save
end
def deadline
#deadline = self.checklists.find_by_category('deadline')
Date.parse(#deadline.to_s)
end
But the above virtual attribute is not working.
When searching for the answer, I found on rails cast that callback will be a better solution for this kind of situation. So I am trying to something like:
class Department < ActiveRecord::Base
after_update :update_checklist
def update_checklist
#cl = Checklist.find_or_create_by_department_id_and_category(self.id, 'deadline')
#cl.update_attributes(:content => ???)
end
I am not sure how to pass the value to the callback.
Please help me with this design. what is the standard way to handle this? Thank you in advance!
update_checklist is a method of Department. So within update_checklist, you can access any Department attributes, just like self.id, self.deadline is what you want.
Assuming a typical has_many association
class Customer < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :customer
end
How can I add a method to the collection of orders? For the sake of code organization, I'm trying to reactor this method (this is a made-up example) inside of my Customer class:
def update_orders
ThirdPartyAPI.look_up(self.orders) do |order|
# Do stuff to the orders
# May need to access 'self', the Customer...
end
end
I don't like this because it puts a lot of knowledge about the Order class inside my Customer class. I can't use an instance method off of an order, since the ThirdPartyAPI can do a batch lookup on multiple orders. I could make a static method off of Order and pass in the array of orders, and their parent customer, but this feels clunky.
I found this in the rails docs, but I couldn't find any good examples of how to use this in practice. Are there any other ways?
I think this should do it
has_many :entities do
def custom_function here
end
def custom_function here
end
end
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