rails passing parameters to callback - ruby-on-rails

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.

Related

Creating new object with relations without the related id

I have a rails app with the following models:
class Product < ActiveRecord::Base
has_many :stores, through: :product_store
attr_accessible :name, :global_uuid
end
class ProductStore < ActiveRecord::Base
attr_accessible :deleted, :product_id, :store_id, :global_uuid
belongs_to :product
belongs_to :store
end
Since this model is for a REST API of a mobile app, I create the objets remotely, on the devices, and then sync with this model. As this happens, it may happen that I have to create a ProductStore before having an id set for Product. I know I could batch the API requests and find some workaround, but I've settled to have a global_uuid attribute that gets created in the mobile app and synced.
What I'd like to know is how can I make this code in my controller:
def create
#product_store = ProductStore.new(params[:product_store])
...
end
be aware that it will be receiving a product_global_uuid parameter instead of a product_id parameter and have it properly populate the model.
I figure I can override ProductStore#new but I'm not sure if there's any ramification when doing that.
Overriding .new is a dangerous business, you don't want to get involved in doing that. I would just go with:
class ProductStore < ActiveRecord::Base
attr_accessible :product_global_uuid
attr_accessor :product_global_uuid
belongs_to :product
before_validation :attach_product_using_global_uuid, on: :create
private
def attach_product_using_global_uuid
self.product = Product.find_by_global_uuid! #product_global_uuid
end
end
Having these kinds of artificial attr_accessors that are only used in model creation is kind of messy, and you want to avoid passing in anything that isn't a direct attribute of the model you are creating where possible. But as you say there are various considerations to balance, and it's not the worst thing in the world.

Complex After save association in ruby on rails

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

Validation to add only one biography for single person

I have two model user and biography, both has one-to-one association and i want to user to add only one biography..here is the model:-
class Biography < ActiveRecord::Base
attr_accessible :biography, :date, :title, :individual_id
belongs_to :user
end
class User < ActiveRecord::Base
has_one :biography
end
How can i validate user so that he can add only one biography.
please help!
You already have check out this link. (source)
A belongs_to association sets up a one-to-one connection with another
model, such that each instance of the declaring model “belongs to” one
instance of the other model.
A has_one association also sets up a one-to-one connection with
another model, but with somewhat different semantics (and
consequences). This association indicates that each instance of a
model contains or possesses one instance of another model.
But if you wanted you could add a :before_save method that will check user.biography does not return something unexpected, but I don't think this is neccessary.
EDIT AFTER COMMENTS:
Try something like a before_filter...
before_filter :check_user, :only => [:new]
This will call the method before the controller action is called. Then you can create the method that will check the current_user has a biography and direct them to it. This is a starting point..
def check_user
current_user.nil? ? redirect_to(new_biography_path) : redirect_to(edit_biography_path)
end
Hope it helps.

How to pass value from controller to model?

class Task < ActiveRecord::Base
has_many :notes, :as => :notable, :dependent => :destroy
has_many :work_times, :dependent => :destroy
end
class WorkTime < ActiveRecord::Base
belongs_to :task
end
class NotesController < ApplicationController
end
end
####
Any help please??
Since the relationship is a has_many you will need to work with a particular time, not the aggregate:
work_time = #task.work_times.last
work_time.start_time = Time.now
work_time.save!
In this case the last WorkTime record is selected and manipulated. Maybe you want to use the first, or select it with a condition:
work_time = #task.work_times.where(:active => true).first
There's a lot of ways to select the correct record to manipulate, but your question is somewhat vague.
If you're looking to create a new entry instead of modifying one, you might want to do this:
#task.work_times.create(:start_time => Time.now)
This is just exercising the ActiveRecord model relationship.
You would simply get the object and just change its value like :
user = User.first
user.username = 'changed_name'
user.save # and save it if you want
But, this is actually code that belongs to a model and should be wrapped by a model method.
As alluded to by a few of the other answers, you need an object of type WorkTime to pass the value to.
From the code you've posted it doesn't look like you've got such an instance. You can either find one (WorkTime.find.. ) or create a new one (WorkTime.new..)
It looks like you have an instance of a note (#note), though I'm not sure where that came from.. you might be able to fetch appropriate WorkTime objects using:
#note.task.work_times
or the first of these with:
#note.task.work_times.first

Set default random column value in ActiveRecord model

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

Resources