diff --git a/lib/couchbase-orm/base.rb b/lib/couchbase-orm/base.rb index 9a3bdb7..05b1767 100644 --- a/lib/couchbase-orm/base.rb +++ b/lib/couchbase-orm/base.rb @@ -46,6 +46,7 @@ class Document include Encrypt extend Enum + extend IgnoredProperties define_model_callbacks :initialize, :only => :after @@ -134,7 +135,6 @@ class Base < Document extend EnsureUnique extend HasMany extend Index - extend IgnoredProperties extend JsonSchema::Validation extend PropertiesAlwaysExistsInDocument diff --git a/lib/couchbase-orm/types/nested.rb b/lib/couchbase-orm/types/nested.rb index 4e332e3..5a2aa33 100644 --- a/lib/couchbase-orm/types/nested.rb +++ b/lib/couchbase-orm/types/nested.rb @@ -26,14 +26,29 @@ def initialize(type:) def cast(value) return nil if value.nil? return value if value.is_a?(@model_class) - return @model_class.new(value) if value.is_a?(Hash) + + if value.is_a?(Hash) + # Filter out ignored properties before creating the nested instance + # Optimization: only call .except if there are properties to ignore + ignored = @model_class.ignored_properties + filtered_value = ignored.empty? ? value : value.except(*ignored) + return @model_class.new(filtered_value) + end raise ArgumentError, "Nested: #{value.inspect} (#{value.class}) is not supported for cast" end def serialize(value) return nil if value.nil? - value = @model_class.new(value) if value.is_a?(Hash) + + if value.is_a?(Hash) + # Filter out ignored properties before creating the nested instance + # Optimization: only call .except if there are properties to ignore + ignored = @model_class.ignored_properties + filtered_value = ignored.empty? ? value : value.except(*ignored) + value = @model_class.new(filtered_value) + end + return value.send(:serialized_attributes) if value.is_a?(@model_class) raise ArgumentError, "Nested: #{value.inspect} (#{value.class}) is not supported for serialization" diff --git a/spec/type_nested_spec.rb b/spec/type_nested_spec.rb index 958fd56..63ca7c0 100644 --- a/spec/type_nested_spec.rb +++ b/spec/type_nested_spec.rb @@ -188,4 +188,64 @@ class WithValidationParent < CouchbaseOrm::Base expect(obj.child.child.errors[:name]).to eq ["can't be blank"] end end + + describe "Ignored Properties" do + class SubTypeWithIgnoredProperties < CouchbaseOrm::NestedDocument + self.ignored_properties = [:deprecated_property] + attribute :name, :string + attribute :value, :string + end + + class ParentWithNestedIgnoredProperties < CouchbaseOrm::Base + self.ignored_properties = [:deprecated_at_root] + attribute :title, :string + attribute :nested, :nested, type: SubTypeWithIgnoredProperties + end + + it "should ignore deprecated properties in nested documents on reload" do + # Create and save a parent with nested document + parent = ParentWithNestedIgnoredProperties.new + parent.title = "Test Parent" + parent.nested = SubTypeWithIgnoredProperties.new(name: "Nested", value: "Valid") + parent.save! + + # Manually add a deprecated property to the nested document in the database + doc_id = parent.id + raw_doc = ParentWithNestedIgnoredProperties.bucket.default_collection.get(doc_id).content + raw_doc["nested"]["deprecated_property"] = "This should be ignored" + ParentWithNestedIgnoredProperties.bucket.default_collection.replace(doc_id, raw_doc) + + # Reload the parent + parent.reload + + # The deprecated property should NOT be present in the nested document + expect(parent.nested.attributes.keys).not_to include("deprecated_property") + expect(parent.nested.name).to eq("Nested") + expect(parent.nested.value).to eq("Valid") + end + + it "should ignore deprecated properties in deeply nested documents" do + # Create a parent with nested documents that have a child + parent = ParentWithNestedIgnoredProperties.new + parent.title = "Test Parent" + parent.nested = SubTypeWithIgnoredProperties.new(name: "Parent Nested", value: "Parent Value") + parent.save! + + # Manually add deprecated properties at multiple levels + doc_id = parent.id + raw_doc = ParentWithNestedIgnoredProperties.bucket.default_collection.get(doc_id).content + raw_doc["deprecated_at_root"] = "Should be ignored at root level" + raw_doc["nested"]["deprecated_property"] = "Should be ignored in nested" + ParentWithNestedIgnoredProperties.bucket.default_collection.replace(doc_id, raw_doc) + + # Reload the parent + parent.reload + + # Deprecated properties should not be present at any level + expect(parent.attributes.keys).not_to include("deprecated_at_root") + expect(parent.nested.attributes.keys).not_to include("deprecated_property") + expect(parent.nested.name).to eq("Parent Nested") + expect(parent.nested.value).to eq("Parent Value") + end + end end