Methods
Public Class
Public Instance
- assign
- attach
- attach_cached
- attached?
- cache
- cache_key
- cached
- cached?
- change
- change?
- changed?
- context
- data
- destroy
- destroy?
- destroy_attached
- destroy_previous
- file
- file!
- file=
- finalize
- get
- initialize_copy
- load_data
- promote
- promote?
- promote_cached
- save
- set
- shrine_class
- store
- store_key
- stored?
- upload
- uploaded?
- uploaded_file
- url
Included modules
Attributes
Public Class methods
Initializes the attached file, temporary and permanent storage.
# File lib/shrine/attacher.rb 41 def initialize(file: nil, cache: :cache, store: :store) 42 @file = file 43 @cache = cache 44 @store = store 45 @context = {} 46 @previous = nil 47 end
Public Instance methods
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("")
# File lib/shrine/attacher.rb 70 def assign(value, **) 71 return if value == "" # skip empty hidden field 72 73 if value.is_a?(Hash) || value.is_a?(String) 74 return if uploaded_file(value) == file # skip assignment for current file 75 end 76 77 attach_cached(value, **) 78 end
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)
# File lib/shrine/attacher.rb 116 def attach(io, storage: store_key, **) 117 file = upload(io, storage, **) if io 118 119 change(file) 120 end
Sets an existing cached file, or uploads an IO object to temporary storage and sets it via attach. Forwards any additional options to
# 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" => {} })
# File lib/shrine/attacher.rb 95 def attach_cached(value, **) 96 if value.is_a?(String) || value.is_a?(Hash) 97 change(cached(value, **)) 98 else 99 attach(value, storage: cache_key, action: :cache, **) 100 end 101 end
Returns whether a file is attached.
attacher.attach(io) attacher.attached? #=> true attacher.attach(nil) attacher.attached? #=> false
# File lib/shrine/attacher.rb 272 def attached? 273 !!file 274 end
Returns the uploader that is used for the temporary storage.
# File lib/shrine/attacher.rb 55 def cache = shrine_class.new(cache_key) 56 # Returns the uploader that is used for the permanent storage. 57 def store = shrine_class.new(store_key) 58 59 # Calls #attach_cached, but skips if value is an empty string (this is 60 # useful when the uploaded file comes from form fields). Forwards any 61 # additional options to #attach_cached. 62 # 63 # attacher.assign(File.open(...)) 64 # attacher.assign(File.open(...), metadata: { "foo" => "bar" }) 65 # attacher.assign('{"id":"...","storage":"cache","metadata":{...}}') 66 # attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} }) 67 # 68 # # ignores the assignment when a blank string is given 69 # attacher.assign("") 70 def assign(value, **) 71 return if value == "" # skip empty hidden field 72 73 if value.is_a?(Hash) || value.is_a?(String) 74 return if uploaded_file(value) == file # skip assignment for current file 75 end 76 77 attach_cached(value, **) 78 end 79 80 # Sets an existing cached file, or uploads an IO object to temporary 81 # storage and sets it via #attach. Forwards any additional options to 82 # #attach. 83 # 84 # # upload file to temporary storage and set the uploaded file. 85 # attacher.attach_cached(File.open(...)) 86 # 87 # # foward additional options to the uploader 88 # attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" }) 89 # 90 # # sets an existing cached file from JSON data 91 # attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}') 92 # 93 # # sets an existing cached file from Hash data 94 # attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} }) 95 def attach_cached(value, **) 96 if value.is_a?(String) || value.is_a?(Hash) 97 change(cached(value, **)) 98 else 99 attach(value, storage: cache_key, action: :cache, **) 100 end 101 end 102 103 # Uploads given IO object and changes the uploaded file. 104 # 105 # # uploads the file to permanent storage 106 # attacher.attach(io) 107 # 108 # # uploads the file to specified storage 109 # attacher.attach(io, storage: :other_store) 110 # 111 # # forwards additional options to the uploader 112 # attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" }) 113 # 114 # # removes the attachment 115 # attacher.attach(nil) 116 def attach(io, storage: store_key, **) 117 file = upload(io, storage, **) if io 118 119 change(file) 120 end 121 122 # Deletes any previous file and promotes newly attached cached file. 123 # It also clears any dirty tracking. 124 # 125 # # promoting cached file 126 # attacher.assign(io) 127 # attacher.cached? #=> true 128 # attacher.finalize 129 # attacher.stored? 130 # 131 # # deleting previous file 132 # previous_file = attacher.file 133 # previous_file.exists? #=> true 134 # attacher.assign(io) 135 # attacher.finalize 136 # previous_file.exists? #=> false 137 # 138 # # clearing dirty tracking 139 # attacher.assign(io) 140 # attacher.changed? #=> true 141 # attacher.finalize 142 # attacher.changed? #=> false 143 def finalize 144 destroy_previous 145 promote_cached 146 @previous = nil 147 end 148 149 # Plugins can override this if they want something to be done in a 150 # "before save" callback. 151 def save 152 end 153 154 # If a new cached file has been attached, uploads it to permanent storage. 155 # Any additional options are forwarded to #promote. 156 # 157 # attacher.assign(io) 158 # attacher.cached? #=> true 159 # attacher.promote_cached 160 # attacher.stored? #=> true 161 def promote_cached(**) 162 promote(**) if promote? 163 end 164 165 # Uploads current file to permanent storage and sets the stored file. 166 # 167 # attacher.cached? #=> true 168 # attacher.promote 169 # attacher.stored? #=> true 170 def promote(storage: store_key, **) 171 set upload(file, storage, action: :store, **) 172 end 173 174 # Delegates to `Shrine.upload`, passing the #context. 175 # 176 # # upload file to specified storage 177 # attacher.upload(io, :store) #=> #<Shrine::UploadedFile> 178 # 179 # # pass additional options for the uploader 180 # attacher.upload(io, :store, metadata: { "foo" => "bar" }) 181 def upload(io, storage = store_key, **) 182 shrine_class.upload(io, storage, **context, **) 183 end 184 185 # If a new file was attached, deletes previously attached file if any. 186 # 187 # previous_file = attacher.file 188 # attacher.attach(file) 189 # attacher.destroy_previous 190 # previous_file.exists? #=> false 191 def destroy_previous 192 @previous.destroy_attached if changed? 193 end 194 195 # Destroys the attached file if it exists and is uploaded to permanent 196 # storage. 197 # 198 # attacher.file.exists? #=> true 199 # attacher.destroy_attached 200 # attacher.file.exists? #=> false 201 def destroy_attached 202 destroy if destroy? 203 end 204 205 # Destroys the attachment. 206 # 207 # attacher.file.exists? #=> true 208 # attacher.destroy 209 # attacher.file.exists? #=> false 210 def destroy 211 file&.delete 212 end 213 214 # Sets the uploaded file with dirty tracking, and runs validations. 215 # 216 # attacher.change(uploaded_file) 217 # attacher.file #=> #<Shrine::UploadedFile> 218 # attacher.changed? #=> true 219 def change(file) 220 @previous = dup if change?(file) 221 set(file) 222 end 223 224 # Sets the uploaded file. 225 # 226 # attacher.set(uploaded_file) 227 # attacher.file #=> #<Shrine::UploadedFile> 228 # attacher.changed? #=> false 229 def set(file) 230 self.file = file 231 end 232 233 # Returns the attached file. 234 # 235 # # when a file is attached 236 # attacher.get #=> #<Shrine::UploadedFile> 237 # 238 # # when no file is attached 239 # attacher.get #=> nil 240 def get 241 file 242 end 243 244 # If a file is attached, returns the uploaded file URL, otherwise returns 245 # nil. Any options are forwarded to the storage. 246 # 247 # attacher.file = file 248 # attacher.url #=> "https://..." 249 # 250 # attacher.file = nil 251 # attacher.url #=> nil 252 def url(**) 253 file&.url(**) 254 end 255 256 # Returns whether the attachment has changed. 257 # 258 # attacher.changed? #=> false 259 # attacher.attach(file) 260 # attacher.changed? #=> true 261 def changed? 262 !!@previous 263 end 264 265 # Returns whether a file is attached. 266 # 267 # attacher.attach(io) 268 # attacher.attached? #=> true 269 # 270 # attacher.attach(nil) 271 # attacher.attached? #=> false 272 def attached? 273 !!file 274 end 275 276 # Returns whether the file is uploaded to temporary storage. 277 # 278 # attacher.cached? # checks current file 279 # attacher.cached?(file) # checks given file 280 def cached?(file = self.file) 281 uploaded?(file, cache_key) 282 end 283 284 # Returns whether the file is uploaded to permanent storage. 285 # 286 # attacher.stored? # checks current file 287 # attacher.stored?(file) # checks given file 288 def stored?(file = self.file) 289 uploaded?(file, store_key) 290 end 291 292 # Generates serializable data for the attachment. 293 # 294 # attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } } 295 def data 296 file&.data 297 end 298 299 # Loads the uploaded file from data generated by `Attacher#data`. 300 # 301 # attacher.file #=> nil 302 # attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } }) 303 # attacher.file #=> #<Shrine::UploadedFile> 304 def load_data(data) 305 @file = data && uploaded_file(data) 306 end 307 308 # Saves the given uploaded file to an instance variable. 309 # 310 # attacher.file = uploaded_file 311 # attacher.file #=> #<Shrine::UploadedFile> 312 def file=(file) 313 unless file.is_a?(Shrine::UploadedFile) || file.nil? 314 fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}" 315 end 316 317 @file = file 318 end 319 320 # Returns attached file or raises an exception if no file is attached. 321 def file! 322 file or fail Error, "no file is attached" 323 end 324 325 # Converts JSON or Hash data into a Shrine::UploadedFile object. 326 # 327 # attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}') 328 # #=> #<Shrine::UploadedFile ...> 329 # 330 # attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} }) 331 # #=> #<Shrine::UploadedFile ...> 332 def uploaded_file(value) 333 shrine_class.uploaded_file(value) 334 end 335 336 # Returns the Shrine class that this attacher's class is namespaced 337 # under. 338 def shrine_class 339 self.class.shrine_class 340 end 341 342 private 343 344 # The copy constructor that's called on #dup and #clone 345 # We need to duplicate the context to prevent it from being shared 346 def initialize_copy(other) 347 super 348 @context = @context.dup 349 end 350 351 # Converts a String or Hash value into an UploadedFile object and ensures 352 # it's uploaded to temporary storage. 353 # 354 # # from JSON data 355 # attacher.cached('{"id":"...","storage":"cache","metadata":{...}}') 356 # #=> #<Shrine::UploadedFile> 357 # 358 # # from Hash data 359 # attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } }) 360 # #=> #<Shrine::UploadedFile> 361 def cached(value, **) 362 uploaded_file = uploaded_file(value) 363 364 # reject files not uploaded to temporary storage, because otherwise 365 # attackers could hijack other users' attachments 366 unless cached?(uploaded_file) 367 fail Shrine::Error, "expected cached file, got #{uploaded_file.inspect}" 368 end 369 370 uploaded_file 371 end 372 373 # Whether attached file should be uploaded to permanent storage. 374 def promote? 375 changed? && cached? 376 end 377 378 # Whether attached file should be deleted. 379 def destroy? 380 attached? && !cached? 381 end 382 383 # Whether assigning the given file is considered a change. 384 def change?(file) 385 @file != file 386 end 387 388 # Returns whether the file is uploaded to specified storage. 389 def uploaded?(file, storage_key) 390 file&.storage_key == storage_key 391 end 392 end 393 394 extend ClassMethods 395 include InstanceMethods 396 end
Returns the temporary storage identifier.
# File lib/shrine/attacher.rb 50 def cache_key = @cache.to_sym 51 # Returns the permanent storage identifier. 52 def store_key = @store.to_sym 53 54 # Returns the uploader that is used for the temporary storage. 55 def cache = shrine_class.new(cache_key) 56 # Returns the uploader that is used for the permanent storage. 57 def store = shrine_class.new(store_key) 58 59 # Calls #attach_cached, but skips if value is an empty string (this is 60 # useful when the uploaded file comes from form fields). Forwards any 61 # additional options to #attach_cached. 62 # 63 # attacher.assign(File.open(...)) 64 # attacher.assign(File.open(...), metadata: { "foo" => "bar" }) 65 # attacher.assign('{"id":"...","storage":"cache","metadata":{...}}') 66 # attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} }) 67 # 68 # # ignores the assignment when a blank string is given 69 # attacher.assign("") 70 def assign(value, **) 71 return if value == "" # skip empty hidden field 72 73 if value.is_a?(Hash) || value.is_a?(String) 74 return if uploaded_file(value) == file # skip assignment for current file 75 end 76 77 attach_cached(value, **) 78 end 79 80 # Sets an existing cached file, or uploads an IO object to temporary 81 # storage and sets it via #attach. Forwards any additional options to 82 # #attach. 83 # 84 # # upload file to temporary storage and set the uploaded file. 85 # attacher.attach_cached(File.open(...)) 86 # 87 # # foward additional options to the uploader 88 # attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" }) 89 # 90 # # sets an existing cached file from JSON data 91 # attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}') 92 # 93 # # sets an existing cached file from Hash data 94 # attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} }) 95 def attach_cached(value, **) 96 if value.is_a?(String) || value.is_a?(Hash) 97 change(cached(value, **)) 98 else 99 attach(value, storage: cache_key, action: :cache, **) 100 end 101 end 102 103 # Uploads given IO object and changes the uploaded file. 104 # 105 # # uploads the file to permanent storage 106 # attacher.attach(io) 107 # 108 # # uploads the file to specified storage 109 # attacher.attach(io, storage: :other_store) 110 # 111 # # forwards additional options to the uploader 112 # attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" }) 113 # 114 # # removes the attachment 115 # attacher.attach(nil) 116 def attach(io, storage: store_key, **) 117 file = upload(io, storage, **) if io 118 119 change(file) 120 end 121 122 # Deletes any previous file and promotes newly attached cached file. 123 # It also clears any dirty tracking. 124 # 125 # # promoting cached file 126 # attacher.assign(io) 127 # attacher.cached? #=> true 128 # attacher.finalize 129 # attacher.stored? 130 # 131 # # deleting previous file 132 # previous_file = attacher.file 133 # previous_file.exists? #=> true 134 # attacher.assign(io) 135 # attacher.finalize 136 # previous_file.exists? #=> false 137 # 138 # # clearing dirty tracking 139 # attacher.assign(io) 140 # attacher.changed? #=> true 141 # attacher.finalize 142 # attacher.changed? #=> false 143 def finalize 144 destroy_previous 145 promote_cached 146 @previous = nil 147 end 148 149 # Plugins can override this if they want something to be done in a 150 # "before save" callback. 151 def save 152 end 153 154 # If a new cached file has been attached, uploads it to permanent storage. 155 # Any additional options are forwarded to #promote. 156 # 157 # attacher.assign(io) 158 # attacher.cached? #=> true 159 # attacher.promote_cached 160 # attacher.stored? #=> true 161 def promote_cached(**) 162 promote(**) if promote? 163 end 164 165 # Uploads current file to permanent storage and sets the stored file. 166 # 167 # attacher.cached? #=> true 168 # attacher.promote 169 # attacher.stored? #=> true 170 def promote(storage: store_key, **) 171 set upload(file, storage, action: :store, **) 172 end 173 174 # Delegates to `Shrine.upload`, passing the #context. 175 # 176 # # upload file to specified storage 177 # attacher.upload(io, :store) #=> #<Shrine::UploadedFile> 178 # 179 # # pass additional options for the uploader 180 # attacher.upload(io, :store, metadata: { "foo" => "bar" }) 181 def upload(io, storage = store_key, **) 182 shrine_class.upload(io, storage, **context, **) 183 end 184 185 # If a new file was attached, deletes previously attached file if any. 186 # 187 # previous_file = attacher.file 188 # attacher.attach(file) 189 # attacher.destroy_previous 190 # previous_file.exists? #=> false 191 def destroy_previous 192 @previous.destroy_attached if changed? 193 end 194 195 # Destroys the attached file if it exists and is uploaded to permanent 196 # storage. 197 # 198 # attacher.file.exists? #=> true 199 # attacher.destroy_attached 200 # attacher.file.exists? #=> false 201 def destroy_attached 202 destroy if destroy? 203 end 204 205 # Destroys the attachment. 206 # 207 # attacher.file.exists? #=> true 208 # attacher.destroy 209 # attacher.file.exists? #=> false 210 def destroy 211 file&.delete 212 end 213 214 # Sets the uploaded file with dirty tracking, and runs validations. 215 # 216 # attacher.change(uploaded_file) 217 # attacher.file #=> #<Shrine::UploadedFile> 218 # attacher.changed? #=> true 219 def change(file) 220 @previous = dup if change?(file) 221 set(file) 222 end 223 224 # Sets the uploaded file. 225 # 226 # attacher.set(uploaded_file) 227 # attacher.file #=> #<Shrine::UploadedFile> 228 # attacher.changed? #=> false 229 def set(file) 230 self.file = file 231 end 232 233 # Returns the attached file. 234 # 235 # # when a file is attached 236 # attacher.get #=> #<Shrine::UploadedFile> 237 # 238 # # when no file is attached 239 # attacher.get #=> nil 240 def get 241 file 242 end 243 244 # If a file is attached, returns the uploaded file URL, otherwise returns 245 # nil. Any options are forwarded to the storage. 246 # 247 # attacher.file = file 248 # attacher.url #=> "https://..." 249 # 250 # attacher.file = nil 251 # attacher.url #=> nil 252 def url(**) 253 file&.url(**) 254 end 255 256 # Returns whether the attachment has changed. 257 # 258 # attacher.changed? #=> false 259 # attacher.attach(file) 260 # attacher.changed? #=> true 261 def changed? 262 !!@previous 263 end 264 265 # Returns whether a file is attached. 266 # 267 # attacher.attach(io) 268 # attacher.attached? #=> true 269 # 270 # attacher.attach(nil) 271 # attacher.attached? #=> false 272 def attached? 273 !!file 274 end 275 276 # Returns whether the file is uploaded to temporary storage. 277 # 278 # attacher.cached? # checks current file 279 # attacher.cached?(file) # checks given file 280 def cached?(file = self.file) 281 uploaded?(file, cache_key) 282 end 283 284 # Returns whether the file is uploaded to permanent storage. 285 # 286 # attacher.stored? # checks current file 287 # attacher.stored?(file) # checks given file 288 def stored?(file = self.file) 289 uploaded?(file, store_key) 290 end 291 292 # Generates serializable data for the attachment. 293 # 294 # attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } } 295 def data 296 file&.data 297 end 298 299 # Loads the uploaded file from data generated by `Attacher#data`. 300 # 301 # attacher.file #=> nil 302 # attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } }) 303 # attacher.file #=> #<Shrine::UploadedFile> 304 def load_data(data) 305 @file = data && uploaded_file(data) 306 end 307 308 # Saves the given uploaded file to an instance variable. 309 # 310 # attacher.file = uploaded_file 311 # attacher.file #=> #<Shrine::UploadedFile> 312 def file=(file) 313 unless file.is_a?(Shrine::UploadedFile) || file.nil? 314 fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}" 315 end 316 317 @file = file 318 end 319 320 # Returns attached file or raises an exception if no file is attached. 321 def file! 322 file or fail Error, "no file is attached" 323 end 324 325 # Converts JSON or Hash data into a Shrine::UploadedFile object. 326 # 327 # attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}') 328 # #=> #<Shrine::UploadedFile ...> 329 # 330 # attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} }) 331 # #=> #<Shrine::UploadedFile ...> 332 def uploaded_file(value) 333 shrine_class.uploaded_file(value) 334 end 335 336 # Returns the Shrine class that this attacher's class is namespaced 337 # under. 338 def shrine_class 339 self.class.shrine_class 340 end 341 342 private 343 344 # The copy constructor that's called on #dup and #clone 345 # We need to duplicate the context to prevent it from being shared 346 def initialize_copy(other) 347 super 348 @context = @context.dup 349 end 350 351 # Converts a String or Hash value into an UploadedFile object and ensures 352 # it's uploaded to temporary storage. 353 # 354 # # from JSON data 355 # attacher.cached('{"id":"...","storage":"cache","metadata":{...}}') 356 # #=> #<Shrine::UploadedFile> 357 # 358 # # from Hash data 359 # attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } }) 360 # #=> #<Shrine::UploadedFile> 361 def cached(value, **) 362 uploaded_file = uploaded_file(value) 363 364 # reject files not uploaded to temporary storage, because otherwise 365 # attackers could hijack other users' attachments 366 unless cached?(uploaded_file) 367 fail Shrine::Error, "expected cached file, got #{uploaded_file.inspect}" 368 end 369 370 uploaded_file 371 end 372 373 # Whether attached file should be uploaded to permanent storage. 374 def promote? 375 changed? && cached? 376 end 377 378 # Whether attached file should be deleted. 379 def destroy? 380 attached? && !cached? 381 end 382 383 # Whether assigning the given file is considered a change. 384 def change?(file) 385 @file != file 386 end 387 388 # Returns whether the file is uploaded to specified storage. 389 def uploaded?(file, storage_key) 390 file&.storage_key == storage_key 391 end 392 end 393 394 extend ClassMethods 395 include InstanceMethods 396 end
Converts a String or Hash value into an UploadedFile object and ensures it’s uploaded to temporary storage.
# from JSON data
attacher.cached('{"id":"...","storage":"cache","metadata":{...}}')
#=> #<Shrine::UploadedFile>
# from Hash data
attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } })
#=> #<Shrine::UploadedFile>
# File lib/shrine/attacher.rb 361 def cached(value, **) 362 uploaded_file = uploaded_file(value) 363 364 # reject files not uploaded to temporary storage, because otherwise 365 # attackers could hijack other users' attachments 366 unless cached?(uploaded_file) 367 fail Shrine::Error, "expected cached file, got #{uploaded_file.inspect}" 368 end 369 370 uploaded_file 371 end
Returns whether the file is uploaded to temporary storage.
attacher.cached? # checks current file attacher.cached?(file) # checks given file
# File lib/shrine/attacher.rb 280 def cached?(file = self.file) 281 uploaded?(file, cache_key) 282 end
Sets the uploaded file with dirty tracking, and runs validations.
attacher.change(uploaded_file) attacher.file #=> #<Shrine::UploadedFile> attacher.changed? #=> true
# File lib/shrine/attacher.rb 219 def change(file) 220 @previous = dup if change?(file) 221 set(file) 222 end
Whether assigning the given file is considered a change.
# File lib/shrine/attacher.rb 384 def change?(file) 385 @file != file 386 end
Returns whether the attachment has changed.
attacher.changed? #=> false attacher.attach(file) attacher.changed? #=> true
# File lib/shrine/attacher.rb 261 def changed? 262 !!@previous 263 end
Generates serializable data for the attachment.
attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } }
# File lib/shrine/attacher.rb 295 def data 296 file&.data 297 end
Destroys the attachment.
attacher.file.exists? #=> true attacher.destroy attacher.file.exists? #=> false
# File lib/shrine/attacher.rb 210 def destroy 211 file&.delete 212 end
Whether attached file should be deleted.
# File lib/shrine/attacher.rb 379 def destroy? 380 attached? && !cached? 381 end
Destroys the attached file if it exists and is uploaded to permanent storage.
attacher.file.exists? #=> true attacher.destroy_attached attacher.file.exists? #=> false
# File lib/shrine/attacher.rb 201 def destroy_attached 202 destroy if destroy? 203 end
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
# File lib/shrine/attacher.rb 191 def destroy_previous 192 @previous.destroy_attached if changed? 193 end
Returns attached file or raises an exception if no file is attached.
# File lib/shrine/attacher.rb 321 def file! 322 file or fail Error, "no file is attached" 323 end
Saves the given uploaded file to an instance variable.
attacher.file = uploaded_file attacher.file #=> #<Shrine::UploadedFile>
# File lib/shrine/attacher.rb 312 def file=(file) 313 unless file.is_a?(Shrine::UploadedFile) || file.nil? 314 fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}" 315 end 316 317 @file = file 318 end
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
# File lib/shrine/attacher.rb 143 def finalize 144 destroy_previous 145 promote_cached 146 @previous = nil 147 end
Returns the attached file.
# when a file is attached attacher.get #=> #<Shrine::UploadedFile> # when no file is attached attacher.get #=> nil
# File lib/shrine/attacher.rb 240 def get 241 file 242 end
The copy constructor that’s called on dup and clone We need to duplicate the context to prevent it from being shared
# File lib/shrine/attacher.rb 346 def initialize_copy(other) 347 super 348 @context = @context.dup 349 end
Loads the uploaded file from data generated by Attacher#data.
attacher.file #=> nil
attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } })
attacher.file #=> #<Shrine::UploadedFile>
# File lib/shrine/attacher.rb 304 def load_data(data) 305 @file = data && uploaded_file(data) 306 end
Uploads current file to permanent storage and sets the stored file.
attacher.cached? #=> true attacher.promote attacher.stored? #=> true
# File lib/shrine/attacher.rb 170 def promote(storage: store_key, **) 171 set upload(file, storage, action: :store, **) 172 end
Whether attached file should be uploaded to permanent storage.
# File lib/shrine/attacher.rb 374 def promote? 375 changed? && cached? 376 end
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
# File lib/shrine/attacher.rb 161 def promote_cached(**) 162 promote(**) if promote? 163 end
Plugins can override this if they want something to be done in a “before save” callback.
# File lib/shrine/attacher.rb 151 def save 152 end
Sets the uploaded file.
attacher.set(uploaded_file) attacher.file #=> #<Shrine::UploadedFile> attacher.changed? #=> false
# File lib/shrine/attacher.rb 229 def set(file) 230 self.file = file 231 end
Returns the Shrine class that this attacher’s class is namespaced under.
# File lib/shrine/attacher.rb 338 def shrine_class 339 self.class.shrine_class 340 end
# File lib/shrine/attacher.rb 57 def store = shrine_class.new(store_key) 58 59 # Calls #attach_cached, but skips if value is an empty string (this is 60 # useful when the uploaded file comes from form fields). Forwards any 61 # additional options to #attach_cached. 62 # 63 # attacher.assign(File.open(...)) 64 # attacher.assign(File.open(...), metadata: { "foo" => "bar" }) 65 # attacher.assign('{"id":"...","storage":"cache","metadata":{...}}') 66 # attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} }) 67 # 68 # # ignores the assignment when a blank string is given 69 # attacher.assign("") 70 def assign(value, **) 71 return if value == "" # skip empty hidden field 72 73 if value.is_a?(Hash) || value.is_a?(String) 74 return if uploaded_file(value) == file # skip assignment for current file 75 end 76 77 attach_cached(value, **) 78 end 79 80 # Sets an existing cached file, or uploads an IO object to temporary 81 # storage and sets it via #attach. Forwards any additional options to 82 # #attach. 83 # 84 # # upload file to temporary storage and set the uploaded file. 85 # attacher.attach_cached(File.open(...)) 86 # 87 # # foward additional options to the uploader 88 # attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" }) 89 # 90 # # sets an existing cached file from JSON data 91 # attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}') 92 # 93 # # sets an existing cached file from Hash data 94 # attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} }) 95 def attach_cached(value, **) 96 if value.is_a?(String) || value.is_a?(Hash) 97 change(cached(value, **)) 98 else 99 attach(value, storage: cache_key, action: :cache, **) 100 end 101 end 102 103 # Uploads given IO object and changes the uploaded file. 104 # 105 # # uploads the file to permanent storage 106 # attacher.attach(io) 107 # 108 # # uploads the file to specified storage 109 # attacher.attach(io, storage: :other_store) 110 # 111 # # forwards additional options to the uploader 112 # attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" }) 113 # 114 # # removes the attachment 115 # attacher.attach(nil) 116 def attach(io, storage: store_key, **) 117 file = upload(io, storage, **) if io 118 119 change(file) 120 end 121 122 # Deletes any previous file and promotes newly attached cached file. 123 # It also clears any dirty tracking. 124 # 125 # # promoting cached file 126 # attacher.assign(io) 127 # attacher.cached? #=> true 128 # attacher.finalize 129 # attacher.stored? 130 # 131 # # deleting previous file 132 # previous_file = attacher.file 133 # previous_file.exists? #=> true 134 # attacher.assign(io) 135 # attacher.finalize 136 # previous_file.exists? #=> false 137 # 138 # # clearing dirty tracking 139 # attacher.assign(io) 140 # attacher.changed? #=> true 141 # attacher.finalize 142 # attacher.changed? #=> false 143 def finalize 144 destroy_previous 145 promote_cached 146 @previous = nil 147 end 148 149 # Plugins can override this if they want something to be done in a 150 # "before save" callback. 151 def save 152 end 153 154 # If a new cached file has been attached, uploads it to permanent storage. 155 # Any additional options are forwarded to #promote. 156 # 157 # attacher.assign(io) 158 # attacher.cached? #=> true 159 # attacher.promote_cached 160 # attacher.stored? #=> true 161 def promote_cached(**) 162 promote(**) if promote? 163 end 164 165 # Uploads current file to permanent storage and sets the stored file. 166 # 167 # attacher.cached? #=> true 168 # attacher.promote 169 # attacher.stored? #=> true 170 def promote(storage: store_key, **) 171 set upload(file, storage, action: :store, **) 172 end 173 174 # Delegates to `Shrine.upload`, passing the #context. 175 # 176 # # upload file to specified storage 177 # attacher.upload(io, :store) #=> #<Shrine::UploadedFile> 178 # 179 # # pass additional options for the uploader 180 # attacher.upload(io, :store, metadata: { "foo" => "bar" }) 181 def upload(io, storage = store_key, **) 182 shrine_class.upload(io, storage, **context, **) 183 end 184 185 # If a new file was attached, deletes previously attached file if any. 186 # 187 # previous_file = attacher.file 188 # attacher.attach(file) 189 # attacher.destroy_previous 190 # previous_file.exists? #=> false 191 def destroy_previous 192 @previous.destroy_attached if changed? 193 end 194 195 # Destroys the attached file if it exists and is uploaded to permanent 196 # storage. 197 # 198 # attacher.file.exists? #=> true 199 # attacher.destroy_attached 200 # attacher.file.exists? #=> false 201 def destroy_attached 202 destroy if destroy? 203 end 204 205 # Destroys the attachment. 206 # 207 # attacher.file.exists? #=> true 208 # attacher.destroy 209 # attacher.file.exists? #=> false 210 def destroy 211 file&.delete 212 end 213 214 # Sets the uploaded file with dirty tracking, and runs validations. 215 # 216 # attacher.change(uploaded_file) 217 # attacher.file #=> #<Shrine::UploadedFile> 218 # attacher.changed? #=> true 219 def change(file) 220 @previous = dup if change?(file) 221 set(file) 222 end 223 224 # Sets the uploaded file. 225 # 226 # attacher.set(uploaded_file) 227 # attacher.file #=> #<Shrine::UploadedFile> 228 # attacher.changed? #=> false 229 def set(file) 230 self.file = file 231 end 232 233 # Returns the attached file. 234 # 235 # # when a file is attached 236 # attacher.get #=> #<Shrine::UploadedFile> 237 # 238 # # when no file is attached 239 # attacher.get #=> nil 240 def get 241 file 242 end 243 244 # If a file is attached, returns the uploaded file URL, otherwise returns 245 # nil. Any options are forwarded to the storage. 246 # 247 # attacher.file = file 248 # attacher.url #=> "https://..." 249 # 250 # attacher.file = nil 251 # attacher.url #=> nil 252 def url(**) 253 file&.url(**) 254 end 255 256 # Returns whether the attachment has changed. 257 # 258 # attacher.changed? #=> false 259 # attacher.attach(file) 260 # attacher.changed? #=> true 261 def changed? 262 !!@previous 263 end 264 265 # Returns whether a file is attached. 266 # 267 # attacher.attach(io) 268 # attacher.attached? #=> true 269 # 270 # attacher.attach(nil) 271 # attacher.attached? #=> false 272 def attached? 273 !!file 274 end 275 276 # Returns whether the file is uploaded to temporary storage. 277 # 278 # attacher.cached? # checks current file 279 # attacher.cached?(file) # checks given file 280 def cached?(file = self.file) 281 uploaded?(file, cache_key) 282 end 283 284 # Returns whether the file is uploaded to permanent storage. 285 # 286 # attacher.stored? # checks current file 287 # attacher.stored?(file) # checks given file 288 def stored?(file = self.file) 289 uploaded?(file, store_key) 290 end 291 292 # Generates serializable data for the attachment. 293 # 294 # attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } } 295 def data 296 file&.data 297 end 298 299 # Loads the uploaded file from data generated by `Attacher#data`. 300 # 301 # attacher.file #=> nil 302 # attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } }) 303 # attacher.file #=> #<Shrine::UploadedFile> 304 def load_data(data) 305 @file = data && uploaded_file(data) 306 end 307 308 # Saves the given uploaded file to an instance variable. 309 # 310 # attacher.file = uploaded_file 311 # attacher.file #=> #<Shrine::UploadedFile> 312 def file=(file) 313 unless file.is_a?(Shrine::UploadedFile) || file.nil? 314 fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}" 315 end 316 317 @file = file 318 end 319 320 # Returns attached file or raises an exception if no file is attached. 321 def file! 322 file or fail Error, "no file is attached" 323 end 324 325 # Converts JSON or Hash data into a Shrine::UploadedFile object. 326 # 327 # attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}') 328 # #=> #<Shrine::UploadedFile ...> 329 # 330 # attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} }) 331 # #=> #<Shrine::UploadedFile ...> 332 def uploaded_file(value) 333 shrine_class.uploaded_file(value) 334 end 335 336 # Returns the Shrine class that this attacher's class is namespaced 337 # under. 338 def shrine_class 339 self.class.shrine_class 340 end 341 342 private 343 344 # The copy constructor that's called on #dup and #clone 345 # We need to duplicate the context to prevent it from being shared 346 def initialize_copy(other) 347 super 348 @context = @context.dup 349 end 350 351 # Converts a String or Hash value into an UploadedFile object and ensures 352 # it's uploaded to temporary storage. 353 # 354 # # from JSON data 355 # attacher.cached('{"id":"...","storage":"cache","metadata":{...}}') 356 # #=> #<Shrine::UploadedFile> 357 # 358 # # from Hash data 359 # attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } }) 360 # #=> #<Shrine::UploadedFile> 361 def cached(value, **) 362 uploaded_file = uploaded_file(value) 363 364 # reject files not uploaded to temporary storage, because otherwise 365 # attackers could hijack other users' attachments 366 unless cached?(uploaded_file) 367 fail Shrine::Error, "expected cached file, got #{uploaded_file.inspect}" 368 end 369 370 uploaded_file 371 end 372 373 # Whether attached file should be uploaded to permanent storage. 374 def promote? 375 changed? && cached? 376 end 377 378 # Whether attached file should be deleted. 379 def destroy? 380 attached? && !cached? 381 end 382 383 # Whether assigning the given file is considered a change. 384 def change?(file) 385 @file != file 386 end 387 388 # Returns whether the file is uploaded to specified storage. 389 def uploaded?(file, storage_key) 390 file&.storage_key == storage_key 391 end 392 end
# File lib/shrine/attacher.rb 52 def store_key = @store.to_sym 53 54 # Returns the uploader that is used for the temporary storage. 55 def cache = shrine_class.new(cache_key) 56 # Returns the uploader that is used for the permanent storage. 57 def store = shrine_class.new(store_key) 58 59 # Calls #attach_cached, but skips if value is an empty string (this is 60 # useful when the uploaded file comes from form fields). Forwards any 61 # additional options to #attach_cached. 62 # 63 # attacher.assign(File.open(...)) 64 # attacher.assign(File.open(...), metadata: { "foo" => "bar" }) 65 # attacher.assign('{"id":"...","storage":"cache","metadata":{...}}') 66 # attacher.assign({ "id" => "...", "storage" => "cache", "metadata" => {} }) 67 # 68 # # ignores the assignment when a blank string is given 69 # attacher.assign("") 70 def assign(value, **) 71 return if value == "" # skip empty hidden field 72 73 if value.is_a?(Hash) || value.is_a?(String) 74 return if uploaded_file(value) == file # skip assignment for current file 75 end 76 77 attach_cached(value, **) 78 end 79 80 # Sets an existing cached file, or uploads an IO object to temporary 81 # storage and sets it via #attach. Forwards any additional options to 82 # #attach. 83 # 84 # # upload file to temporary storage and set the uploaded file. 85 # attacher.attach_cached(File.open(...)) 86 # 87 # # foward additional options to the uploader 88 # attacher.attach_cached(File.open(...), metadata: { "foo" => "bar" }) 89 # 90 # # sets an existing cached file from JSON data 91 # attacher.attach_cached('{"id":"...","storage":"cache","metadata":{...}}') 92 # 93 # # sets an existing cached file from Hash data 94 # attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} }) 95 def attach_cached(value, **) 96 if value.is_a?(String) || value.is_a?(Hash) 97 change(cached(value, **)) 98 else 99 attach(value, storage: cache_key, action: :cache, **) 100 end 101 end 102 103 # Uploads given IO object and changes the uploaded file. 104 # 105 # # uploads the file to permanent storage 106 # attacher.attach(io) 107 # 108 # # uploads the file to specified storage 109 # attacher.attach(io, storage: :other_store) 110 # 111 # # forwards additional options to the uploader 112 # attacher.attach(io, upload_options: { acl: "public-read" }, metadata: { "foo" => "bar" }) 113 # 114 # # removes the attachment 115 # attacher.attach(nil) 116 def attach(io, storage: store_key, **) 117 file = upload(io, storage, **) if io 118 119 change(file) 120 end 121 122 # Deletes any previous file and promotes newly attached cached file. 123 # It also clears any dirty tracking. 124 # 125 # # promoting cached file 126 # attacher.assign(io) 127 # attacher.cached? #=> true 128 # attacher.finalize 129 # attacher.stored? 130 # 131 # # deleting previous file 132 # previous_file = attacher.file 133 # previous_file.exists? #=> true 134 # attacher.assign(io) 135 # attacher.finalize 136 # previous_file.exists? #=> false 137 # 138 # # clearing dirty tracking 139 # attacher.assign(io) 140 # attacher.changed? #=> true 141 # attacher.finalize 142 # attacher.changed? #=> false 143 def finalize 144 destroy_previous 145 promote_cached 146 @previous = nil 147 end 148 149 # Plugins can override this if they want something to be done in a 150 # "before save" callback. 151 def save 152 end 153 154 # If a new cached file has been attached, uploads it to permanent storage. 155 # Any additional options are forwarded to #promote. 156 # 157 # attacher.assign(io) 158 # attacher.cached? #=> true 159 # attacher.promote_cached 160 # attacher.stored? #=> true 161 def promote_cached(**) 162 promote(**) if promote? 163 end 164 165 # Uploads current file to permanent storage and sets the stored file. 166 # 167 # attacher.cached? #=> true 168 # attacher.promote 169 # attacher.stored? #=> true 170 def promote(storage: store_key, **) 171 set upload(file, storage, action: :store, **) 172 end 173 174 # Delegates to `Shrine.upload`, passing the #context. 175 # 176 # # upload file to specified storage 177 # attacher.upload(io, :store) #=> #<Shrine::UploadedFile> 178 # 179 # # pass additional options for the uploader 180 # attacher.upload(io, :store, metadata: { "foo" => "bar" }) 181 def upload(io, storage = store_key, **) 182 shrine_class.upload(io, storage, **context, **) 183 end 184 185 # If a new file was attached, deletes previously attached file if any. 186 # 187 # previous_file = attacher.file 188 # attacher.attach(file) 189 # attacher.destroy_previous 190 # previous_file.exists? #=> false 191 def destroy_previous 192 @previous.destroy_attached if changed? 193 end 194 195 # Destroys the attached file if it exists and is uploaded to permanent 196 # storage. 197 # 198 # attacher.file.exists? #=> true 199 # attacher.destroy_attached 200 # attacher.file.exists? #=> false 201 def destroy_attached 202 destroy if destroy? 203 end 204 205 # Destroys the attachment. 206 # 207 # attacher.file.exists? #=> true 208 # attacher.destroy 209 # attacher.file.exists? #=> false 210 def destroy 211 file&.delete 212 end 213 214 # Sets the uploaded file with dirty tracking, and runs validations. 215 # 216 # attacher.change(uploaded_file) 217 # attacher.file #=> #<Shrine::UploadedFile> 218 # attacher.changed? #=> true 219 def change(file) 220 @previous = dup if change?(file) 221 set(file) 222 end 223 224 # Sets the uploaded file. 225 # 226 # attacher.set(uploaded_file) 227 # attacher.file #=> #<Shrine::UploadedFile> 228 # attacher.changed? #=> false 229 def set(file) 230 self.file = file 231 end 232 233 # Returns the attached file. 234 # 235 # # when a file is attached 236 # attacher.get #=> #<Shrine::UploadedFile> 237 # 238 # # when no file is attached 239 # attacher.get #=> nil 240 def get 241 file 242 end 243 244 # If a file is attached, returns the uploaded file URL, otherwise returns 245 # nil. Any options are forwarded to the storage. 246 # 247 # attacher.file = file 248 # attacher.url #=> "https://..." 249 # 250 # attacher.file = nil 251 # attacher.url #=> nil 252 def url(**) 253 file&.url(**) 254 end 255 256 # Returns whether the attachment has changed. 257 # 258 # attacher.changed? #=> false 259 # attacher.attach(file) 260 # attacher.changed? #=> true 261 def changed? 262 !!@previous 263 end 264 265 # Returns whether a file is attached. 266 # 267 # attacher.attach(io) 268 # attacher.attached? #=> true 269 # 270 # attacher.attach(nil) 271 # attacher.attached? #=> false 272 def attached? 273 !!file 274 end 275 276 # Returns whether the file is uploaded to temporary storage. 277 # 278 # attacher.cached? # checks current file 279 # attacher.cached?(file) # checks given file 280 def cached?(file = self.file) 281 uploaded?(file, cache_key) 282 end 283 284 # Returns whether the file is uploaded to permanent storage. 285 # 286 # attacher.stored? # checks current file 287 # attacher.stored?(file) # checks given file 288 def stored?(file = self.file) 289 uploaded?(file, store_key) 290 end 291 292 # Generates serializable data for the attachment. 293 # 294 # attacher.data #=> { "id" => "...", "storage" => "...", "metadata": { ... } } 295 def data 296 file&.data 297 end 298 299 # Loads the uploaded file from data generated by `Attacher#data`. 300 # 301 # attacher.file #=> nil 302 # attacher.load_data({ "id" => "...", "storage" => "...", "metadata" => { ... } }) 303 # attacher.file #=> #<Shrine::UploadedFile> 304 def load_data(data) 305 @file = data && uploaded_file(data) 306 end 307 308 # Saves the given uploaded file to an instance variable. 309 # 310 # attacher.file = uploaded_file 311 # attacher.file #=> #<Shrine::UploadedFile> 312 def file=(file) 313 unless file.is_a?(Shrine::UploadedFile) || file.nil? 314 fail ArgumentError, "expected file to be a Shrine::UploadedFile or nil, got #{file.inspect}" 315 end 316 317 @file = file 318 end 319 320 # Returns attached file or raises an exception if no file is attached. 321 def file! 322 file or fail Error, "no file is attached" 323 end 324 325 # Converts JSON or Hash data into a Shrine::UploadedFile object. 326 # 327 # attacher.uploaded_file('{"id":"...","storage":"...","metadata":{...}}') 328 # #=> #<Shrine::UploadedFile ...> 329 # 330 # attacher.uploaded_file({ "id" => "...", "storage" => "...", "metadata" => {} }) 331 # #=> #<Shrine::UploadedFile ...> 332 def uploaded_file(value) 333 shrine_class.uploaded_file(value) 334 end 335 336 # Returns the Shrine class that this attacher's class is namespaced 337 # under. 338 def shrine_class 339 self.class.shrine_class 340 end 341 342 private 343 344 # The copy constructor that's called on #dup and #clone 345 # We need to duplicate the context to prevent it from being shared 346 def initialize_copy(other) 347 super 348 @context = @context.dup 349 end 350 351 # Converts a String or Hash value into an UploadedFile object and ensures 352 # it's uploaded to temporary storage. 353 # 354 # # from JSON data 355 # attacher.cached('{"id":"...","storage":"cache","metadata":{...}}') 356 # #=> #<Shrine::UploadedFile> 357 # 358 # # from Hash data 359 # attacher.cached({ "id" => "...", "storage" => "cache", "metadata" => { ... } }) 360 # #=> #<Shrine::UploadedFile> 361 def cached(value, **) 362 uploaded_file = uploaded_file(value) 363 364 # reject files not uploaded to temporary storage, because otherwise 365 # attackers could hijack other users' attachments 366 unless cached?(uploaded_file) 367 fail Shrine::Error, "expected cached file, got #{uploaded_file.inspect}" 368 end 369 370 uploaded_file 371 end 372 373 # Whether attached file should be uploaded to permanent storage. 374 def promote? 375 changed? && cached? 376 end 377 378 # Whether attached file should be deleted. 379 def destroy? 380 attached? && !cached? 381 end 382 383 # Whether assigning the given file is considered a change. 384 def change?(file) 385 @file != file 386 end 387 388 # Returns whether the file is uploaded to specified storage. 389 def uploaded?(file, storage_key) 390 file&.storage_key == storage_key 391 end 392 end 393 394 extend ClassMethods 395 include InstanceMethods 396 end 397 end
Returns whether the file is uploaded to permanent storage.
attacher.stored? # checks current file attacher.stored?(file) # checks given file
# File lib/shrine/attacher.rb 288 def stored?(file = self.file) 289 uploaded?(file, store_key) 290 end
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" })
# File lib/shrine/attacher.rb 181 def upload(io, storage = store_key, **) 182 shrine_class.upload(io, storage, **context, **) 183 end
Returns whether the file is uploaded to specified storage.
# File lib/shrine/attacher.rb 389 def uploaded?(file, storage_key) 390 file&.storage_key == storage_key 391 end
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 ...>
# File lib/shrine/attacher.rb 332 def uploaded_file(value) 333 shrine_class.uploaded_file(value) 334 end
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
# File lib/shrine/attacher.rb 252 def url(**) 253 file&.url(**) 254 end