rails admin + carrierwave updating breaks the image url - ruby-on-rails

I'm using Rails 5 with rails_admin and carrierwave gems.
I have a model Photo and and image uploader mounted on it (as per carrierwave documentation), looks roughly like this:
class Photo < ActiveRecord::Base
mount_uploader :image, ImageUploader
belongs_to :project
validates :name, presence: true
validates :image, presence: true
end
Given I have already some Photo objects created I can see a list of them in the rails_admin admin view.
And I start editing one of them
And I edit name
And I proceed to save it
Then rails admin fires some of it's magic and photo is being saved but after this action the image dissapears.
I have been digging a little in what request are being fired and rails_admin fires a PUT request with such params:
{
"authenticity_token"=>"xxx",
"photo"=>{
"name"=>"test2",
"description"=>"ewdeeweeefxxxwefwe",
"project_id"=>"3",
"image_cache"=>"",
"main"=>"0",
"about_us"=>"0"
},
"return_to"=>"http://localhost:3000/panel-admin/photo?model_name=photo", "_save"=>"", "model_name"=>"photo", "id"=>"29"}
and my Photo object is being update with not only name but also with image_url that of course overrides Image that already was mounted to the Photo
I have no idea why this is happening and how to prevent it.
Anyone might have encountered this issue and knows how to resolve it?

I found that. When I uncomment my custom filename method in uploader, it works well.

Related

Retrieve file from S3. - Carrierwave Rails Gem

I'm implementing Carrierwave with fog storage into my Rails App. The whole purpose of this app is to store pdf articles and have the ability for anyone with access to the app to retrieve them. Right now, I have the functionality of storing the article pdf working great. But, now I need to be able to retrieve the pdf from S3. Is this possible? I noticed in the docs there is a uploader.retrieve_from_store!("my_file.png") method. I tried to run this in the console and I got this error NoMethodError: undefined method retrieve_from_store! for ArticleUploader:Class Any help with this would be great! I'm just not finding any suitable answers so far. Thanks!
Article Uploader
class ArticleUploader < CarrierWave::Uploader::Base
storage :fog
def extension_whitelist
%w(jpg jpeg gif png pdf)
end
end
Article Model
class Article < ApplicationRecord
mount_uploader :file, ArticleUploader
validates :title, presence: :true
validates :publication_date, presence: :true
validates :source, presence: :true
end
You can access it with the methods that carrierwave has, in your case:
article = Article.find(1)
article.file.url
If you are on development it will output the path for the file and if you are on production and using S3, it will output the whole url, http://s3.amazonaws.com/<vendor>/articles/1/file.pdf for example.
You can find more information on the official docs:
https://github.com/carrierwaveuploader/carrierwave#activerecord
There is also an old rails cast on that http://railscasts.com/episodes/253-carrierwave-file-uploads

creating an alias_attribute for a paperclip image in Rails

I'm currently building an app which has a model Post. I'm using the paperclip gem to upload images, and everything is going well.
class Post < ActiveRecord::Base
has_attached_file :headerimage, styles: {banner => "400x300#"}
end
As you can see by my class above, if I were to get a Post object, I could get the banner image with the following in my view:
image = Post.first.headerimage(:banner)
Alias
However, in my app, it must have the image attribute image refer to the thumbnail image. So, in my models class, I wrote
class Post < ActiveRecord::Base
has_attached_file :headerimage, styles: {banner => "400x300#"}
alias_attribute :image, :headerimage
end
which allows me to get an image by calling the following:
image = Post.first.image
This is what I want - however, it gets the original image from paperclip, so it is equivalent to writing the following:
image = Post.first.headerimage instead of image = Post.first.headerimage(:banner)
How can I set up a proper alias_attribute to access the paperclip thumnail? I can't seem to find an answer anywhere else, and I am unsure how paperclip is actually working.
I thought I might logically be able to do something like
alias_attribute :image, :headerimage(:banner)
but that does not work.
You can just try this - basically call the original with arguments since aliases won't take parameter but we know the fixed parameter to pass:
alias :image, :headerimage
def headerimage(name=nil)
headerimage(name || :thumb)
end

