Module: Shrine::Attacher::InstanceMethods

Included in:
Shrine::Attacher
Defined in:
lib/shrine/attacher.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#contextObject (readonly)

Returns options that are automatically forwarded to the uploader. Can be modified with additional data.


38
39
40
# File 'lib/shrine/attacher.rb', line 38

def context
  @context
end

#fileObject

Returns the attached uploaded file.


34
35
36
# File 'lib/shrine/attacher.rb', line 34

def file
  @file
end

Instance Method Details

#assign(value, **options) ⇒ Object

Calls #attach_cached, but skips if value is an empty string (this is useful when the uploaded file comes from form fields). Forwards any additional options to #attach_cached.

attacher.assign(File.open(...))
attacher.assign(File.open(...), metadata: { "foo" => "bar" })
attacher.assign('{"id":"...","storage":"cache","metadata":{...}}')
attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} })

# ignores the assignment when a blank string is given
attacher.assign("")

70
71
72
73
74
75
76
77
78
# File 'lib/shrine/attacher.rb', line 70

def assign(value, **options)
  return if value == "" # skip empty hidden field

  if value.is_a?(Hash) || value.is_a?(String)
    return if uploaded_file(value) == file # skip assignment for current file
  end

  attach_cached(value, **options)
end

#attach(io, storage: store_key, **options) ⇒ Object

Uploads given IO object and changes the uploaded file.

# uploads the file to permanent storage
attacher.attach(io)

# uploads the file to specified storage
attacher.attach(io, storage: :other_store)

# forwards additional options to the uploader
attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" })

# removes the attachment
attacher.attach(nil)

116
117
118
119
120
# File 'lib/shrine/attacher.rb', line 116

def attach(io, storage: store_key, **options)
  file = upload(io, storage, **options) if io

  change(file)
end

#attach_cached(value, **options) ⇒ Object

Sets an existing cached file, or uploads an IO object to temporary storage and sets it via #attach. Forwards any additional options to #attach.

# upload file to temporary storage and set the uploaded file.
attacher.attach_cached(File.open(...))

# foward additional options to the uploader
attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" })

# sets an existing cached file from JSON data
attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}')

# sets an existing cached file from Hash data
attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} })

95
96
97
98
99
100
101
# File 'lib/shrine/attacher.rb', line 95

def attach_cached(value, **options)
  if value.is_a?(String) || value.is_a?(Hash)
    change(cached(value, **options))
  else
    attach(value, storage: cache_key, action: :cache, **options)
  end
end

#attached?Boolean

Returns whether a file is attached.

attacher.attach(io)
attacher.attached? #=> true

attacher.attach(nil)
attacher.attached? #=> false

Returns:

  • (Boolean)

272
273
274
# File 'lib/shrine/attacher.rb', line 272

def attached?
  !!file
end

#cacheObject

Returns the uploader that is used for the temporary storage.


55
# File 'lib/shrine/attacher.rb', line 55

def cache; shrine_class.new(cache_key); end

#cache_keyObject

Returns the temporary storage identifier.


50
# File 'lib/shrine/attacher.rb', line 50

def cache_key; @cache.to_sym; end

#cached?(file = self.file) ⇒ Boolean

Returns whether the file is uploaded to temporary storage.

attacher.cached?       # checks current file
attacher.cached?(file) # checks given file

Returns:

  • (Boolean)

280
281
282
# File 'lib/shrine/attacher.rb', line 280

def cached?(file = self.file)
  uploaded?(file, cache_key)
end

#change(file) ⇒ Object

Sets the uploaded file with dirty tracking, and runs validations.

attacher.change(uploaded_file)
attacher.file #=> #<Shrine::UploadedFile>
attacher.changed? #=> true

219
220
221
222
# File 'lib/shrine/attacher.rb', line 219

def change(file)
  @previous = dup if change?(file)
  set(file)
end

#changed?Boolean

Returns whether the attachment has changed.

attacher.changed? #=> false
attacher.attach(file)
attacher.changed? #=> true

Returns:

  • (Boolean)

261
262
263
# File 'lib/shrine/attacher.rb', line 261

def changed?
  !!@previous
end

#dataObject

Generates serializable data for the attachment.

attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } }

295
296
297
# File 'lib/shrine/attacher.rb', line 295

def data
  file&.data
end

#destroyObject

Destroys the attachment.

attacher.file.exists? #=> true
attacher.destroy
attacher.file.exists? #=> false

210
211
212
# File 'lib/shrine/attacher.rb', line 210

def destroy
  file&.delete
end

#destroy_attachedObject

Destroys the attached file if it exists and is uploaded to permanent storage.

attacher.file.exists? #=> true
attacher.destroy_attached
attacher.file.exists? #=> false

201
202
203
# File 'lib/shrine/attacher.rb', line 201

def destroy_attached
  destroy if destroy?
end

#destroy_previousObject

If a new file was attached, deletes previously attached file if any.

previous_file = attacher.file
attacher.attach(file)
attacher.destroy_previous
previous_file.exists? #=> false

191
192
193
# File 'lib/shrine/attacher.rb', line 191

def destroy_previous
  @previous.destroy_attached if changed?
