Skip to content

Commit 55d1bd7

Browse files
neilshwekyp-mongo
andauthored
MONGOID-5474 add readonly! and raise on save/update (#5455)
* MONGOID-5474 add readonly! and raise on save/update * MONGOID-5474 flip flag for 9.0 and add default * MONGOID-5474 fix error tests * Update spec/mongoid/config_spec.rb * Apply suggestions from code review Co-authored-by: Oleg Pudeyev <[email protected]> * Update lib/mongoid/config.rb Co-authored-by: Oleg Pudeyev <[email protected]> Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent 7f6fd08 commit 55d1bd7

26 files changed

+681
-32
lines changed

lib/config/locales/en.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,8 @@ en:
567567
resolution: "Don't define '%{name}' as readonly, or do not attempt
568568
to update its value after the document is persisted."
569569
readonly_document:
570-
message: "Attempted to persist the readonly document '%{klass}'."
571-
summary: "Documents loaded from the database using #only
572-
cannot be persisted."
570+
message: "Attempted to persist a readonly document of class '%{klass}'."
571+
summary: "Documents that are marked readonly cannot be persisted."
573572
resolution: "Don't attempt to persist documents that are flagged as
574573
readonly."
575574
scope_overwrite:

lib/mongoid/config.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ module Config
127127
# always return a Hash.
128128
option :legacy_attributes, default: false
129129

130+
# When this flag is true, a document is only readonly if it has been
131+
# projected using #only or #without, and readonly documents will not
132+
# be deletable/destroyable. When this flag is false, a document will
133+
# become readonly only once the #readonly! method is called, and an error
134+
# will be raised on attempting to save or update such documents, instead
135+
# of just on delete.
136+
# When this feature flag is turned on, the readonly state will be reset on
137+
# reload, but when it is turned off, it won't be.
138+
option :legacy_readonly, default: false
139+
130140
# Returns the Config singleton, for use in the configure DSL.
131141
#
132142
# @return [ self ] The Config singleton.

lib/mongoid/config/defaults.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def load_defaults(version)
1919
# versions are to give old functionality. Because of this, it is
2020
# possible to recurse to later version to get all of the options to
2121
# turn off. Note that this won't be true when adding feature flags to
22-
# 8.1, since the default will be the old functionality until the next
22+
# 9.x, since the default will be the old functionality until the next
2323
# major version is released. More likely, the recursion will have to go
2424
# in the other direction (towards earlier versions).
2525

@@ -48,6 +48,7 @@ def load_defaults(version)
4848
self.map_big_decimal_to_decimal128 = false
4949
when "8.0"
5050
# All flag defaults currently reflect 8.0 behavior.
51+
self.legacy_readonly = true
5152
when "8.1"
5253
# All flag defaults currently reflect 8.1 behavior.
5354
when "9.0"

lib/mongoid/persistable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ def post_process_persist(result, options = {})
180180
#
181181
# @return [ Object ] The result of the operation.
182182
def prepare_atomic_operation
183+
raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
183184
operations = yield({})
184185
persist_or_delay_atomic_operation(operations)
185186
self

lib/mongoid/persistable/creatable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def post_process_insert
100100
#
101101
# @return [ Document ] The document.
102102
def prepare_insert(options = {})
103+
raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
103104
return self if performing_validations?(options) &&
104105
invalid?(options[:context] || :create)
105106
run_callbacks(:save, with_children: false) do

lib/mongoid/persistable/deletable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ module Deletable
1616
#
1717
# @return [ TrueClass ] True.
1818
def delete(options = {})
19-
raise Errors::ReadonlyDocument.new(self.class) if readonly?
2019
prepare_delete do
2120
unless options[:persist] == false
2221
if embedded?
@@ -102,6 +101,7 @@ def notifying_parent?(options = {})
102101
#
103102
# @return [ Object ] The result of the block.
104103
def prepare_delete
104+
raise Errors::ReadonlyDocument.new(self.class) if readonly?
105105
yield(self)
106106
freeze
107107
self.destroyed = true

lib/mongoid/persistable/updatable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def init_atomic_updates
9696
#
9797
# @return [ true | false ] The result of the update.
9898
def prepare_update(options = {})
99+
raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
99100
return false if performing_validations?(options) &&
100101
invalid?(options[:context] || :update)
101102
process_flagged_destroys

lib/mongoid/persistable/upsertable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def upsert(options = {})
3838
#
3939
# @return [ true | false ] If the operation succeeded.
4040
def prepare_upsert(options = {})
41+
raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
4142
return false if performing_validations?(options) && invalid?(:upsert)
4243
result = run_callbacks(:upsert) do
4344
yield(self)

lib/mongoid/stateful.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,35 @@ def pushable?
9595
!_parent.delayed_atomic_sets[atomic_path]
9696
end
9797

98+
# Flags the document as readonly. Will cause a ReadonlyDocument error to be
99+
# raised if the document is attempted to be saved, updated or destroyed.
100+
#
101+
# @example Flag the document as readonly.
102+
# document.readonly!
103+
#
104+
# @return [ true | false ] true if the document was successfully marked
105+
# readonly, false otherwise.
106+
def readonly!
107+
if Mongoid.legacy_readonly
108+
Mongoid::Warnings.warn_legacy_readonly
109+
false
110+
else
111+
@readonly = true
112+
end
113+
end
114+
98115
# Is the document readonly?
99116
#
100117
# @example Is the document readonly?
101118
# document.readonly?
102119
#
103120
# @return [ true | false ] If the document is readonly.
104121
def readonly?
105-
__selected_fields != nil
122+
if Mongoid.legacy_readonly
123+
__selected_fields != nil
124+
else
125+
@readonly ||= false
126+
end
106127
end
107128

108129
# Determine if the document can be set.

lib/mongoid/warnings.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ def warning(id, message)
2424
warning :geo_haystack_deprecated, 'The geoHaystack type is deprecated.'
2525
warning :as_json_compact_deprecated, '#as_json :compact option is deprecated. Please call #compact on the returned Hash object instead.'
2626
warning :symbol_type_deprecated, 'The BSON Symbol type is deprecated by MongoDB. Please use String or StringifiedSymbol field types instead of the Symbol field type'
27+
warning :legacy_readonly, 'The readonly! method will only mark the document readonly when the legacy_readonly feature flag is switched off.'
2728
end
2829
end

0 commit comments

Comments
 (0)