Rails paperclip default image only under certain conditions - ruby-on-rails

I've got a Lead model which is splited by lead_status param on products and offers. Only leads with product status should contain images and offers should not. I've migrated attached product_image table to schema and tried to set a default image only for products. Like this:
class Lead < ApplicationRecord
has_attached_file :product_image, styles: { small: "150x150>", default: "350x350"}
validates_attachment_content_type :product_image, content_type: /\Aimage\/.*\z/
before_save :product_image_default_url
def product_image_default_url
if self.lead_status == "product" && self.product_image.url.nil?
self.product_image.url = "/images/:style/default_user_avatar.png"
end
end
Every time when I save a new lead without uploaded image I get "/product_images/original/missing.png" as a default url. No matter which status it has.
Model doesn't recognize new leads by it's status
How can I change that? Force my Lead model to save a default image url according to a "product" status and ignore all those with "offer" status?
My rails version is 5.2.1 and paperclip 6.0.0

Try with following,
has_attached_file
:product_image,
styles: { small: "150x150>", default: "350x350"},
default_url: ":style/default_user_avatar.png"
# app/assets/images/medium/default_user_avatar.png
# app/assets/images/thumbs/default_user_avatar.png
Existing method is,
def default_url
if #attachment_options[:default_url].respond_to?(:call)
#attachment_options[:default_url].call(#attachment)
elsif #attachment_options[:default_url].is_a?(Symbol)
#attachment.instance.send(#attachment_options[:default_url])
else
#attachment_options[:default_url]
end
end
In initializer, Provide monkey patch for following,
require 'uri'
require 'active_support/core_ext/module/delegation'
module Paperclip
class UrlGenerator
def default_url
if #attachment.instance.lead_status == 'product'
default_url = attachment_options[:default_url]
else
default_url = # provide another missing default_url
end
if default_url.respond_to?(:call)
default_url.call(#attachment)
elsif default_url.is_a?(Symbol)
#attachment.instance.send(default_url)
else
default_url
end
end
end
end
Update as per cases

Related

Rails API cache endpoint with Carrierwave uploader

I'm experiencing some issues when caching some data in my Rails API, when the model has a Carrierwave uploader attached. The issue started when I added versions in my ImageUploader.
The model looks like this:
class Article < ApplicationRecord
validates_presence_of :title, :intro, :body, :image, :release_date, :slug
mount_uploader :image, ImageUploader
The ImageUploader now looks like this:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :fog
# Override the directory where uploaded files will be stored.
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process resize_to_fit: [1920,1080]
version :thumb do
process resize_to_fit: [400,400]
end
version :big do
process resize_to_fill: [1080,1080]
end
Before the change I had no versions, and did no processing the original image.
I have tried caching this endpoint, which looks like this:
today = Date.today
data = Rails.cache.fetch("articles_#{params[:slug]}", :expires_in => 12.hours) do
Article
.includes(:related_articles, :users, :external_authors, :categories)
.where('release_date <= ?', today)
.where(slug: params[:slug])
.first
.as_json(include: [{ related_articles: { except: [:body, :intro, :preview] } }, { categories: { } }, { users: { except: [:password_digest, :password_reset_token, :password_reset_sent_at] } }, { external_authors: { } }])
end
render json: data, status: :ok
The problem is that it initally works, then it suddenly does not work. Especially it does not work after restarting the server (but it has sometimes stopped working randomly after multiple fetches where it worked being cached), giving me error like the following: uninitialized constant ImageUploader::Uploader2293312720
If I then change the cacheKey, it works again, but suddenly the error comes back, either randomly (it works multiple times before it randomly gives me the error), or when restarting server.
For now, I've just removed the caching, and it works fine with no issues.
I'm using: Rails v5.2.5, and Carrierwave v2.2.2.

Save a local attachment on the server

Though paperclip gem is generally used for easy file uploads, I have a similar requirement at server side. I have generated an image at server and want to save it in various styles as configured in paperclip options.
I have generated the migration and my model looks like this:
attr_accessible :genimg
has_attached_file :genimg
styles: { mini: '48x48>', small: '100x100>',
product: '240x240>', large: '600x600>' },
default_style: :product
url: '/images/:basename.:extension',
path: ':rails_root/app/assets/images/:basename.:extension'
convert_options: { all: '-strip -auto-orient' }
Is there a way I can save my local image in various formats using paperclip. How can I do so? If that's not possible, what are the other possible way to try out?
class YourController < ActionController::Base
def your_action
#image = Image.find 1 # (might be other way how you get the asset)
file_path = '/path/to/your/file'
file = File.open(file_path, 'r')
#image.asset = file
#image.save
end
end
in console you might do:
Image.new(:data => File.new(path_to_your_file, "r"))

Rails + carrierwave not creating conditional versions

I'm trying to set up conditional versions with carrierwave in my Rails app. I've implemented what seems to be an exact duplicate of the examples provided here.
The version is never created though unless my is_ipod? simply returns true. The code below is what I currently have and is not working. Notice the commented sections I've used to verify the image_type attribute is actually set correctly.
version :ipod_portrait_thumb, :if => :is_ipod? do
process resize_to_fit: [150,200]
end
def is_ipod? image
model.image_type == 'iPod Screenshot'
#if (model.image_type == "iPod Screenshot")
#if (model.image_type!=nil)
#puts "+++++"+model.image_type
# if (model.image_type=="iPod Screenshot")
#puts "+++++++ I AM HERE"
# return true
# end
#end
end
If is_ipod? looks like this:
def is_ipod? image
true
end
the version is created as expected. What am I missing? Thanks!
UPDATE:
I've edited the is_ipod? method to look like this:
def is_ipod? image
puts (image.path || "") + ': ' + ((model.image_type||"") == 'iPod Screenshot').to_s
model.image_type == 'iPod Screenshot'
end
Which outputs this to the console:
/public/uploads/tmp/20130325-1024-15906-5363/drawing.png: false
/public/uploads/tmp/20130325-1024-15906-5363/drawing.png: false
/public/uploads/app_image/image/59/drawing.png: true
So the version is trying to be created three times, twice for temp files and once for the final file. The model attribute is only set for the final file. Is this related? Can anyone tell me how this is different than this example?
class MyUploader < CarrierWave::Uploader::Base
version :monkey, :if => :is_monkey?
protected
def is_monkey? picture
model.favorite_food == 'banana'
end
end
Here is my model class in case that helps:
class AppImage < ActiveRecord::Base
attr_accessible :app_id, :image, :image_type, :image_cache
belongs_to :app
mount_uploader :image, AppImageUploader
validates :image_type, presence: true
validates :image, presence: true
end
Thanks!
I had a similar problem^ and I tried to create different sizes depending on model attribute. My solution is simple: just recreate versions after save.
app/uploaders/project_item_picture_uploader.rb
class ProjectItemPictureUploader < CarrierWave::Uploader::Base
...
version :preview do
process dynamic_process: true
end
protected
def dynamic_process(*args)
resize_to_fill *(model.get_size) if model.persisted?
end
end
app/model/project_item.rb
class ProjectItem < ActiveRecord::Base
mount_uploader :picture, ProjectItemPictureUploader
validates_presence_of :picture
def get_size
double ? [168, 57] : [76, 57]
end
after_save :recreate_delayed_versions!
def recreate_delayed_versions!
picture.recreate_versions!
end
end
It turns out this is happening because the AppImage model is a child of another model and is being added here on a nested form. For whatever reason child models don't have their attributes set by the time carrierwave processes versions.
I've verified this by adding carrierwave attachments to my parent model App. When the versions are processed for an App attachment, the attributes are set.
Maybe later I'll dig deeper to try and understand better (I'm pretty new to Rails), but for now I'm working around the issue by not having conditional versions.
I have the same issue this night and i'm doing some experimentation with it, finally i figured out, it seem that when your create a version for the first time (when you post your form), if you have this line :
model.image_type == 'iPod Screenshot'
this means that you should obligatory have a field in your form named image_type and the value for this field should be "iPod Screenshot"
CarrierWave verify the same thing when you show your image like article.image_url(:ipod_portrait_thumb) which means that you should have in your database a field named image_type with value "iPod Screenshot"
so if you have this line :
version :ipod_portrait_thumb, :if => :is_ipod?
you are telling CarrierWave to execute ipod_portrait_thumb function each time you Create or Show a record
in Case that your form don't contains the field image_type with value 'iPod Screenshot', for example because you set it in your model/controller, the way is to check another field to permit CarrierWave create your conditional version, so it's simple you can do something like this :
model.image_type == 'iPod Screenshot' || another_field_in_my_form_help_you_to_know_type_is_ipode == "something" # or just .present? rather than == "something" switch your case
here when CarrierWave try to create version the first time it will check this another_field_...... , and when you have a page that show your record (product, article....) it verify model.image_type == 'iPod Screenshot' that is stored already in your database
Hope this help you :)

