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.
Related
I have a model as bellow:
class Note < Record
include Shared::ContentBasedModel
algoliasearch disable_indexing: AppConfig.apis.algolia.disable_indexing do
attributes :id, :key
[:keywords, :tags, :description, :summary].each do |attr|
attribute [attr] do
self.meta[attr.to_s]
end
end
attribute :content do
Nokogiri.HTML(self.meta["html"]).text.split(' ').reject { |i| i.to_s.length < 5 }.map(&:strip).join ' '
end
attribute :photo do
unless self.meta["images"].blank?
self.meta["images"].first["thumb"]
end
end
attribute :slug do
to_param
end
attribute :url do
Rails.application.routes.url_helpers.note_path(self)
end
end
end
I am using AlgoliaSearch gem to index my models into the Algolia's API and when I was trying to index the model with some long content I get the following error:
Error: Algolia::AlgoliaProtocolError (400: Cannot POST to https://XXXX.algolia.net/1/indexes/Note/batch: {"message":"Record at the position 1 objectID=56 is too big size=20715 bytes. Contact us if you need an extended quota","position":1,"objectID":"56","status":400} (400))
After this, I removed EVERYTHING as the following, BUT I am still getting the exact same error!!
class Note < Record
include Shared::ContentBasedModel
algoliasearch disable_indexing: AppConfig.apis.algolia.disable_indexing do
attributes :id
end
end
It seems that Rails does not update the cached models.
Envirnoment: production
Rails version: v6
Question: Why is this happening & how can I clear cached model?
Note: I have tried everything, including removing the tmp/cache folder but it does not go away!
It looks like the object's size itself is bigger than some max allowed size.
objectID=56 is too big size=20715 bytes
Contact https://www.algolia.com/ (as the suggest)
Contact us if you need an extended quota
How do you check your code? Are you entering in rails console on your server? Might it be that you run an old release instead of the new one, in the case if you use Capistrano or Mina for deploy?
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.
When using either Carrierwave or Paperclip everything works as expected with the exception of edit/update. When an image or file exists for a record it's set to nil on save if you don't explicitly upload another file.
What I've been looking for, but haven't found, is a solution that allows other form fields to be updated but keeps the existing file. Unless, of course, the file is updated as well.
My setup is using nested models so for both Carrierwave and Paperclip I've tried the following no success.
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-keep-%28not-replace%29-files-on-nested-edits
Apparently I'm missing something but I can't for the life of me figure out what.
Any help is greatly appreciated.
I use Paperclip with nested_form and it's straight forward, no surprises.
Did you make sure to check if your nested record is a new record or already existing? You only want to display the file_field if it's actually a new record, otherwise display the image (or anything but the file_field). However that's what your link already explained.
Additionally could you post which params are sent on submit - check your logs to do that.
Ok, so after some digging with pry I found a/the solution. First, here's the setup.
The models
class Machine < ActiveRecord::Base
has_many :parts
accepts_nested_attributes_for :parts
end
class Part < ActiveRecord::Base
belongs_to :machine
end
The form
= simple_form_for #machine do |m|
..... A bunch of inputs left out for brevity...
= m.simple_fields_for :parts do |p|
= p.input :name
= p.input :location
= p.input :price
= p.file_field :image
In the original Machine Controller - machine_posting_params
def machine_posting_params
params.require(:machine).permit(:name, part_attributes: [:name, :location, :price, :image])
end
In the working Machine Controller - machine_posting_params
def machine_posting_params
params.require(:machine).permit(:name, part_attributes: [:name, :location, :price, :image, :id])
end
The problem, which I couldn't find any documentation for, is that :id is being passed into the part_attributes by, I believe, carrierwave. When it isn't accounted for by part_attributes it causes carrierwave to dump the image reference on update.
So, if you have a nested form and your images keep disappearing on update try adding :id to xxxxx_attributs. It works for Carrierwave but I see no reason this wouldn't work for Paperclip. If I have time I'll switch and report back.
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.
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.