diff --git a/actiontext/CHANGELOG.md b/actiontext/CHANGELOG.md index 6f2baec2bd9..aff60a73e7b 100644 --- a/actiontext/CHANGELOG.md +++ b/actiontext/CHANGELOG.md @@ -1,3 +1,7 @@ +* Support `strict_loading:` option for `has_rich_text` declaration + + *Sean Doyle* + * Update ContentAttachment so that it can encapsulate arbitrary HTML content in a document. *Jamis Buck* diff --git a/actiontext/lib/action_text/attribute.rb b/actiontext/lib/action_text/attribute.rb index ae8fc93fbfa..9daf042f07c 100644 --- a/actiontext/lib/action_text/attribute.rb +++ b/actiontext/lib/action_text/attribute.rb @@ -30,7 +30,11 @@ module ActionText # # * :encrypted - Pass true to encrypt the rich text attribute. The encryption will be non-deterministic. See # +ActiveRecord::Encryption::EncryptableRecord.encrypts+. Default: false. - def has_rich_text(name, encrypted: false) + # + # * :strict_loading - Pass true to force strict loading. When + # omitted, strict_loading: will be set to the value of the + # strict_loading_by_default class attribute (false by default). + def has_rich_text(name, encrypted: false, strict_loading: strict_loading_by_default) class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name} rich_text_#{name} || build_rich_text_#{name} @@ -47,7 +51,8 @@ module ActionText rich_text_class_name = encrypted ? "ActionText::EncryptedRichText" : "ActionText::RichText" has_one :"rich_text_#{name}", -> { where(name: name) }, - class_name: rich_text_class_name, as: :record, inverse_of: :record, autosave: true, dependent: :destroy + class_name: rich_text_class_name, as: :record, inverse_of: :record, autosave: true, dependent: :destroy, + strict_loading: strict_loading scope :"with_rich_text_#{name}", -> { includes("rich_text_#{name}") } scope :"with_rich_text_#{name}_and_embeds", -> { includes("rich_text_#{name}": { embeds_attachments: :blob }) } diff --git a/actiontext/test/unit/strict_loading_test.rb b/actiontext/test/unit/strict_loading_test.rb new file mode 100644 index 00000000000..f7e8b69ba68 --- /dev/null +++ b/actiontext/test/unit/strict_loading_test.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "test_helper" + +class ActionText::StrictLoadingTest < ActiveSupport::TestCase + class MessageWithStrictLoading < Message + self.strict_loading_by_default = true + + has_rich_text :strict_loading_content + end + + class MessageWithFooter < MessageWithStrictLoading + has_rich_text :footer, strict_loading: false + end + + test "has_rich_text reads strict_loading: option from strict_loading_by_default" do + MessageWithStrictLoading.create! strict_loading_content: "ignored" + + assert_raises ActiveRecord::StrictLoadingViolationError do + MessageWithStrictLoading.all.map(&:strict_loading_content) + end + + MessageWithStrictLoading.with_rich_text_strict_loading_content.map(&:strict_loading_content) + end + + test "pre-loading the association does not raise a StrictLoadingViolationError" do + MessageWithStrictLoading.create! strict_loading_content: "ignored" + + records = MessageWithStrictLoading.with_rich_text_strict_loading_content.all + + records.map(&:strict_loading_content) + end + + test "has_rich_text accepts strict_loading: overrides" do + MessageWithFooter.create! footer: "ignored" + + records = MessageWithFooter.all + + records.map(&:footer) + end +end