10

I have a Project model which accepts nested attributes for tasks. And Task has a virtual attribute "name". So every time I change the name, it gets persisted as encrypted_task_name before update. On the project edit page the form has a input field for task name (and not encrypted_task_name). When I change the name and since name is a virtual attribute, Rails doesn't detect a change in Task and doesn't update that task while updating Project.

How do I make sure that task is saved even if its virtual attributes are changed during Project update?

One option that I don't want to use is :autosave => true on task.rb since I task is rarely updated.

Swamy g
  • 2,106
  • 4
  • 23
  • 35

4 Answers4

26

I ran into the same problem. Using :autosave => true didn't even work for me. I managed to solve it by adding attribute_will_change!(:my_virtual_attribute) to the writer for my virtual attribute. So, in your case:

class Task < ActiveRecord::Base
  ..
  def name=(the_name)
    attribute_will_change!(:name)
    ..
  end
  ..
end

This marks the object as unchanged or dirty, and that makes update_attributes save the nested model correctly.

Links:

http://apidock.com/rails/ActiveRecord/Dirty/attribute_will_change%21 http://ryandaigle.com/articles/2008/3/31/what-s-new-in-edge-rails-dirty-objects

David van Geest
  • 1,957
  • 1
  • 20
  • 19
  • 2
    Wish I could upvote you more. You know when you have no idea why something is going wrong, then SO produced the EXACT answer to what you're looking for? Yeah, that just happened to me. – Rob d'Apice May 25 '11 at 08:22
  • 2
    I _do_ know that feeling. Figured it was time to produce one of those answers myself :-). Thanks for the upvote! – David van Geest May 25 '11 at 17:19
  • You saved me... This problem was killing me. – Robin Sep 06 '12 at 23:03
2

For Rails 5.1 and up it's advisable to use attribute instead of attr_accessor as it dirties up the object, thus triggering the validation.

class Task < ActiveRecord::Base
  attribute :name, :string
end
Andrei Erdoss
  • 1,573
  • 1
  • 13
  • 14
  • thanks. This helped me to solve a related issue with updating nested_attributes: https://stackoverflow.com/questions/69025258/unable-to-update-nested-attributes-even-when-providing-record-id – Miroslav Sep 04 '21 at 13:00
0

Check out Attribute Filters gem. It takes care of virtual attributes tracking (automagically wrapping setter methods) by adding attr_virtual DSL keyword and lets you do other things, like declarative filtering of attributes:

class User < ActiveRecord::Base
  include ActiveModel::AttributeFilters::Common::Split

  split_attribute   :real_name => [ :first_name, :last_name ]
  before_validation :filter_attributes

  attr_virtual  :real_name
  attr_accessor :real_name
end
siefca
  • 1,007
  • 13
  • 16
0

In general, I'd recommend RailsCasts.com - episodes 167 and 16

http://railscasts.com/episodes/167-more-on-virtual-attributes and
http://railscasts.com/episodes/16-virtual-attributes

In episode 167, Ryan does something very similar

If this doesn't help, could you post the relevant code for your Project and Task models?

Tilo
  • 33,354
  • 5
  • 79
  • 106