Track dirty for not-persisted attribute in an ActiveRecord object in rails

前端 未结 4 1451
天命终不由人
天命终不由人 2021-02-05 03:27

I have an object that inherits from ActiveRecord, yet it has an attribute that is not persisted in the DB, like:

 class Foo < ActiveRecord::Base
   attr_acces         


        
相关标签:
4条回答
  • 2021-02-05 03:49

    I'm using the attribute_will_change! method and things seem to be working fine.

    It's a private method defined in active_model/dirty.rb, but ActiveRecord mixes it in all models.

    This is what I ended up implementing in my model class:

    def bar
      @bar ||= init_bar
    end
    def bar=(value)
      attribute_will_change!('bar') if bar != value
      @bar = value
    end
    def bar_changed?
      changed.include?('bar')
    end
    

    The init_bar method is just used to initialise the attribute. You may or may not need it.

    I didn't need to specify any other method (such as define_attribute_methods) or include any modules. You do have to reimplement some of the methods yourself, but at least the behaviour will be mostly consistent with ActiveModel.

    I admit I haven't tested it thoroughly yet, but so far I've encountered no issues.

    0 讨论(0)
  • 2021-02-05 03:52

    ActiveRecord has the #attribute method (source) which once invoked from your class will let ActiveModel::Dirty to create methods such as bar_was, bar_changed?, and many others.

    Thus you would have to call attribute :bar within any class that extends from ActiveRecord (or ApplicationRecord for most recent versions of Rails) in order to create those helper methods upon bar.

    Edit: Note that this approach should not be mixed with attr_accessor :bar

    Edit 2: Another note is that unpersisted attributes defined with attribute (eg attribute :bar, :string) will be blown away on save. If you need attrs to hang around after save (as I did), you actually can (carefully) mix with attr_reader, like so:

    attr_reader :bar
    attribute :bar, :string
    
    def bar=(val)
      super
      @bar = val
    end
    
    0 讨论(0)
  • 2021-02-05 04:05

    I figured out a solution that worked for me...

    Save this file as lib/active_record/nonpersisted_attribute_methods.rb: https://gist.github.com/4600209

    Then you can do something like this:

    require 'active_record/nonpersisted_attribute_methods'
    class Foo < ActiveRecord::Base
      include ActiveRecord::NonPersistedAttributeMethods
      define_nonpersisted_attribute_methods [:bar]
    end
    
    foo = Foo.new
    foo.bar = 3
    foo.bar_changed? # => true
    foo.bar_was # => nil
    foo.bar_change # => [nil, 3]
    foo.changes[:bar] # => [nil, 3]
    

    However, it looks like we get a warning when we do it this way:

    DEPRECATION WARNING: You're trying to create an attribute `bar'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc.
    

    So I don't know if this approach will break or be harder in Rails 4...

    0 讨论(0)
  • 2021-02-05 04:08

    Write the bar= method yourself and use an instance variable to track changes.

    def bar=(value)
      @bar_changed = true
      @bar = value
    end
    
    def bar_changed?
      if @bar_changed
        @bar_changed = false
        return true
      else
        return false
      end
    end
    
    0 讨论(0)
提交回复
热议问题