actiontext 7.1.5.2 → 8.1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -132
  3. data/app/assets/javascripts/actiontext.esm.js +114 -14
  4. data/app/assets/javascripts/actiontext.js +120 -17
  5. data/app/helpers/action_text/content_helper.rb +2 -0
  6. data/app/helpers/action_text/tag_helper.rb +63 -37
  7. data/app/javascript/actiontext/attachment_upload.js +78 -12
  8. data/app/javascript/actiontext/index.js +10 -1
  9. data/app/models/action_text/encrypted_rich_text.rb +2 -2
  10. data/app/models/action_text/record.rb +2 -0
  11. data/app/models/action_text/rich_text.rb +62 -27
  12. data/db/migrate/20180528164100_create_action_text_tables.rb +1 -1
  13. data/lib/action_text/attachable.rb +35 -33
  14. data/lib/action_text/attachables/content_attachment.rb +2 -0
  15. data/lib/action_text/attachables/missing_attachable.rb +2 -0
  16. data/lib/action_text/attachables/remote_image.rb +2 -0
  17. data/lib/action_text/attachment.rb +27 -25
  18. data/lib/action_text/attachment_gallery.rb +2 -0
  19. data/lib/action_text/attachments/caching.rb +2 -0
  20. data/lib/action_text/attachments/minification.rb +2 -0
  21. data/lib/action_text/attachments/trix_conversion.rb +2 -0
  22. data/lib/action_text/attribute.rb +61 -27
  23. data/lib/action_text/content.rb +49 -28
  24. data/lib/action_text/deprecator.rb +2 -0
  25. data/lib/action_text/encryption.rb +2 -0
  26. data/lib/action_text/engine.rb +7 -2
  27. data/lib/action_text/fixture_set.rb +35 -35
  28. data/lib/action_text/fragment.rb +4 -0
  29. data/lib/action_text/gem_version.rb +6 -4
  30. data/lib/action_text/html_conversion.rb +2 -0
  31. data/lib/action_text/plain_text_conversion.rb +65 -28
  32. data/lib/action_text/rendering.rb +2 -1
  33. data/lib/action_text/serialization.rb +2 -0
  34. data/lib/action_text/system_test_helper.rb +52 -33
  35. data/lib/action_text/trix_attachment.rb +2 -0
  36. data/lib/action_text/version.rb +3 -1
  37. data/lib/generators/action_text/install/install_generator.rb +4 -21
  38. data/lib/generators/action_text/install/templates/actiontext.css +414 -5
  39. data/lib/rails/generators/test_unit/install_generator.rb +2 -0
  40. data/package.json +3 -2
  41. metadata +28 -16
  42. data/app/assets/javascripts/trix.js +0 -13720
  43. data/app/assets/stylesheets/trix.css +0 -412
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/object/try"
4
6
  require "action_view/helpers/tags/placeholderable"
5
7
 
@@ -7,21 +9,32 @@ module ActionText
7
9
  module TagHelper
8
10
  cattr_accessor(:id, instance_accessor: false) { 0 }
9
11
 
10
- # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
11
- # that Trix will write to on changes, so the content will be sent on form submissions.
12
+ # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as
13
+ # well as a hidden field that Trix will write to on changes, so the content will
14
+ # be sent on form submissions.
15
+ #
16
+ # #### Options
17
+ # * `:class` - Defaults to "trix-content" so that default styles will be
18
+ # applied. Setting this to a different value will prevent default styles
19
+ # from being applied.
20
+ # * `[:data][:direct_upload_url]` - Defaults to `rails_direct_uploads_url`.
21
+ # * `[:data][:blob_url_template]` - Defaults to
22
+ # `rails_service_blob_url(":signed_id", ":filename")`.
23
+ #
12
24
  #
13
- # ==== Options
14
- # * <tt>:class</tt> - Defaults to "trix-content" so that default styles will be applied.
15
- # Setting this to a different value will prevent default styles from being applied.
16
- # * <tt>[:data][:direct_upload_url]</tt> - Defaults to +rails_direct_uploads_url+.
17
- # * <tt>[:data][:blob_url_template]</tt> - Defaults to <tt>rails_service_blob_url(":signed_id", ":filename")</tt>.
25
+ # #### Example
18
26
  #
