Paperclip Get dirty file before update - ruby-on-rails

Background:
I'm developing Rails Application and in some models I'm using paperclip gem to save attachments. In same models I'm using public_activity gem to track the model changes & I've prepared Restore functionality based on that to be able to Undo changes on that model with specific conditions.
Now using paperclip option :preserve_files => true it's simple to get the old file in case of delete, but in case of update, I don't know how.
Question:
public_activity gem is already using before_action callback and I can handle all dirty fields except Paperclip file update.
So how can I get the dirty updated file without changing my models or adding extra callbacks (because I'm using this achievement in multiple models and Undo functionality is generic).
Note:
I'm using File.exist?(paperclip_attachment.path) to check whether the file is still exist or no, and it returns false in case of update callback of public_activity (I think it is same as before_update callback).

I've did a simple way to get the dirty file with just edit Undo Functionality in public_activity update callback:
dirty_file_path = Dir.glob(File.join(File.dirname(paper_clip_attachment.path), '*.*')).max { |a,b| File.ctime(a) <=> File.ctime(b) }
paper_clip_attachment.path includes the path to the file, but the file itself is not yet created.
The code above just checks the container folder of the file paper_clip_attachment.path then checks the latest created file and save it's path to be used in Undo step.

Related

Copying / Cloning an ActiveStorage attribute

I want to clone an attached image to another model. Either as:
a reference, such that the attachment won't be deleted until all the referring objects are deleted
by cloning the attachment and having it as a distinct replica...
How does rails handle ActiveStorage attachments? Do they get deleted automatically when the model goes or does it need to be done manually? Is there a reference counting mechanism?
This worked for me in Rails 6.1.3
model
.new_file
.attach(io: StringIO.new(original_file.download),
filename: original_file.filename,
content_type: original_file.content_type)

How to delete files selectively in Carrierwave when uploading multiple files?

I'm uploading multiple files with Carrierwave gem as pointed out here: https://github.com/carrierwaveuploader/carrierwave#multiple-file-uploads
I can delete them all in one shot with no problem with user.remove_photos! being photos an array or json in the db; however, how do I delete selectively one or two out of the complete set of files previously uploaded?
#user.remove_photos! <--- removes all the photos
#user.save
#user.remove_photos[0]! <--- does not work
By the way, I'm using sqlite3 and in order to make it work you need to do the following tweaks:
rails g migration add_photos_to_users photos:string <-- note is string
and in your model definition, after mounting the uploader:
serialize :photos, JSON
both, not yet shown on the Readme, although the commit was already done by rusikf here: https://github.com/rusikf/carrierwave/commit/dedc9c341e34f2e0e57d47c3eec65347fef398bd

carrierwave: point to existing image

In my rails app, I'm using Carrierwave to upload images on Amazon S3. I'd like to point to existing Amazon S3 images without having to re-upload the image. For example, if I have an existing Amazon S3 image at http://test.s3.amazonaws.com/image/path/0001/image.jpg, can I update an image's path to point to this image? I don't want to use the remote upload option because I really just want to use the same exact image that's already there (but save it in my record's "path" attribute).
In the console, I've tried:
image.update_attributes(:path=> "http://test.s3.amazonaws.com/image/path/0001/image.jpg")
but this fails to override the image's path.
Chiming in, better late than never! Caveat: This is for rails 4, and I am testing on rails 4.1 only at the moment.
This is harder than it should be, methinks! The reason this was absolutely crucial to me was that I am attaching 100MB+ MP3 files, which I cannot receive on my host, due to CloudFlare SSL limitations (and common sense). Fortunately, AWS supports preauthorized uploads, and I got carrierwave to do the right thing for me:
Step 1: get carrierwave to tell me where it would store a file if it could:
m.raw_write_attribute('file','file.mp3');
url = m.file.url
signed = aws_presigned_url(url)
raw_write_attribute does not save anything, it just bypasses carrierwave when setting the value. This makes the object act as if it read 'file.mp3' out of the database. Then you can ask Carrierwave "where the file lives". I then upload the file directly from the client to S3. When that's done, I make another API call to Rails, which performs the following code:
m.raw_write_attribute('file','file.mp3');
m.update_attribute('file','file.mp3');
These two paired get around Carrierwave. The first makes carrierwave think that the 'file' column is set to 'file.mp3', the second explicitly tells rails to persist 'file.mp3' to the DB. Because of the raw_write_attribute call, Carrierwave allows the second through un-changed.
In my case update_column and update_columns worked great:
model.update_columns file_1: 'filename.txt'
Update column is with comma:
model.update_column :file_1, 'filename.txt'
This will not run any callback and set column to filename.txt.
When I do model.file_1.url I get the right S3 URL.
I am a bit late to the party, but you can use Active Record's raw_write_attribute method something like:
#image.raw_write_attribute(:path, "http://test.s3.amazonaws.com/image/path/0001/image.jpg")
I found that you can actually do this, for example if your mount_uploader is :path, then:
image.remote_path_url = "http://test.s3.amazonaws.com/image/path/0001/image.jpg"
image.save

Rails, CarrierWave, and Fog - Ignore Missing Files on Destroy or Overwrite

What is the correct way in Rails w CarrierWave using Fog to delete/destroy records when there are missing attachments/images?
I am trying to clean up a few records after a missing import of images to RackSpace. There are a few missing images and thumbs. When I try to delete a record I get a error
Fog::Storage::Rackspace::NotFound
Is there a CarrierWave or Fog setting to make it more tolerant to these kinds of scenarios?
I just ran into this issue and found the original issue filed here: https://github.com/jnicklas/carrierwave/issues/481 and the wiki page describing the fix here: https://github.com/jnicklas/carrierwave/wiki/How-To%3A-Silently-ignore-missing-files-on-destroy-or-overwrite
However I wasn't happy with the solution, I didn't want to have to add those 2 methods into all of my models that use an uploader. I tend to write 1 base uploader and subclass that for any changes to specific needs. So I dug into those methods: remove_#{column_name}! and remove_previously_stored_#{column_name} and found theme here: https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/mount.rb#L204 and https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/mount.rb#L204
Both of these methods just call remove! on the uploader. So the easiest way to fix the issue is to override the remove! method in the uploader. Then you only need to override one method and in 1 place. My overrride looks like the following:
class CloudfilesUploader < CarrierWave::Uploader::Base
# Override to silently ignore trying to remove missing previous file
def remove!
begin
super
rescue Fog::Storage::Rackspace::NotFound
end
end
end
That should solve your problems when trying to re-upload an image and overwrite an image that doesn't exist or when you just try and delete an image that doesn't exist.
~ Tom

How can I specify what files can be uploaded to the server in paperclip

Yes I know that paperclip has a validates_attachment_content_type, but I would really like to have it validate_by_file_extension ... ie ... I have an array of allowed file extensions in my app, and I'd like for paperclip to see if the file about to be uploaded has a file extension in that array, and if not I want it to not even start the upload and kick back an error.
How do I go about doing this.
you can define your own validation methods:
validate :validate_by_file_extension
def validate_by_file_extension
errors.add_to_base("Invalid file extension") unless ALLOWED_EXTENSIONS.include?(File.extname(attachment_file_name))
end
but you can't easily kick back before the start of the upload from rails, as in most cases by the time your rails controller action method gets called, the file has been streamed and a temp file has been created. You'd need to go higher on the stack to be able to stop things from even starting to upload.

Resources