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

Always demongoize uncastable values to nil

    • Type: Icon: Improvement Improvement
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 8.0.1
    • Affects Version/s: None
    • Component/s: None
    • Labels:
      None

      It is possible, in a Mongoid application, to declare a type on a field which already contains data in the database of other, incompatible types. When retrieving such data, Mongoid is presently inconsistent with what happens:

      • Some types raise an exception.
      • Some types replace the database value with nil.
      • Some types replace the database value with a "default" value for the type.
      • Some types end up placing a value of the wrong type in the field (thus essentially overriding the user's field type specification).

      Research by dmitry.rybakov:

      irb(main):015:0> Date.demongoize('i am so bad')
      date.rb:48:in `demongoize': undefined method `year' for "i am so bad":String (NoMethodError)
      
      irb(main):018:0> Time.demongoize('so am i')
      time.rb:57:in `demongoize': undefined method `getlocal' for "so am i":String (NoMethodError)
      
      irb(main):020:0> DateTime.demongoize('i am also bad')
      time.rb:57:in `demongoize': undefined method `getlocal' for "i am also bad":String (NoMethodError)
      
      irb(main):021:0> Float.demongoize('i am sooooooo bad')
      => 0.0
      
      irb(main):022:0> Integer.demongoize('could not be worse')
      => 0
      
      irb(main):023:0> Range.demongoize('and yet it can')
      => nil..
      
      irb(main):024:0> Set.demongoize('i have no idea what i am doing')
      ruby/3.1.0/set.rb:280:in `do_with_enum': value must be enumerable (ArgumentError)
      
      irb(main):026:0> Regexp.demongoize(42).class.name
      => "Integer"
      
      irb(main):028:0> Hash.demongoize('i give up')
      => "i give up"
      

      This manifests as:

        class Foo
          include Mongoid::Document
      
          field :a
        end
      
        Foo.delete_all
      
        Foo.create!(a: 'zz')
      
        class Foo
          field :a, type: BigDecimal
        end
      
        p Foo.first, Foo.first.a
      produces:
      
      NoMethodError: undefined method `getlocal' for "zz":String
      Test with the code as proposed with BigDecimal field:
      
        class Foo
          include Mongoid::Document
      
          field :a
        end
      
        Foo.delete_all
      
        Foo.create!(a: 'zz')
      
        class Foo
          field :a, type: Time
        end
      
        p Foo.first, Foo.first.a
      
      produces:
      
      #<Foo _id: 61d76a95a15d5d4e9c038186, a: "zz">
      nil
      

      Note that the first of the four options causes difficulties when working with data that is stored in the database, the second two options can be considered to lose data, the last option can cause weird application errors down the line.

      Mongoid should:

      Behave consistently with all types that it supports
      Document its behavior

      Possible solutions could involve a global or a per-field option as to how to handle values in the database that cannot be cast to the defined field type.

      The action items are as follows:
      create the ``validate_db_attribute_types`` flag
      document feature flag
      standardize demongoization functionality (always raise, rescue on flag)
      create InvalidDBValue error
      write release note for this change
      add documentation to Uncastable values section
      add note to custom types protocol

      • Revised design *
      • Deongoization of uncastable values will always return nil
      • Original value will be accessible in attributes_before_type_cast (will be done in MONGOID-5404)

            Assignee:
            neil.shweky@mongodb.com Neil Shweky (Inactive)
            Reporter:
            oleg.pudeyev@mongodb.com Oleg Pudeyev (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: