-
Type: Improvement
-
Resolution: Fixed
-
Priority: Unknown
-
Affects Version/s: None
-
Component/s: None
-
None
attributes_before_type_cast is used by ActiveRecord to store incoming values when type conversions are performed during attribute assignment and when reading data from the database (where no type conversion normally happens, thus attributes_before_type_cast is the same as attributes). The equivalent operations in Mongoid are mongoization and demongoization. Currently Mongoid only populates attributes_before_type_cast during attribute assignment (prior to mongoization). Mongoid should also populate attributes_before_type_cast during demongoization, as part of instantiating models during reads from thedatabase.
The new behavior will match AR. Currently, since attributes_before_type_cast are always empty on a model just retrieved from the database, users shouldn't be using this method for anything as it essentially doesn't work. Making it work as per AR is not expected to cause compatibility issues.
The contents of attributes_before_type_cast can be one of two totally different types: user input upon assignment, raw DB data upon read from DB. In case of Mongoid this is pre-mongoized and pre-demongoized input which could be of totally different types (e.g. Range & Hash). For AR, the behavior is actually the same for hstore for example:
class CreateTypeTests < ActiveRecord::Migration[7.0] def change create_table :type_tests do |t| t.string :str t.decimal :dec t.time :t t.timestamp :ts t.hstore :hs t.text :a, array: true t.inet :i t.timestamps end end end
irb(main):004:0> a=TypeTest.new(hs:{a:42}) => #<TypeTest:0x00007f6cc29d1238 ... irb(main):005:0> a.attributes_before_type_cast => {"id"=>nil, "str"=>nil, "dec"=>nil, "t"=>nil, "ts"=>nil, "hs"=>{:a=>42}, "a"=>nil, "i"=>nil, "created_at"=>nil, "updated_at"=>nil} irb(main):006:0> a.save! TRANSACTION (0.4ms) BEGIN TypeTest Create (0.7ms) INSERT INTO "type_tests" ("str", "dec", "t", "ts", "hs", "a", "i", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id" [["str", nil], ["dec", nil], ["t", nil], ["ts", nil], ["hs", "\"a\"=>\"42\""], ["a", nil], ["i", nil], ["created_at", "2022-07-01 15:53:22.582224"], ["updated_at", "2022-07-01 15:53:22.582224"]] TRANSACTION (6.0ms) COMMIT => true irb(main):007:0> a.attributes_before_type_cast => {"id"=>45, "str"=>nil, "dec"=>nil, "t"=>nil, "ts"=>nil, "hs"=>"\"a\"=>\"42\"", "a"=>nil, "i"=>nil, "created_at"=>Fri, 01 Jul 2022 15:53:22.582224000 UTC +00:00, "updated_at"=>Fri, 01 Jul 2022 15:53:22.582224000 UTC +00:00}
assigning a value to an attribute writes the original input into ABTC
saving the model resets ABTC to mongonized attribute values (as they are written into DB)
reloading a model resets ABTC to mongoized attribute values as they are in DB
reading a model from DB writes attribute values as they are in DB to ABTC
Tests:
Test *_before_type_cast followed by attributes_before_type_cast, attributes_before_type_cast should have all attributes in the model