19
- # ==== Example
27
+ # rich_textarea_tag "content", message.content
28
+ # # <input type="hidden" name="content" id="trix_input_post_1">
29
+ # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
20
30
  #
21
- # rich_text_area_tag "content", message.content
22
- # # <input type="hidden" name="content" id="trix_input_post_1">
23
- # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
24
- def rich_text_area_tag(name, value = nil, options = {})
31
+ # rich_textarea_tag "content", nil do
32
+ # "<h1>Default content</h1>"
33
+ # end
34
+ # # <input type="hidden" name="content" id="trix_input_post_1" value="&lt;h1&gt;Default content&lt;/h1&gt;">
35
+ # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
36
+ def rich_textarea_tag(name, value = nil, options = {}, &block)
37
+ value = capture(&block) if value.nil? && block_given?
25
38
  options = options.symbolize_keys
26
39
  form = options.delete(:form)
27
40
 
@@ -37,6 +50,7 @@ module ActionText
37
50
 
38
51
  input_tag + editor_tag
39
52
  end
53
+ alias_method :rich_text_area_tag, :rich_textarea_tag
40
54
  end
41
55
  end
42
56
 
@@ -46,48 +60,60 @@ module ActionView::Helpers
46
60
 
47
61
  delegate :dom_id, to: ActionView::RecordIdentifier
48
62
 
49
- def render
63
+ def render(&block)
50
64
  options = @options.stringify_keys
51
- add_default_name_and_id(options)
65
+ add_default_name_and_field(options)
52
66
  options["input"] ||= dom_id(object, [options["id"], :trix_input].compact.join("_")) if object
53
- html_tag = @template_object.rich_text_area_tag(options.delete("name"), options.fetch("value") { value }, options.except("value"))
67
+ html_tag = @template_object.rich_textarea_tag(options.delete("name"), options.fetch("value") { value }, options.except("value"), &block)
54
68
  error_wrapping(html_tag)
55
69
  end
56
70
  end
57
71
 
58
72
  module FormHelper
59
- # Returns a +trix-editor+ tag that instantiates the Trix JavaScript editor as well as a hidden field
60
- # that Trix will write to on changes, so the content will be sent on form submissions.
73
+ # Returns a `trix-editor` tag that instantiates the Trix JavaScript editor as
74
+ # well as a hidden field that Trix will write to on changes, so the content will
75
+ # be sent on form submissions.
76
+ #
77
+ # #### Options
78
+ # * `:class` - Defaults to "trix-content" which ensures default styling is
79
+ # applied.
80
+ # * `:value` - Adds a default value to the HTML input tag.
81
+ # * `[:data][:direct_upload_url]` - Defaults to `rails_direct_uploads_url`.
82
+ # * `[:data][:blob_url_template]` - Defaults to
83
+ # `rails_service_blob_url(":signed_id", ":filename")`.
84
+ #
61
85
  #
62
- # ==== Options
63
- # * <tt>:class</tt> - Defaults to "trix-content" which ensures default styling is applied.
64
- # * <tt>:value</tt> - Adds a default value to the HTML input tag.
65
- # * <tt>[:data][:direct_upload_url]</tt> - Defaults to +rails_direct_uploads_url+.
66
- # * <tt>[:data][:blob_url_template]</tt> - Defaults to <tt>rails_service_blob_url(":signed_id", ":filename")</tt>.
86
+ # #### Example
87
+ # rich_textarea :message, :content
88
+ # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
89
+ # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
67
90
  #
68
- # ==== Example
69
- # rich_text_area :message, :content
70
- # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
71
- # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
91
+ # rich_textarea :message, :content, value: "<h1>Default message</h1>"
92
+ # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="&lt;h1&gt;Default message&lt;/h1&gt;">
93
+ # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
72
94
  #
73
- # rich_text_area :message, :content, value: "<h1>Default message</h1>"
74
- # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="<h1>Default message</h1>">
75
- # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
76
- def rich_text_area(object_name, method, options = {})
77
- Tags::ActionText.new(object_name, method, self, options).render
95
+ # rich_textarea :message, :content do
96
+ # "<h1>Default message</h1>"
97
+ # end
98
+ # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="&lt;h1&gt;Default message&lt;/h1&gt;">
99
+ # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
100
+ def rich_textarea(object_name, method, options = {}, &block)
101
+ Tags::ActionText.new(object_name, method, self, options).render(&block)
78
102
  end
