Rails after_create callback can't access model's attributes - ruby-on-rails

I can't access my model's attributes in the after_create callback... seems like I should be able to right?
controller:
#dog = Dog.new(:color => 'brown', :gender => 'male')
#dog.user_id = current_user.id
#dog.save
model:
class Dog < ActiveRecord::Base
def after_create
logger.debug "[DOG CREATED] color:#{color} gender:#{gender} user:#{user_id}"
end
end
console: (all seems well)
>>Dog.last
=>#<Dog id: 1, color: "brown", gender: "male", user_id: 1>
log: (wtf!?)
...
[DOG CREATED] color: gender:male user
...
Some of my attributes show up and others don't! oh no! Anyone know what I'm doing wrong? I've always been able to user after_create in such ways in the past.
Note: The actual variable names and values I used were different, but the methods and code are the same.

Figured out my own problem.
One of the attributes was a virtual one, in which I used self.update_attribute...oops!
def price=(amt)
self.update_attribute(:price_in_cents, (amt*100.0).to_i)
end
So for the record, update_attribute will actually create database record (and trigger after_create) if it hasn't been created yet.
Next time I'll be sure to post full code!

Try this instead:
class Dog < ActiveRecord::Base
def after_create(dog)
logger.debug "[DOG CREATED] color:#{dog.color} gender:#{dog.gender} user:#{dog.user_id}"
end
end

Try using after_save instead of after_create may be that works. no tested though.
after_create () Is called after Base.save on new objects that haven‘t been saved yet (no record exists). Note that this callback is still wrapped in the transaction around save. For example, if you invoke an external indexer at this point it won‘t see the changes in the database.

The macro style callback is probably a better idea generally than simply overriding the method. It lets you fire a few different methods at the same time in the lifecycle (if you want). I think what you need is this:
class Dog < ActiveRecord::Base
after_create :dog_logger
def dog_logger
logger.debug "[DOG CREATED] color:#{self.color} gender:#{self.gender} user:#{self.user_id}"
end
end

Related

How to determine changed attributes on after_save callback

I can't catch the changed attributes on after_save callback but I can catch them on after_update callback. I think after_save is just a combination of after_create and after_update. I'd appreciate it if someone give me at least a hint.
class Student < ApplicationRecord
after_save: after_save_callback
def after_save_callback
if username_changed? ### This is always false
# do something
end
end
end
student = Student.create(name: 'John Doe', username: 'abcdef')
student.username = '123456'
student.save
My Rails version is 5.0.7.
You can use saved_change_to_username?
The behavior of attribute_changed? inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after save returned (e.g. the opposite of what it returns now). To maintain the current behavior, use saved_change_to_attribute? instead.

nested attributes update triggers wrong callback in observer

Two classes in the app/model:
class Job
accepts_nested_attributes_for :address
end
class Address
end
Then in jobs_controller, update method, I do #job.update(job_params), address params included in the job_params from the form submission.
The address can be updated correctly, however the address observer does not behave properly. Instead after_updated be called, it actually triggers after_create when address gets updated.
address_observer.rb
# cannot be triggered when address gets updated
def after_update(address)
end
# can be triggered when address gets updated
def after_create(address)
end
Cannot figure it out why, anyone could give some help on this? Thanks a lot in advance.
In your case, you need to make sure your parameters are passed in with a key address_attributes, and the correct id. If you do not include the id, a record will be created. This is why after_create is firing instead of after_update.
Here's an example (assuming a has_one relation:)
{ job: { address_attributes: { id: 1, foo: 'bar' } } }
Here is the relevant documentation: ActiveRecord::NestedAttributes::ClassMethods
You can now set or update attributes on the associated posts through an attribute hash for a member: include the key :posts_attributes with an array of hashes of post attributes as a value.
For each hash that does not have an id key a new record will be instantiated [...]
instead of observer..use callback in model...
after_commit :add_count_in_profile, on: :create
def add_count_in_profile
Rails.logger.info "---------updating images count in the profile for #
end

Rails model with array field, modify push method

