Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-4062

attr_readonly does not behave as expected for save or save!

    • Type: Icon: Task Task
    • Resolution: Done
    • 5.0.0
    • Affects Version/s: None
    • Component/s: None
    • Labels:

      Hi,

      Following Readonly Attributes I would expect that trying to save a document where I've modified a readonly attribute would fail. It does not.

      For example:

      class Band
        include Mongoid::Document
        field :name, type: String
        field :origin, type: String
      
        attr_readonly :name, :origin
      end
      
      # These fail as documented
      rails console> band = Band.first
      rails console> band.update_attribute(:name, "Tool")
      Mongoid::Errors::ReadonlyAttribute: 
      Problem:
        Attempted to set the readonly attribute 'name' with the value: Tool.
      Summary:
        Attributes flagged as readonly via Model.attr_readonly can only have values set when the document is a new record.
      Resolution:
        Don't define 'name' as readonly, or do not attempt to update its value after the document is persisted.
      	from /Users/joel/.rvm/gems/ruby-2.2.2/gems/mongoid-4.0.2/lib/mongoid/persistable/updatable.rb:29:in `update_attribute'
      ...
      
      rails console> band.remove_attribute(:name)
      Mongoid::Errors::ReadonlyAttribute: 
      Problem:
        Attempted to set the readonly attribute 'name' with the value: nil.
      Summary:
        Attributes flagged as readonly via Model.attr_readonly can only have values set when the document is a new record.
      Resolution:
        Don't define 'name' as readonly, or do not attempt to update its value after the document is persisted.
      	from /Users/joel/.rvm/gems/ruby-2.2.2/gems/mongoid-4.0.2/lib/mongoid/attributes.rb:144:in `remove_attribute'
      
      # This does not fail, but I would expect it to
      rails console> band = Band.first
      rails console> band.name = 'new name'
      rails console> band.save
      => true
      
      # I would also expect this to fail
      rails console> band = Band.first
      rails console> band.name = 'new name'
      rails console> band.save!
      => true
      

      Neither save nor save! fail when I try to save after modifying a readonly attribute: save returns true and save! fails to raise an exception, in both cases indicating that the save succeeded. However, if I then re-read the band I just saved, my "new name" value is not there. So save told me that the save succeeded but it didn't, as my changes were not persisted.

      This violates the principal of least surprise. update_attributes and save are basically the same - they both modify attributes in the backing store. But update_attributes fails / raises an exception when we try to modify a readonly attribute, whereas save and save! do not.

      This is risky because depending on whether we choose update_attributes or save, we may have a subtle bug in business logic and different enforcement of readonly-ness.

      So save and save! should fail if we have tried to set a readonly attribute to a new value prior to invoking them.

      I'm happy to help with more diagnostics or code changes if needed.

      I am using the following versions:

      • OS X 10.10.3
      • ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
      • rails 4.2.1
      • mongoid 4.0.2
      • mongodb 3.0.2

      Thanks!

            Assignee:
            estolfo estolfo
            Reporter:
            joelpresence joelpresence
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: