upload image to multiple models in rails - ruby-on-rails

I am using carrierwave and Minimagick gem to upload an attachment to S3. Now I want to save the some.pdf in two models(ie, assignment, and message). I give the same parameters in attachment field to save in two tables. But the second table attachment saves blurry. First one gets clear view of attachment.
My controller codes like,
#assignment = Assignment.new(assignment_params)
#message = Message.new
begin
Message.transaction do
asign_att = params[:assignment][:attachment]
#assignment.save!
#message.attachment = asign_att
#message.save!
end
end
My model has,
(in attachment.rb) mount_uploader :attachment, AttachmentUploader
(in message.rb) mount_uploader :attachment, ImageUploader
I want to save same file into two models with clear view. What I want to do? Thanks in advance.

Check in your second table uploader file if you have specified any version or something like that.
With version you can create clones of attachment in different resolutions like this.
version :thumb do
process resize_to_fit 50, 50
end

I would use a callback to do this, something like:
after_commit :assign_to_models
def assign_to_models
...
end
IMHO, I would create an model that has all the carrierwave attachements, and have it belongs both to message and attachement.
I hope this helps

Related

Carrierwave object.url VS object.image_url

In my Rails 5 app I am using Carrierwave to upload images.
I have to model that uses the same uploader:
account.rb:
mount_uploader :logo, ImageUploader
image.rb:
mount_uploader :image, ImageUploader
This uploads the file to:
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
The strange this now is that I can use:
#account.logo&.url(:thumb) // works!
#account.logo&.image_url(:thumb) // error!
But on the image model (one product has many images):
#product.images.first&.image_url(:thumb) // works!
#product.images.first&.url(:thumb) // error!
So in the first case I have to use .url and in the second one .image_url
An I have no idea why...any help?
The instance method image_url is defined dynamically based on the column that is passed to mount_uploader and simply calls url on the column. The definition looks like this...
def #{column}_url(*args)
#{column}.url(*args)
end
So, I would suspect that logo_url would work on #account (although I have not tested this)
source

ActiveStorage hook after analyze

I have pictures attached to a model. Those pictures are analyzed and the EXIF data is saved as metadata on the ActiveStorage::Blob.
class Foo < ApplicationRecord
has_one_attached :picture
end
There is an attribute on this model that I use for sorting the instances called order_date. This attribute has to be updated with the EXIF time after the blob got analyzed.
Using paperclip, a before_commit callback method was sufficient. With ActiveStorage, I also tried before_save and after_touch but both are not working.
How can I run code right after the ActiveStorage::AnalyzeJob has run successfully?
(I want to avoid monkey-patching ActiveStorage::AnalyzeJob, because it is also performed for other attachments.)
Thanks very much for your help!
I couldn't find anything official. I ended up overriding the analyze job since it is very simple anyways. It looks like below.
#/app/jobs/active_storage/analyze_job.rb
class ActiveStorage::AnalyzeJob < ActiveStorage::BaseJob
def perform(blob)
blob.analyze
blob.attachments.includes(:record).each do |attachment|
if attachment.record_type == 'Content'
record = attachment.record
record.set_file_info
record.save!
end
end
end
end

Migrating Carrierwave to ActiveStorage

I have an application using Carrierwave to handle file uploads but really love the simplicity of ActiveStorage. There are plenty of tutorials on migrating from Paperclip to ActiveStorage with the former sunsetting development, but I see nothing on migrating from Carrierwave to ActiveStorage. Has anyone successfully done the migration and could point me in the right direction?
The procedure is really simple actually.
step 1:
configure the active store bucket. try to use a different bucket than your carrierwave one
step 2:
configure your model in order to provide access to the ActiveStorage. example
class Photo < AR::Base
mount_uploader :file, FileUploader # this is the current carrierwave implementation. Don't remove it
has_one_attached :file_new # this will be your new file
end
Now you will have two implementations for the same model. carrierwave access at file and ActiveStorage at file_new
step 3:
download images from Carrierwave and save it to active storage
This can be implemented in a rake file, activeJob etc..
Photo.find_each do |photo|
begin
filename = File.basename(URI.parse(photo.fileurl))
photo.file_new.attach(io: open(photo.file.url), filename: d.file )
rescue => e
## log/handle your errors in order to retry later
end
end
At this point you will have one image on the carrierwave bucket and the newly created image on the active storage bucket!
(optional)
step 4
Once you are ready with the migration modify your model changing the active storage accessor and remove the carrierwave integration
class Photo < AR::Base
has_one_attached :file # we changed the atachment name from file_new to file
end
This is a convenience option so your integration in controllers and other places remain intact. hopefully!
 step 5
