Allow Attachables to override default template when attachment is missing

This commit is contained in:
Matt Swanson 2023-02-13 10:45:12 -05:00
parent c88ca3b720
commit fbf0145f19
8 changed files with 83 additions and 9 deletions

@ -1,3 +1,12 @@
* Attachables now can override default attachment missing template.
When rendering ActionText attachments where the underlying attachable model has
been removed, a fallback template is used. You now can override this template on
a per-model basis. For example, you could render a placeholder image for a file
attachment or the text "Deleted User" for a User attachment.
*Matt Swanson*, *Joel Drapper*
* Update bundled Trix version from `1.3.1` to `2.0.4`.
*Sean Doyle*

@ -15,7 +15,7 @@ def from_node(node)
elsif attachable = ActionText::Attachables::RemoteImage.from_node(node)
attachable
else
ActionText::Attachables::MissingAttachable
ActionText::Attachables::MissingAttachable.new(node["sgid"])
end
end
@ -37,6 +37,10 @@ def attachable_from_sgid(sgid)
def from_attachable_sgid(sgid)
ActionText::Attachable.from_attachable_sgid(sgid, only: self)
end
def to_missing_attachable_partial_path
ActionText::Attachables::MissingAttachable::DEFAULT_PARTIAL_PATH
end
end
def attachable_sgid

@ -2,11 +2,25 @@
module ActionText
module Attachables
module MissingAttachable
class MissingAttachable
extend ActiveModel::Naming
def self.to_partial_path
"action_text/attachables/missing_attachable"
DEFAULT_PARTIAL_PATH = "action_text/attachables/missing_attachable"
def initialize(sgid)
@sgid = SignedGlobalID.parse(sgid, for: ActionText::Attachable::LOCATOR_NAME)
end
def to_partial_path
if model
model.to_missing_attachable_partial_path
else
DEFAULT_PARTIAL_PATH
end
end
def model
@sgid&.model_name.to_s.safe_constantize
end
end
end

@ -1,6 +1,10 @@
class Person < ApplicationRecord
include ActionText::Attachable
def self.to_missing_attachable_partial_path
"people/missing_attachable"
end
def to_trix_content_attachment_partial_path
"people/trix_content_attachment"
end

@ -0,0 +1 @@
<span class="missing-attachable">Missing person</span>

@ -48,4 +48,13 @@ class ActionText::ControllerRenderTest < ActionDispatch::IntegrationTest
assert_select ".mentioned-person", text: alice.name
end
test "resolves missing ActionText::Attachable based on their to_missing_attachable_partial_path" do
alice = people(:alice)
alice.destroy!
get messages_path
assert_select ".missing-attachable", text: "Missing person"
end
end

@ -56,19 +56,26 @@ class ActionText::ContentTest < ActiveSupport::TestCase
end
test "identifies destroyed attachables as missing" do
attachable = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}"></action-text-attachment>)
attachable.destroy!
file = create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg")
html = %Q(<action-text-attachment sgid="#{file.attachable_sgid}"></action-text-attachment>)
file.destroy!
content = content_from_html(html)
assert_equal 1, content.attachments.size
assert_equal ActionText::Attachables::MissingAttachable, content.attachments.first.attachable
attachable = content.attachments.first.attachable
assert_kind_of ActionText::Attachables::MissingAttachable, attachable
assert_equal file.class, attachable.model
assert_equal ActionText::Attachables::MissingAttachable::DEFAULT_PARTIAL_PATH, attachable.to_partial_path
end
test "extracts missing attachables" do
html = '<action-text-attachment sgid="missing"></action-text-attachment>'
content = content_from_html(html)
assert_equal 1, content.attachments.size
assert_equal ActionText::Attachables::MissingAttachable, content.attachments.first.attachable
attachable = content.attachments.first.attachable
assert_kind_of ActionText::Attachables::MissingAttachable, attachable
assert_nil attachable.model
end
test "converts Trix-formatted attachments" do

@ -205,12 +205,38 @@ partial-local variable:
<span><%= image_tag user.avatar %> <%= user.name %></span>
```
If Action Text is unable to resolve the `User` instance (for example, if the
record has been deleted), then a default fallback partial will be rendered.
Rails provides a global partial for missing attachments. This partial is installed
in your application at `views/action_text/attachables/missing_attachable` and can
be modified if you want to render different HTML.
To render a different missing attachment partial, define a class-level
`to_missing_attachable_partial_path` method:
```ruby
class User < ApplicationRecord
def self.to_missing_attachable_partial_path
"users/missing_attachable"
end
end
```
Then declare that partial.
```html+erb
<%# app/views/users/missing_attachable.html.erb %>
<span>Deleted user</span>
```
To integrate with Action Text `<action-text-attachment>` element rendering, a
class must:
* include the `ActionText::Attachable` module
* implement `#to_sgid(**options)` (made available through the [`GlobalID::Identification` concern][global-id])
* (optional) declare `#to_attachable_partial_path`
* (optional) declare a class-level method `#to_missing_attachable_partial_path` for handling missing records
By default, all `ActiveRecord::Base` descendants mix-in
[`GlobalID::Identification` concern][global-id], and are therefore