Paperclip how to change basename (filename)?

I am trying to change the basename (filename) of photos:
In my model I have:
attr_accessor :image_url, :basename
has_attached_file :image,
:styles => { :original => ["300x250>", :png], :small => ["165x138>", :png] },
:url => "/images/lille/:style/:id/:basename.:extension",
:path => ":rails_root/public/images/lille/:style/:id/:basename.:extension"
before_save :basename
private
def basename
self.basename = "HALLLO"
end
But the filename is not changed at all.
If you are assigning the file directly you can do this:
photo.image = the_file
photo.image.instance_write(:file_name, "the_desired_filename.png")
photo.save
Im doing this to strip whitespaces:
before_post_process :transliterate_file_name
private
def transliterate_file_name
self.instance_variable_get(:#_paperclip_attachments).keys.each do |attachment|
attachment_file_name = (attachment.to_s + '_file_name').to_sym
if self.send(attachment_file_name)
self.send(attachment).instance_write(:file_name, self.send(attachment_file_name).gsub(/ /,'_'))
end
end
end
I hope this will help you.
edit:
In your example:
def basename
self.image_file_name = "foobar"
end
Should do the job. (but might rename the method ;) )
Paperclip now allows you to pass in a FilenameCleaner object when setting up has_attached_file.
Your FilenameCleaner object must respond to call with filename as the only parameter. The default FilenameCleaner removes invalid characters if restricted_characters option is supplied when setting up has_attached_file.
So it'll look something like:
has_attached_file :image,
filename_cleaner: MyRandomFilenameCleaner.new
styles: { thumbnail: '100x100' }
And MyRandomFilenameCleaner will be:
class MyRandomFilenameCleaner
def call(filename)
extension = File.extname(filename).downcase
"#{Digest::SHA1.hexdigest(filename + Time.current.to_s).slice(0..10)}#{extension}"
end
end
You could get away with passing in a class that has a self.call method rather than an object but this conforms to Paperclip's documentation in Attachment.rb.
I wanted to avoid having to add a before_create callback to every model with an attachment. I had a look at the source and at the time of this writing it looked sth like:
module Paperclip
class Attachment
...
def assign_file_information
instance_write(:file_name, cleanup_filename(#file.original_filename))
instance_write(:content_type, #file.content_type.to_s.strip)
instance_write(:file_size, #file.size)
end
So you could just patch cleanup_filename.
config/initializers/paperclip.rb
module Paperclip
class Attachment
def cleanup_filename(filename)
"HALLLO"
end
end
end

Save image from URL by paperclip

Please suggest me a way to save an image from an URL by Paperclip.
In Paperclip 3.1.4 it's become even simpler.
def picture_from_url(url)
self.picture = URI.parse(url)
end
This is slightly better than open(url). Because with open(url) you're going to get "stringio.txt" as the filename. With the above you're going to get a proper name of the file based on the URL. i.e.
self.picture = URI.parse("http://something.com/blah/avatar.png")
self.picture_file_name # => "avatar.png"
self.picture_content_type # => "image/png"
Here is a simple way:
require "open-uri"
class User < ActiveRecord::Base
has_attached_file :picture
def picture_from_url(url)
self.picture = open(url)
end
end
Then simply :
user.picture_from_url "http://www.google.com/images/logos/ps_logo2.png"
It didn't work for me until I used "open" for parsed URI.
once I added "open" it worked!
def picture_from_url(url)
self.picture = URI.parse(url).open
end
My paperclip version is 4.2.1
Before open it wouldn't detect the content type right, because it wasn't a file. It would say image_content_type: "binary/octet-stream", and even if I override it with the right content type it wouldn't work.
First download the image with the curb gem to a TempFile and then simply assign the tempfile object and save your model.
Into official documentation is reported here https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL
Anyway it seems not updated, because in last version of paperclip something has changed and this line of code is no more valid:
user.picture = URI.parse(url)
It raise an error, in particular this error is raised:
Paperclip::AdapterRegistry::NoHandlerError: No handler found for #<URI:: ...
The new correct syntax is this one:
url = "https://www.example.com/photo.jpeg"
user.picture = Paperclip.io_adapters.for(URI.parse(url).to_s, { hash_digest: Digest::MD5 })
Also we need to add these lines into config/initializers/paperclip.rb file:
Paperclip::DataUriAdapter.register
Paperclip::HttpUrlProxyAdapter.register
Tested this with paperclip version 5.3.0 and it works.
It may helpful to you. Here is the code using paperclip and image present in remote URL .
require 'rubygems'
require 'open-uri'
require 'paperclip'
model.update_attribute(:photo,open(website_vehicle.image_url))
In model
class Model < ActiveRecord::Base
has_attached_file :photo, :styles => { :small => "150x150>", :thumb => "75x75>" }
end
As those are old Answer's here's a newer one:
Add Image Remote URL to your desired Controller in the Database
$ rails generate migration AddImageRemoteUrlToYour_Controller image_remote_url:string
$ rake db:migrate
Edit your Model
attr_accessible :description, :image, :image_remote_url
.
.
.
def image_remote_url=(url_value)
self.image = URI.parse(url_value) unless url_value.blank?
super
end
*In Rails4 you have to add the attr_accessible in the Controller.
Update your form, if you allow other to upload an Image from a URL
<%= f.input :image_remote_url, label: "Enter a URL" %>
This is a hardcore method:
original_url = url.gsub(/\?.*$/, '')
filename = original_url.gsub(/^.*\//, '')
extension = File.extname(filename)
temp_images = Magick::Image.from_blob open(url).read
temp_images[0].write(url = "/tmp/#{Uuid.uuid}#{extension}")
self.file = File.open(url)
where Uuid.uuid just makes some random ID.

Resources