Module: Collavre::Creative::Describable

Extended by:
ActiveSupport::Concern
Included in:
Collavre::Creative
Defined in:
app/models/collavre/creative/describable.rb

Instance Method Summary collapse

Instance Method Details

#attachment_node_html(blob) ⇒ Object

HTML for embedding a blob inline, branching on content type. The proxy path MUST match what extract_signed_ids_from_description scans and what the sanitizer allows.



68
69
70
71
72
73
74
75
76
77
78
# File 'app/models/collavre/creative/describable.rb', line 68

def attachment_node_html(blob)
  url = "/public-assets/blobs/#{blob.signed_id}/#{blob.filename.sanitized}"
  name = ERB::Util.html_escape(blob.filename.to_s)
  if blob.content_type.to_s.start_with?("image/")
    %(<img src="#{url}" alt="#{name}">)
  elsif blob.content_type.to_s.start_with?("video/")
    %(<video controls src="#{url}"></video>)
  else
    %(<a href="#{url}" download="#{name}" data-filesize="#{blob.byte_size}">#{name}</a>)
  end
end

#attachments_embeddable?Boolean

GitHub-synced creatives reject any description change (description_cannot_change_if_github_source), so embedding would raise and orphan the blob. Callers MUST check this before creating the blob.

Returns:

  • (Boolean)


45
46
47
# File 'app/models/collavre/creative/describable.rb', line 45

def attachments_embeddable?
  !effective_origin.github_markdown?
end

#creative_snippetObject



38
39
40
# File 'app/models/collavre/creative/describable.rb', line 38

def creative_snippet
  CGI.unescapeHTML(ActionController::Base.helpers.strip_tags(effective_origin.description || "")).truncate(24, omission: "...")
end

#effective_description(variation_id = nil, html = true) ⇒ Object

Linked Creative의 description을 안전하게 반환



25
26
27
28
29
30
31
32
33
34
35
36
# File 'app/models/collavre/creative/describable.rb', line 25

def effective_description(variation_id = nil, html = true)
  if variation_id.present?
    variation_tag = tags.find_by(label_id: variation_id)
    return variation_tag.value if variation_tag&.value.present?
  end
  description_val = origin_id.nil? ? description : origin.description
  if html
    description_val&.to_s || ""
  else
    ActionController::Base.helpers.strip_tags(description_val&.to_s || "")
  end
end

#embed_attachment_blob!(blob) ⇒ Object

Append an attachment node and save; after_save reconcile attaches the blob to creative.files. Linked creatives can’t change their own description (it lives on the origin), so embed on effective_origin —otherwise the save raises and orphans the blob.



53
54
55
56
57
58
59
60
61
62
63
# File 'app/models/collavre/creative/describable.rb', line 53

def embed_attachment_blob!(blob)
  target = effective_origin
  return target.embed_attachment_blob!(blob) unless target == self

  node = attachment_node_html(blob)
  new_html = "#{description}#{node}"
  # Markdown-mode creatives derive description from markdown_source; demote
  # to HTML so the embedded node is the persisted source of truth.
  self.content_type_input = "html" if data&.dig("content_type") == "markdown"
  update!(description: new_html)
end

#remove_attachment!(signed_id) ⇒ Object

Remove the attachment for ‘signed_id`. HTML is the source of truth, so strip the node and let after_save reconcile detach + safe-purge. A blob that’s attached but not embedded (legacy, not yet backfilled) is detached directly. Returns true if an attachment was present.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/models/collavre/creative/describable.rb', line 84

def remove_attachment!(signed_id)
  target = effective_origin
  return target.remove_attachment!(signed_id) unless target == self

  blob = ActiveStorage::Blob.find_signed(signed_id)
  return false unless blob

  attachment = files.attachments.find_by(blob_id: blob.id)
  return false unless attachment

  stripped = description_without_attachment_node(blob.signed_id)
  if stripped
    # Demote markdown -> html so the stripped HTML is the persisted source
    # of truth (mirrors embed_attachment_blob!).
    self.content_type_input = "html" if data&.dig("content_type") == "markdown"
    update!(description: stripped)
  else
    detach_and_maybe_purge(attachment)
  end
  true
end