I've been trying to find an answer to my question but so far no luck.
I have a model with an array field and I'd like method calls to happen when something gets pushed into the array.
class Shop::Order
include Mongoid::Document
include Mongoid::Timestamps
embeds_many :items,class_name: 'Shop::OrderItem', inverse_of: :order
accepts_nested_attributes_for :items
field :price, type: Money, default: Money.new(0)
field :untaxed_price, type: Money, default: Money.new(0)
end
So when doing order.items << Shop::OrderItem.new(...)
I'd like a method foo to be called.
EDIT: Add reason
So the reason for this is that I want to update the price and untaxed_price of an order each time an item is added to it.
Does it need to happen as soon as you push it? Or can it happen before you save the order? If you can wait until you save, you can do this:
before_validate :update_tax_info
def update_tax_info
if items_changed?
calculate_tax #whatever that may be
end
end
Throwing it in a validation would allow that callback to be called without saving. You could call #order.valid? to update the tax information.
I think monkey patching << is a bad idea. I have two ideas:
Use some kind of observer which listens on create of OrderItem and performs appropriate action
Overwrite OrderItem.create method (or even better provide abstraction):
```
class OrderItem
def add(params)
if create(params)
calculate_something
end
end
end
```
This documentation gives you the range of choices that you have to implement the desired behavior: http://mongoid.org/en/mongoid/docs/callbacks.html
To paraphrase, you have the option of using callbacks like before_save and before_update to do your calculations, or you can implement an Observer class to do this for you.
You can also use the changed method to see if the items array has changed and whether you need to update the derived fields.
Here's some example code:
class OrderObserver < Mongoid::Observer
def before_save(order)
do_something
end
end
Do remember to instantiate your observer in application.rb using:
config.mongoid.observers = :order_observer

before_destroy callback that halts but also updates model?

I am using a callback to halt a destroy action and instead want to set the :archived_at field. Problem is, the "return false" in my before_destroy causes a rollback that eliminates my update. Solutions to this?
class MarkArchived
def before_destroy(model)
update_attribute(:archived_at, Time.now) and return false
end
end
class User < ActiveRecord::Base
before_destroy MarkArchived
end
class Account < ActiveRecord::Base
before_destroy MarkArchived
end
If I understand correctly, you want some your objects (or some classes of objects) to be archived, never destroyed ? I think this gem might be suited for your use
Something like this?
def hide_or_destroy
if shifts.any?
update(hidden: true)
else
destroy
end
end

How to set some field in model as readonly when a condition is met?

I have models like this:
class Person
has_many :phones
...
end
class Phone
belongs_to :person
end
I want to forbid changing phones associated to person when some condition is met. Forbidden field is set to disabled in html form. When I added a custom validation to check it, it caused save error even when phone doesn't change. I think it is because a hash with attributes is passed to
#person.update_attributes(params[:person])
and there is some data with phone number (because form include fields for phone). How to update only attributes that changed? Or how to create validation that ignore saves when a field isn't changing? Or maybe I'm doing something wrong?
You might be able to use the
changed # => []
changed? # => true|false
changes # => {}
methods that are provided.
The changed method will return an array of changed attributes which you might be able to do an include?(...) against to build the functionality you are looking for.
Maybe something like
validate :check_for_changes
def check_for_changes
errors.add_to_base("Field is not changed") unless changed.include?("field")
end
def validate
errors.add :phone_number, "can't be updated" if phone_number_changed?
end
-- don't know if this works with associations though
Other way would be to override update_attributes, find values that haven't changed and remove them from params hash and finally call original update_attributes.
Why don't you use before_create, before_save callbacks in model to restrict create/update/save/delete or virtually any such operation. I think hooking up observers to decide whether you want to restrict the create or allow; would be a good approach. Following is a short example.
class Person < ActiveRecord::Base
#These callbacks are run every time a save/create is done.
before_save :ensure_my_condition_is_met
before_create :some_other_condition_check
protected
def some_other_condition_check
#checks here
end
def ensure_my_condition_is_met
# checks here
end
end
More information for callbacks can be obtained here:
http://guides.rubyonrails.org/activerecord_validations_callbacks.html#callbacks-overview
Hope it helps.

Resources