Update your records on active_storage_attachments table in order for the attachments be found as file and not file_new update column name from "file_new" to "file"
notes
Is possible to make some other tweaks to the application in order to handle things to consider
if your site will be running while you do the migration one way to fully operate would be implement active storage for new uploads then when you display images you can display active storage and carrierwave as a fallback
something like this in a helper:
photo.attached? ? url_for(photo.file_new) : photo.file.url
I hope this helps!
To begin with
You'll have to run this bundle exec rails active_storage:install
rails db:migrate
Replace mount_uploader :image, ImageUploader, to look like has_one_attached :image, in your model.
For rendering the image in the view, you should replace image_url with url_for(user.image).
You don't have to make any change to the controller code or in params, as the attribute image is already a strong parameter.
# user.rb
class User < ApplicationRecord
# mount_uploader :image, ImageUploader
has_one_attached :image
end
# show.html.erb
<%= image_tag url_for(user.image) %>
or
<%= image_tag user.image %>

Rails4 and Paperclip 4.0.2 callback turns as infinite loop

I set paperclip to My "Document" model, with a fairly standard config. It works well, but I want to separate the addtitional styles file generation inside a background job (using Resque).
(I would like to stop the creation of the :orginal style to to assign a file manually, but it doesn't seem to be possible)
So, to extract styles inside a background job, I first stop paperclip styles processors.
Then I call the reprocess! inside an after_save callback to generate them.
Doing this put the update action inside an infinite loop, and it is exactly when I call the reprocess!
Here is my Document model (simplified for understanding purpose)
class Document < ActiveRecord::Base
has_attached_file :attachment
before_post_process :stop_process
after_save :process_styles #same result with after_update (since user can only add attachment when updating profile)
# Kill all paperclip styles
#still generate the :orginal style. Block that too to copy it manually from remote folder would be nice
def stop_process
false
end
#call for the generation of the additional styles (:medium, :thumb)
def process_styles
Profile.processDocumentJob(id)
end
#will be a background job
def self.processDocumentJob(id)
document = Document.find(id)
document.attachment.reprocess!
document.save(validate: false)
end
end
Despite my document.save(validate: false), the process loop during the updating.
I tried after_update callback, tweak my code and conditions, with no success.
Thank you in advance for your precious help
I know this is an old question, but happened to me today and solved this way....a bit hacky btw... I'm not proud of my solution. This works in Rails 5.
Added a virtual attribute to my model "skip_callbacks"
Added a method to check if the attribute is true "should_skip_callbacks?"
In my callback added "unless: :should_skip_callbacks?" condition
Before the paperclip reprocess, set the virtual attribute "skip_callbacks" to true
Here's a simplified version of my model:
class Organization < ApplicationRecord
attr_accessor :skip_callbacks
has_attached_file :logo, styles: { header: "380x160>" }
validates_attachment :logo, content_type: {content_type: ['image/png','image/jpg','image/gif']}
after_save :reprocess_logo, unless: :should_skip_callbacks?
def should_skip_callbacks?
self.skip_callbacks
end
def reprocess_logo
self.skip_callbacks = true
self.logo.reprocess!
end
end

How to make localized Paperclip attachments with Globalize3?

I have a project using Paperclip gem for attachments and Globalize3 for attribute translation. Records need to have a different attachment for each locale.
I though about moving Paperclip attributes to translation table, and that might work, but I don't think that would work when Paperclip needs to delete attachments.
What's the best way to achieve something like that?
UPDATE: to be clear, I want this because my client wants to upload different images for each locale.
Unfortunately I didn't find a way to do this using Globalize3. In theory, I could have added a separate model for image and add image_id to list of translated columns (to have something like MainModel -> Translation -> Image), but it seems that Globalize has some migration issues with non-string columns.
Instead of using Globalize3, I did this with a separate Image model with locale attribute and main model which accepts nested attributes for it. Something along the lines of:
class MainModel < ActiveRecord::Base
has_many :main_model_images
accepts_nested_attributes_for :main_model_images
# return image for locale or any other as a fallback
def localized_image(locale)
promo_box_images.where(:locale => locale).first || promo_box_images.first
end
end
class MainModelImage < ActiveRecord::Base
belongs_to :main_model
has_attached_file :image
validates :locale,
:presence => true,
:uniqueness => { :scope => :main_model_id }
end
Tricky part was getting form to accept nested attributes only for one image, instead of all images in has_many relation.
=f.fields_for :main_model_images, #main_model.image_for_locale(I18n.locale) do |f_image|
=f_image.hidden_field :locale
=f_image.label :image
You could also try the paperclip-globalize3 gem, it should handle the case you describe. https://github.com/emjot/paperclip-globalize3
Ok since you asked me to share my solution to this problem even though I am using Carrierwave as a library for uploading here is it:
Ok so I would have a model setup like this:
class MyModel < ActiveRecord::Base
# ...
translates :attr_one, :attr_two, :uploaded_file
Now what I need for CarrierWave to work is place to attach the uploader to and that can be done on the Translation model
Translation.mount_uploader :uploaded_file, FileUploader
end
Now for your question about deleting, I think though I haven't needed to do it but it should work as the README says it should but on the translation model. https://github.com/jnicklas/carrierwave#removing-uploaded-files
MyModel.first.translation.remove_uploaded_file!
I haven't taken a look at paperclip for a good 2 years and if this is not applicable knowledge I suggest you try out carrierwave.

Resources