103
+ alias_method :rich_text_area, :rich_textarea
79
104
  end
80
105
 
81
106
  class FormBuilder
82
- # Wraps ActionView::Helpers::FormHelper#rich_text_area for form builders:
107
+ # Wraps ActionView::Helpers::FormHelper#rich_textarea for form builders:
83
108
  #
84
- # <%= form_with model: @message do |f| %>
85
- # <%= f.rich_text_area :content %>
86
- # <% end %>
109
+ # <%= form_with model: @message do |f| %>
110
+ # <%= f.rich_textarea :content %>
111
+ # <% end %>
87
112
  #
88
113
  # Please refer to the documentation of the base helper for details.
89
- def rich_text_area(method, options = {})
90
- @template.rich_text_area(@object_name, method, objectify_options(options))
114
+ def rich_textarea(method, options = {}, &block)
115
+ @template.rich_textarea(@object_name, method, objectify_options(options), &block)
91
116
  end
117
+ alias_method :rich_text_area, :rich_textarea
92
118
  end
93
119
  end
@@ -1,32 +1,86 @@
1
- import { DirectUpload } from "@rails/activestorage"
1
+ import { DirectUpload, dispatchEvent } from "@rails/activestorage"
2
2
 
3
3
  export class AttachmentUpload {
4
- constructor(attachment, element) {
4
+ constructor(attachment, element, file = attachment.file) {
5
5
  this.attachment = attachment
6
6
  this.element = element
7
- this.directUpload = new DirectUpload(attachment.file, this.directUploadUrl, this)
7
+ this.directUpload = new DirectUpload(file, this.directUploadUrl, this)
8
+ this.file = file
8
9
  }
9
10
 
10
11
  start() {
11
- this.directUpload.create(this.directUploadDidComplete.bind(this))
12
+ return new Promise((resolve, reject) => {
13
+ this.directUpload.create((error, attributes) => this.directUploadDidComplete(error, attributes, resolve, reject))
14
+ this.dispatch("start")
15
+ })
12
16
  }
13
17
 
14
18
  directUploadWillStoreFileWithXHR(xhr) {
15
19
  xhr.upload.addEventListener("progress", event => {
16
- const progress = event.loaded / event.total * 100
17
- this.attachment.setUploadProgress(progress)
20
+ // Scale upload progress to 0-90% range
21
+ const progress = (event.loaded / event.total) * 90
22
+ if (progress) {
23
+ this.dispatch("progress", { progress: progress })
24
+ }
25
+ })
26
+
27
+ // Start simulating progress after upload completes
28
+ xhr.upload.addEventListener("loadend", () => {
29
+ this.simulateResponseProgress(xhr)
18
30
  })
19
31
  }
20
32
 
21
- directUploadDidComplete(error, attributes) {
22
- if (error) {
23
- throw new Error(`Direct upload failed: ${error}`)
33
+ simulateResponseProgress(xhr) {
34
+ let progress = 90
35
+ const startTime = Date.now()
36
+
37
+ const updateProgress = () => {
38
+ // Simulate progress from 90% to 99% over estimated time
39
+ const elapsed = Date.now() - startTime
40
+ const estimatedResponseTime = this.estimateResponseTime()
41
+ const responseProgress = Math.min(elapsed / estimatedResponseTime, 1)
42
+ progress = 90 + (responseProgress * 9) // 90% to 99%
43
+
44
+ this.dispatch("progress", { progress })
45
+
46
+ // Continue until response arrives or we hit 99%
47
+ if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
48
+ requestAnimationFrame(updateProgress)
49
+ }
24
50
  }
25
51
 
26
- this.attachment.setAttributes({
27
- sgid: attributes.attachable_sgid,
28
- url: this.createBlobUrl(attributes.signed_id, attributes.filename)
52
+ // Stop simulation when response arrives
53
+ xhr.addEventListener("loadend", () => {
54
+ this.dispatch("progress", { progress: 100 })
29
55
  })
56
+
57
+ requestAnimationFrame(updateProgress)
58
+ }
59
+
60
+ estimateResponseTime() {
61
+ // Base estimate: 1 second for small files, scaling up for larger files
62
+ const fileSize = this.file.size
63
+ const MB = 1024 * 1024
64
+
65
+ if (fileSize < MB) {
66
+ return 1000 // 1 second for files under 1MB
67
+ } else if (fileSize < 10 * MB) {
68
+ return 2000 // 2 seconds for files 1-10MB
69
+ } else {
70
+ return 3000 + (fileSize / MB * 50) // 3+ seconds for larger files
71
+ }
72
+ }
73
+
74
+ directUploadDidComplete(error, attributes, resolve, reject) {
75
+ if (error) {
76
+ this.dispatchError(error, reject)
77
+ } else {
78
+ resolve({
79
+ sgid: attributes.attachable_sgid,
80
+ url: this.createBlobUrl(attributes.signed_id, attributes.filename)
81
+ })
82
+ this.dispatch("end")
83
+ }
30
84
  }
31
85
 
32
86
  createBlobUrl(signedId, filename) {
@@ -35,6 +89,18 @@ export class AttachmentUpload {
35
89
  .replace(":filename", encodeURIComponent(filename))
36
90
  }
37
91
 
92
+ dispatch(name, detail = {}) {
93
+ detail.attachment = this.attachment
94
+ return dispatchEvent(this.element, `direct-upload:${name}`, { detail })
95
+ }
96
+
97
+ dispatchError(error, reject) {
98
+ const event = this.dispatch("error", { error })
99
+ if (!event.defaultPrevented) {
100
+ reject(error)
101
+ }
102
+ }
103
+
38
104
  get directUploadUrl() {
39
105
  return this.element.dataset.directUploadUrl
40
106
  }
@@ -4,7 +4,16 @@ addEventListener("trix-attachment-add", event => {
4
4
  const { attachment, target } = event
5
5
 
6
6
  if (attachment.file) {
7
- const upload = new AttachmentUpload(attachment, target)
7
+ const upload = new AttachmentUpload(attachment, target, attachment.file)
8
+ const onProgress = event => attachment.setUploadProgress(event.detail.progress)
9
+
10
+ target.addEventListener("direct-upload:progress", onProgress)
11
+
8
12
  upload.start()
13
+ .then(attributes => attachment.setAttributes(attributes))
14
+ .catch(error => alert(error))
15
+ .finally(() => target.removeEventListener("direct-upload:progress", onProgress))
9
16
  }
10
17
  })
18
+
19
+ export { AttachmentUpload }
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  class EncryptedRichText < RichText
5
- self.table_name = "action_text_rich_texts"
6
-
7
7
  encrypts :body
8
8
  end
9
9
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  class Record < ActiveRecord::Base # :nodoc:
5
7
  self.abstract_class = true
@@ -1,55 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
- # = Action Text \RichText
6
+ # # Action Text RichText
5
7
  #
6
- # The RichText record holds the content produced by the Trix editor in a serialized +body+ attribute.
7
- # It also holds all the references to the embedded files, which are stored using Active Storage.
8
- # This record is then associated with the Active Record model the application desires to have
9
- # rich text content using the +has_rich_text+ class method.
8
+ # The RichText record holds the content produced by the Trix editor in a
9
+ # serialized `body` attribute. It also holds all the references to the embedded
10
+ # files, which are stored using Active Storage. This record is then associated
11
+ # with the Active Record model the application desires to have rich text content
12
+ # using the `has_rich_text` class method.
10
13
  #
11
- # class Message < ActiveRecord::Base
12
- # has_rich_text :content
13
- # end
14
+ # class Message < ActiveRecord::Base
15
+ # has_rich_text :content
16
+ # end
14
17
  #
15
- # message = Message.create!(content: "<h1>Funny times!</h1>")
16
- # message.content #=> #<ActionText::RichText....
17
- # message.content.to_s # => "<h1>Funny times!</h1>"
18
- # message.content.to_plain_text # => "Funny times!"
18
+ # message = Message.create!(content: "<h1>Funny times!</h1>")
19
+ # message.content #=> #<ActionText::RichText....
20
+ # message.content.to_s # => "<h1>Funny times!</h1>"
21
+ # message.content.to_plain_text # => "Funny times!"
19
22
  #
23
+ # message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
24
+ # message.content #=> #<ActionText::RichText....
25
+ # message.content.to_s # => "<div>safeunsafe</div>"
26
+ # message.content.to_plain_text # => "safeunsafe"
20
27
  class RichText < Record
21
- self.table_name = "action_text_rich_texts"
28
+ ##
29
+ # :method: to_s
30
+ #
31
+ # Safely transforms RichText into an HTML String.
32
+ #
33
+ # message = Message.create!(content: "<h1>Funny times!</h1>")
34
+ # message.content.to_s # => "<h1>Funny times!</h1>"
35
+ #
36
+ # message = Message.create!(content: "<div onclick='action()'>safe<script>unsafe</script></div>")
37
+ # message.content.to_s # => "<div>safeunsafe</div>"
22
38
 
23
39
  serialize :body, coder: ActionText::Content
24
40
  delegate :to_s, :nil?, to: :body
25
41
 
42
+ ##
43
+ # :method: record
44
+ #
45
+ # Returns the associated record.
26
46
  belongs_to :record, polymorphic: true, touch: true
47
+
48
+ ##
49
+ # :method: embeds
50
+ #
51
+ # Returns the ActiveStorage::Attachment records from the embedded files.
52
+ #
53
+ # Attached ActiveStorage::Blob records are extracted from the `body`
54
+ # in a {before_validation}[rdoc-ref:ActiveModel::Validations::Callbacks::ClassMethods#before_validation] callback.
27
55
  has_many_attached :embeds
28
56
 
29
- before_save do
57
+ before_validation do
30
58
  self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present?
31
59
  end
32
60
 
33
- # Returns the +body+ attribute as plain text with all HTML tags removed.
61
+ # Returns a plain-text version of the markup contained by the `body` attribute,
62
+ # with tags removed but HTML entities encoded.
63
+ #
64
+ # message = Message.create!(content: "<h1>Funny times!</h1>")
65
+ # message.content.to_plain_text # => "Funny times!"
66
+ #
67
+ # NOTE: that the returned string is not HTML safe and should not be rendered in
68
+ # browsers.
34
69
  #
35
- # message = Message.create!(content: "<h1>Funny times!</h1>")
36
- # message.content.to_plain_text # => "Funny times!"
70
+ # message = Message.create!(content: "&lt;script&gt;alert()&lt;/script&gt;")
71
+ # message.content.to_plain_text # => "<script>alert()</script>"
37
72
  def to_plain_text
38
73
  body&.to_plain_text.to_s
39
74
  end
40
75
 
41
- # Returns the +body+ attribute in a format that makes it editable in the Trix
76
+ # Returns the `body` attribute in a format that makes it editable in the Trix
42
77
  # editor. Previews of attachments are rendered inline.
43
78
  #
44
- # content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
45
- # message = Message.create!(content: content)
46
- # message.content.to_trix_html # =>
47
- # # <div class="trix-content">
48
- # # <h1>Funny times!</h1>
49
- # # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
50
- # # <img src="http://example.org/rails/active_storage/.../funny.jpg">
51
- # # </figure>
52
- # # </div>
79
+ # content = "<h1>Funny Times!</h1><figure data-trix-attachment='{\"sgid\":\"..."\}'></figure>"
80
+ # message = Message.create!(content: content)
81
+ # message.content.to_trix_html # =>
82
+ # # <div class="trix-content">
83
+ # # <h1>Funny times!</h1>
84
+ # # <figure data-trix-attachment='{\"sgid\":\"..."\}'>
85
+ # # <img src="http://example.org/rails/active_storage/.../funny.jpg">
86
+ # # </figure>
87
+ # # </div>
53
88
  def to_trix_html
54
89
  body&.to_trix_html
55
90
  end
@@ -20,6 +20,6 @@ class CreateActionTextTables < ActiveRecord::Migration[6.0]
20
20
  setting = config.options[config.orm][:primary_key_type]
21
21
  primary_key_type = setting || :primary_key
22
22
  foreign_key_type = setting || :bigint
23
- [primary_key_type, foreign_key_type]
23
+ [ primary_key_type, foreign_key_type ]
24
24
  end
25
25
  end
@@ -1,31 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
- # = Action Text \Attachable
6
+ # # Action Text Attachable
5
7
  #
6
8
  # Include this module to make a record attachable to an ActionText::Content.
7
9
  #
8
- # class Person < ApplicationRecord
9
- # include ActionText::Attachable
10
- # end
10
+ # class Person < ApplicationRecord
11
+ # include ActionText::Attachable
12
+ # end
11
13
  #
12
- # person = Person.create! name: "Javan"
13
- # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
14
- # content = ActionText::Content.new(html)
15
- # content.attachables # => [person]
14
+ # person = Person.create! name: "Javan"
15
+ # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
16
+ # content = ActionText::Content.new(html)
17
+ # content.attachables # => [person]
16
18
  module Attachable
17
19
  extend ActiveSupport::Concern
18
20
 
19
21
  LOCATOR_NAME = "attachable"
20
22
 
21
23
  class << self
22
- # Extracts the +ActionText::Attachable+ from the attachment HTML node:
24
+ # Extracts the `ActionText::Attachable` from the attachment HTML node:
23
25
  #
24
- # person = Person.create! name: "Javan"
25
- # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
26
- # fragment = ActionText::Fragment.wrap(html)
27
- # attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
28
- # ActionText::Attachable.from_node(attachment_node) # => person
26
+ # person = Person.create! name: "Javan"
27
+ # html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
28
+ # fragment = ActionText::Fragment.wrap(html)
29
+ # attachment_node = fragment.find_all(ActionText::Attachment.tag_name).first
30
+ # ActionText::Attachable.from_node(attachment_node) # => person
29
31
  def from_node(node)
30
32
  if attachable = attachable_from_sgid(node["sgid"])
31
33
  attachable
@@ -57,23 +59,23 @@ module ActionText
57
59
  ActionText::Attachable.from_attachable_sgid(sgid, only: self)
58
60
  end
59
61
 
60
- # Returns the path to the partial that is used for rendering missing attachables.
61
- # Defaults to "action_text/attachables/missing_attachable".
62
+ # Returns the path to the partial that is used for rendering missing
63
+ # attachables. Defaults to "action_text/attachables/missing_attachable".
62
64
  #
63
65
  # Override to render a different partial:
64
66
  #
65
- # class User < ApplicationRecord
66
- # def self.to_missing_attachable_partial_path
67
- # "users/missing_attachable"
67
+ # class User < ApplicationRecord
68
+ # def self.to_missing_attachable_partial_path
69
+ # "users/missing_attachable"
70
+ # end
68
71
  # end
69
- # end
70
72
  def to_missing_attachable_partial_path
71
73
  ActionText::Attachables::MissingAttachable::DEFAULT_PARTIAL_PATH
72
74
  end
73
75
  end
74
76
 
75
- # Returns the Signed Global ID for the attachable. The purpose of the ID is
76
- # set to 'attachable' so it can't be reused for other purposes.
77
+ # Returns the Signed Global ID for the attachable. The purpose of the ID is set
78
+ # to 'attachable' so it can't be reused for other purposes.
77
79
  def attachable_sgid
78
80
  to_sgid(expires_in: nil, for: LOCATOR_NAME).to_s
79
81
  end
@@ -98,30 +100,30 @@ module ActionText
98
100
  false
99
101
  end
100
102
 
101
- # Returns the path to the partial that is used for rendering the attachable
102
- # in Trix. Defaults to +to_partial_path+.
103
+ # Returns the path to the partial that is used for rendering the attachable in
104
+ # Trix. Defaults to `to_partial_path`.
103
105
  #
104
106
  # Override to render a different partial:
105
107
  #
106
- # class User < ApplicationRecord
107
- # def to_trix_content_attachment_partial_path
108
- # "users/trix_content_attachment"
108
+ # class User < ApplicationRecord
109
+ # def to_trix_content_attachment_partial_path
110
+ # "users/trix_content_attachment"
111
+ # end
109
112
  # end
110
- # end
111
113
  def to_trix_content_attachment_partial_path
112
114
  to_partial_path
113
115
  end
114
116
 
115
117
  # Returns the path to the partial that is used for rendering the attachable.
116
- # Defaults to +to_partial_path+.
118
+ # Defaults to `to_partial_path`.
117
119
  #
118
120
  # Override to render a different partial:
119
121
  #
120
- # class User < ApplicationRecord
121
- # def to_attachable_partial_path
122
- # "users/attachable"
122
+ # class User < ApplicationRecord
123
+ # def to_attachable_partial_path
124
+ # "users/attachable"
125
+ # end
123
126
  # end
124
- # end
125
127
  def to_attachable_partial_path
126
128
  to_partial_path
127
129
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class ContentAttachment # :nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class MissingAttachable
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionText
4
6
  module Attachables
5
7
  class RemoteImage