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.



73
74
75
76
77
78
79
80
81
82
83
# File 'app/models/collavre/creative/describable.rb', line 73

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)


50
51
52
# File 'app/models/collavre/creative/describable.rb', line 50

def attachments_embeddable?
  !effective_origin.github_markdown?
end

#creative_snippetObject



43
44
45
# File 'app/models/collavre/creative/describable.rb', line 43

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을 안전하게 반환



30
31
32
33
34
35
36
37
38
39
40
41
# File 'app/models/collavre/creative/describable.rb', line 30

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.



58
59
60
61
62
63
64
65
66
67
68
# File 'app/models/collavre/creative/describable.rb', line 58

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.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/models/collavre/creative/describable.rb', line 89

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