Module: Plutonium::Attachments
- Defined in:
- lib/plutonium/attachments.rb
Overview
Staging and revival for attachments that travel as PLAIN STRINGS — backend-agnostic and model-free.
Two subsystems need this and neither has a model to hang an attachment off
while the work is in flight: a wizard stages a file across steps, and an
async interaction has to carry one from the request into a job. Both hold a
bare attribute :photo, :string, so the value has to BE a string.
Two shapes exist, and they are distinguishable — ActiveStorage's signed_id is opaque, Shrine's cached-file data parses as JSON — so each revives through its own backend with no model and no per-field configuration.
Revival is deliberately separate from staging. Attachments.stage_upload runs in the request, where the file is; Attachments.resolve runs wherever the value is read, which may be a render or a job. Assigning a token straight to a model attachment (both backends accept one) needs neither.
Backend objects answer DIFFERENT method names (filename/content_type vs
original_filename/mime_type), so a resolved token is wrapped in
Resolved, a uniform view over both.
Defined Under Namespace
Classes: Resolved
Constant Summary collapse
- STAGING_ONLY_INPUT_OPTIONS =
Options on a file input that are consumed SERVER-SIDE to stage the upload, and are never form or HTML concerns.
They have to be kept out of what the form renders: Phlex rejects a Class-valued
uploader:as an attribute outright, andbackend:would leak as a stray one. Both the wizard (Wizard::StepAdapter) and an interaction's form read this list, so the two cannot drift. %i[backend uploader].freeze
Class Method Summary collapse
-
.default_backend ⇒ Object
The default staging backend when a caller names none: the globally configured one, else auto-detected (active_shrine loaded → Shrine, else ActiveStorage).
-
.resolve(value) ⇒ Array<Resolved>
Resolve a staged attachment token (or array of them) into uniform Resolved view(s).
-
.stage_upload(value, backend: nil, uploader: nil) ⇒ Object
SERVER-SIDE staging: turn a submitted attachment value into a token string to stage in
data, minting one from an uploaded file when needed. -
.validation_errors(value, backend: nil, uploader: nil) ⇒ Array<String>
Run the EFFECTIVE Shrine uploader's attacher validations against a staged token (or array of them), returning the validation messages — so a file that violates the uploader's
validate_*rules is rejected at the STEP (stage phase), not deferred toexecute's model assignment.
Class Method Details
.default_backend ⇒ Object
The default staging backend when a caller names none: the globally configured one, else auto-detected (active_shrine loaded → Shrine, else ActiveStorage).
Subsystems layer their own override on top of this rather than replacing it — see Wizard::Attachments.attachment_backend.
81 82 83 84 |
# File 'lib/plutonium/attachments.rb', line 81 def default_backend Plutonium.configuration. || (defined?(ActiveShrine) ? :shrine : :active_storage) end |
.resolve(value) ⇒ Array<Resolved>
Resolve a staged attachment token (or array of them) into uniform Resolved view(s).
43 44 45 |
# File 'lib/plutonium/attachments.rb', line 43 def resolve(value) Array(value).filter_map { |token| resolve_token(token) } end |
.stage_upload(value, backend: nil, uploader: nil) ⇒ Object
SERVER-SIDE staging: turn a submitted attachment value into a token string
to stage in data, minting one from an uploaded file when needed.
Handles every shape a step POST can carry for an attachment field:
- an already-minted token String (direct upload, or the hidden preview field on re-submit) → kept verbatim;
- an uploaded file (IO-like) → uploaded to the backend's cache, returning its token (an AS signed_id, or Shrine cached-file JSON);
- blank / no selection → nil (the caller drops the key so the previously staged token survives a Back/re-submit);
- an Array (multiple) → each element mapped, blanks dropped.
65 66 67 68 69 70 71 72 73 |
# File 'lib/plutonium/attachments.rb', line 65 def stage_upload(value, backend: nil, uploader: nil) if value.is_a?(Array) value.filter_map { |v| stage_upload(v, backend:, uploader:) }.presence elsif value.is_a?(String) value.presence elsif value.respond_to?(:read) upload_to_cache(value, backend || default_backend, uploader:) end end |
.validation_errors(value, backend: nil, uploader: nil) ⇒ Array<String>
Run the EFFECTIVE Shrine uploader's attacher validations against a staged
token (or array of them), returning the validation messages — so a file that
violates the uploader's validate_* rules is rejected at the STEP (stage
phase), not deferred to execute's model assignment.
The effective uploader is the field's uploader: if given, else base
Shrine — both of which may carry Attacher.validate rules. Returns []
when the field isn't Shrine-backed (ActiveStorage has no attacher here), when
nothing is staged, or when the effective uploader declares no validations.
100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/plutonium/attachments.rb', line 100 def validation_errors(value, backend: nil, uploader: nil) return [] unless (backend || default_backend).to_sym == :shrine klass = shrine_uploader(uploader) # Shrine's `validation` plugin is OPTIONAL — without it (or `validation_helpers`) # the Attacher has no `#errors` and nothing to enforce. Detect it up front so a # plugin-less app is a clean no-op, not a per-step rescued NoMethodError. return [] unless klass::Attacher.method_defined?(:errors) Array(value).flat_map { |token| token_validation_errors(klass, token) } end |