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:

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

      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)

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


                Error rendering 'slack.nextup.jira:slack-integration-plus'. Please contact your Jira administrators.