Here's a script demonstrating the bug:
require 'bundler/inline' gemfile do source "https://rubygems.org" gem "mongoid" gem "ostruct" # get rid of warnings in newer Rubies end Mongoid.configure do |config| config.clients.default = { hosts: ['localhost:27017'], database: 'mongoid_test', } end class Article include Mongoid::Document embeds_many :authors end class Author include Mongoid::Document include Mongoid::Timestamps unless ENV["SKIP_TIMESTAMP"] field :name, type: String embedded_in :article before_save :destroy_if_rodrigo def destroy_if_rodrigo destroy if name == "Rodrigo" end end article = Article.create article.authors << Author.new(name: "Rodrigo") article.save
Run it without including Mongoid::Timestamps:
> SKIP_TIMESTAMP=1 ruby mongoid-frozen-attributes-bug.rb
Now try with Mongoid::Timestamps:
> ruby mongoid-frozen-attributes-bug.rb
ruby mongoid-frozen-attributes-bug.rb /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/attributes.rb:191:in `block in write_attribute': can't modify frozen Hash: {"_id"=>BSON::ObjectId('6727ceff9b99b276f43be4ec'), "name"=>"Rodrigo"} (FrozenError) from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/threaded/lifecycle.rb:31:in `_assigning' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/attributes.rb:174:in `write_attribute' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/fields.rb:696:in `block (2 levels) in create_field_setter' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/timestamps/created.rb:28:in `set_created_at' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:362:in `block in make_lambda' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:179:in `block in call' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:668:in `block (2 levels) in default_terminator' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:667:in `catch' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:667:in `block in default_terminator' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:180:in `call' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:559:in `block in invoke_before' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:559:in `each' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:559:in `invoke_before' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:109:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/interceptable.rb:139:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/persistable/creatable.rb:111:in `block (2 levels) in prepare_insert' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:110:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/interceptable.rb:139:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/persistable/creatable.rb:110:in `block in prepare_insert' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/activesupport-7.2.2/lib/active_support/callbacks.rb:101:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/interceptable.rb:139:in `run_callbacks' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/persistable/creatable.rb:109:in `prepare_insert' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/persistable/creatable.rb:21:in `insert' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/persistable/savable.rb:27:in `save' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/association/embedded/embeds_many/proxy.rb:96:in `block in <<' from <internal:kernel>:90:in `tap' from /Users/rodrigo.rosas/.rbenv/versions/3.3.5/lib/ruby/gems/3.3.0/gems/mongoid-9.0.2/lib/mongoid/association/embedded/embeds_many/proxy.rb:94:in `<<' from mongoid-frozen-attributes-bug.rb:37:in `<main>'
By the way, `ostruct` should be added as a dependency.