end

#file!Object

Returns attached file or raises an exception if no file is attached.


321
322
323
# File 'lib/shrine/attacher.rb', line 321

def file!
  file or fail Error, "no file is attached"
end

#finalizeObject

Deletes any previous file and promotes newly attached cached file. It also clears any dirty tracking.

# promoting cached file
attacher.assign(io)
attacher.cached? #=> true
attacher.finalize
attacher.stored?

# deleting previous file
previous_file = attacher.file
previous_file.exists? #=> true
attacher.assign(io)
attacher.finalize
previous_file.exists? #=> false

# clearing dirty tracking
attacher.assign(io)
attacher.changed? #=> true
attacher.finalize
attacher.changed? #=> false

143
144
145
146
147
# File 'lib/shrine/attacher.rb', line 143

def finalize
  destroy_previous
  promote_cached
  @previous = nil
end

#getObject

Returns the attached file.

# when a file is attached
attacher.get #=> #<Shrine::UploadedFile>

# when no file is attached
attacher.get #=> nil

240
241
242
# File 'lib/shrine/attacher.rb', line 240

def get
  file
end

#initialize(file: nil, cache: :cache, store: :store) ⇒ Object

Initializes the attached file, temporary and permanent storage.


41
42
43
44
45
46
47
# File 'lib/shrine/attacher.rb', line 41

def initialize(file: nil, cache: :cache, store: :store)
  @file     = file
  @cache    = cache
  @store    = store
  @context  = {}
  @previous = nil
end

#load_data(data) ⇒ Object

Loads the uploaded file from data generated by `Attacher#data`.

attacher.file #=> nil
attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } })
attacher.file #=> #<Shrine::UploadedFile>

304
305
306
# File 'lib/shrine/attacher.rb', line 304

def load_data(data)
  @file = data && uploaded_file(data)
end

#promote(storage: store_key, **options) ⇒ Object

Uploads current file to permanent storage and sets the stored file.

attacher.cached? #=> true
attacher.promote
attacher.stored? #=> true

170
171
172
# File 'lib/shrine/attacher.rb', line 170

def promote(storage: store_key, **options)
  set upload(file, storage, action: :store, **options)
end

#promote_cached(**options) ⇒ Object

If a new cached file has been attached, uploads it to permanent storage. Any additional options are forwarded to #promote.

attacher.assign(io)
attacher.cached? #=> true
attacher.promote_cached
attacher.stored? #=> true

161
162
163
# File 'lib/shrine/attacher.rb', line 161

def promote_cached(**options)
  promote(**options) if promote?
end

#saveObject

Plugins can override this if they want something to be done in a “before save” callback.


151
152
# File 'lib/shrine/attacher.rb', line 151

def save
end

#set(file) ⇒ Object

Sets the uploaded file.

attacher.set(uploaded_file)
attacher.file #=> #<Shrine::UploadedFile>
attacher.changed? #=> false

229
230
231
# File 'lib/shrine/attacher.rb', line 229

def set(file)
  self.file = file
end

#shrine_classObject

Returns the Shrine class that this attacher's class is namespaced under.


338
339
340
# File 'lib/shrine/attacher.rb', line 338

def shrine_class
  self.class.shrine_class
end

#storeObject

Returns the uploader that is used for the permanent storage.


57
# File 'lib/shrine/attacher.rb', line 57

def store; shrine_class.new(store_key); end

#store_keyObject

Returns the permanent storage identifier.


52
# File 'lib/shrine/attacher.rb', line 52

def store_key; @store.to_sym; end

#stored?(file = self.file) ⇒ Boolean

Returns whether the file is uploaded to permanent storage.

attacher.stored?       # checks current file
attacher.stored?(file) # checks given file

Returns:

  • (Boolean)

288
289
290
# File 'lib/shrine/attacher.rb', line 288

def stored?(file = self.file)
  uploaded?(file, store_key)
end

#upload(io, storage = store_key, **options) ⇒ Object

Delegates to `Shrine.upload`, passing the #context.

# upload file to specified storage
attacher.upload(io, :store) #=> #<Shrine::UploadedFile>

# pass additional options for the uploader
attacher.upload(io, :store, metadata: { "foo" => "bar" })

181
182
183
# File 'lib/shrine/attacher.rb', line 181

def upload(io, storage = store_key, **options)
  shrine_class.upload(io, storage, **context, **options)
end

#uploaded_file(value) ⇒ Object

Converts JSON or Hash data into a Shrine::UploadedFile object.

attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}')
#=> #<Shrine::UploadedFile ...>

attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} })
#=> #<Shrine::UploadedFile ...>

332
333
334
# File 'lib/shrine/attacher.rb', line 332

def uploaded_file(value)
  shrine_class.uploaded_file(value)
end

#url(**options) ⇒ Object

If a file is attached, returns the uploaded file URL, otherwise returns nil. Any options are forwarded to the storage.

attacher.file = file
attacher.url #=> "https://..."

attacher.file = nil
attacher.url #=> nil

252
253
254
# File 'lib/shrine/attacher.rb', line 252

def url(**options)
  file&.url(**options)
end