CarrierWave gem isn't uploading, so why is there no raised exception?

I'm attempting to install CarrierWave gem on ActiveAdmin in rails, and the setup seemed easy enough. However, when I attempt to upload a test image to the /public/uploads directory, the image isn't saved. What's more irritating is the fact that there is no exception being raised, so I don't know where to look in order to find the issue. I can create a post, browse for an image, select that image, and submit the post in order to be saved, but I still end up with IMAGE: EMPTY on the show page in ActiveAdmin as shown below. In the image, I wrote a lorem ipsum post that included an image, and I saved it.
How to I actually get the uploader to upload?
ruby 1.9.3p547 (2014-05-14 revision 45962) [x86_64-darwin10.8.0]
Rails 4.1.6
This is the show page for a single Post object within ActiveAdmin
This is a full page screenshot of the form in question
The same form, but zoomed in. Obviously, I wasn't trying to upload an image in this screenshot.
/app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"public/uploads"
end
end
/app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :category
scope :rails, -> { where(category_id: 1) }
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
mount_uploader :image, ImageUploader
end
After a couple more hours of searching, I found the inspiration for an answer in another question, which you can find here.
The reason I wasn't getting a result at all was because I had not permitted the application to accept an :image in the app/admin/post.rb file. Now, I still don't know why there was no error raised (or even a line logged to the console), but fixing the problem of getting an upload is as simple as permitting the upload parameter as follows:
permit_params :title, :slug, :blurb, :content, :category_id, :image
When I posted this question, I had not added in that last :image, so my application was simply throwing away that parameter instead of saving it within the file system with the rest of the post.

Server side validation of file size

I am trying to find a way to limit the size of files that users can upload, and it seems to be possible with javascript, but what bothers me is that what happens if a user just turns javascript off and upload like 1GB file to the server, and I did find a way to validate it on server side but it takes place once the file has been uploaded, which isnt an option. So I am trying to find the right way to do it but the best thing I came up with is to create a form for file uploading with javascript and make validations on client side but I guess there is still a way around it...
I am using RoR and CarrierWave if that matters ...
EDIT
1)Everything I can find on this page is to use size validation which happens after the file has been uploaded
2) I use that validation by carrierwave but again, it takes place once the file has been uploaded, it doesnt make sense to wait an hour till some huge file gets uploaded just to know its too big
You can use a Rails custom validator to verify your attachment meets specific file size requirements.
Grab a copy of the validator from https://gist.github.com/1009861 and save it to your lib/ folder as file_size_validator.rb. Add the error translations to config/locales/en.yml or wherever is appropriate for your setup. Then do this in your parent model:
# app/models/brand.rb
require 'file_size_validator'
class Brand < ActiveRecord::Base
mount_uploader :logo, BrandLogoUploader
validates :logo,
:presence => true,
:file_size => {
:maximum => 0.5.megabytes.to_i
}
end
Like validates_length_of, validates_file_size accepts :maximum, :minimum, :in [range], and :is options.
Another solution
A custom validator could also be used like this.
app/models/user.rb
class User< ActiveRecord::Base
attr_accessible :product_upload_limit
has_many :products
end
app/models/brand.rb
class Product < ActiveRecord::Base
mount_uploader :file, FileUploader
belongs_to :user
validate :file_size
def file_size
if file.file.size.to_f/(1000*1000) > user.product_upload_limit.to_f
errors.add(:file, "You cannot upload a file greater than #{upload_limit.to_f}MB")
end
end
end
Here, the upload limit varies from user to user & is saved in the user model.
The problem was solved by nginx, which works like a buffer so if somebody is trying to upload a 1GB file, it will go to your buffer first so it won't block your unicorn instance until it's completely uploaded, so that's one good thing. The other is that you can set the max content-length parameter in your nginx config file, so if you set to 5 Megabytes and somebody is trying to upload 1 GB file, it'll be denied. But again, I am not sure how it works, if it just checks the http header, then I have a feeling that somebody can simply tamper with this value and fool your nginx.

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