I have two models, each with their own Carrierwave uploaders:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
and:
class Bookshelf < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
I want the user's avatar to be the latest bookshelf image he's uploaded. I try to achieve this like so:
class BookcasesController < ApplicationController
def create
#bookcase = current_user.bookcases.build(params[:bookcase])
if #bookcase.save
current_user.avatar = #bookcase.image
current_user.avatar.recreate_versions!
end
end
end
Unfortunately, this has no effect on the avatar at all. How else might I achieve this?
current_user.avatar = #bookcase.image
current_user.avatar.recreate_versions!
Doesn't actually save --- you can either:
current_user.avatar.save
or as you put:
current_user.update_attribute(:avatar, #bookcase.image)
If your image file is stored locally and you don't mind opening a file descriptor, you could also do this:
current_user.avatar = File.open(#bookcase.image.path)
current_user.save
Related
I have a user model
class User < ApplicationRecord
has_one_attached :photo
end
I'm trying to:
upload an image to Cloudinary via URL (this works)
attach it to a user instance which uses ActiveStorage (this does not)
Here is what I think should work
user_img_response = Cloudinary::Uploader.upload("https://www.formula1.com/content/dam/fom-website/manual/Misc/2019-Races/Monaco2019/Monaco%20chicane%20HAM%20VER%20sized.jpg.transform/9col/image.jpg")
img_id = user_img_response["url"].match(/image\/upload.*/)[0]
signature = "#{img_id}##{user_img_response["signature"]}"
preloaded_file = Cloudinary::PreloadedFile.new(signature)
user = User.new(title: "Chris")
user.photo = preloaded_file
user.save
=> true
However, the photo is not being attached to the user instance
user.photo.attached?
=> false
Assuming your app/models/photo.rb looks similar to this:
class Photo < ActiveRecord::Base
attr_accessible :title, :bytes, :image, :image_cache
belongs_to :album
mount_uploader :image, ImageUploader
validates_presence_of :title, :image
end
What happens if you try:
...
user = User.new(title: "Chris")
user.photo.image = preloaded_file # <---- assign file to image attribute
user.save
You can also try to emulate this sample app for your case: https://github.com/cloudinary/cloudinary_gem/tree/master/samples/photo_album
EDIT: you can try something like this:
require 'uri'
file = URI.open(user_img_response["url"]) # use cloudinary url
photo.image.attach(io: file, filename: 'image.jpg')
See: https://blog.eq8.eu/til/upload-remote-file-from-url-with-activestorage-rails.html
When I try to access my localhost, I get the following message.
**LoadError in ListingsController#index
Unable to autoload constant Listing, expected C:/Sites/redemo/app/models/listing.rb to define it**
This is the code it refers to in listings_controller.rb
# GET /listings.json
def index
#listing =Listing.all
end
My listing.rb is as follows:
class Listings < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
Please, what else am I supposed to do to define it?
You check your model definition. While controller is Listings, model should be Listing
Therefore change to this;
class Listing < ActiveRecord::Base
mount_uploader :image, ImageUploader
end
My uploader is working well apart from one small thing. The setting of default images. I'm using carrierwave for users to upload profile images of themselves:
user model
class User < ActiveRecord::Base
has_one :avatar, class_name: 'Image', foreign_key: :user_id
before_create :create_fallback_image
def create_fallback_image
self.create_avatar
end
end
image model
class Image < ActiveRecord::Base
mount_uploader :file_name, AvatarUploader, auto_validate: false
belongs_to :user
end
avatar uploader
class AvatarUploader < BaseUploader
include CarrierWave::RMagick
storage :file
process resize_to_fit: [75, 75]
process convert: 'gif'
def default_url
'foobar'
end
def filename
random_string + '.gif'
end
end
def random_string
#random_string ||= User.random_string
end
end
When a user signs up without uploading an optional profile image, they are assigned an association to their profile image, but instead of the default_url working, they get a random string from the filename method.
I thought I could get around it like this:
user model
class User < ActiveRecord::Base
has_one :avatar, class_name: 'Image', foreign_key: :user_id
before_create :create_fallback_image
def create_fallback_image
# look here:
self.create_avatar.create_fallback
end
end
image model
class Image < ActiveRecord::Base
mount_uploader :file_name, AvatarUploader, auto_validate: false
belongs_to :user
def create_fallback
self.update_attributes(file_name: 'my_fallback.jpg')
end
end
and while it nearly, nearly works, when I update the attributes of the file_name column, the uploader kicks in and my_fallback.jpg is overridden by a random string from my random_string method!
Carrierwave has a built-in fallback mechanism for default image
Update your default_url method in AvatarUploader as below:
def default_url
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "my_fallback.jpg"].compact.join('_'))
end
where change fallback/ to your desired folder path.
This way, when an avatar is not uploaded for a particular user then my_fallback.jpg would be used as fallback image.
Refer to section Providing a default URL in Carrierwave Documentation.
when I update the attributes of the file_name column, the uploader
kicks in and my_fallback.jpg is overridden by a random string from my
random_string method!
This happens because you have overridden filename method in AvatarUploader which gets called every time an image is uploaded. If you notice, its calling random_string method in it. Hence, you get a random string as your filename.
UPDATE
As per the chat session with OP, if an avatar is not uploaded for a user then a default image should be shown. I suggested the following helper :
module ApplicationHelper
def display_avatar(user)
unless user.avatar.nil?
image_tag(user.avatar.file_name)
else
image_tag("/path/to/fallback.jpg")
end
end
## ...
end
Use this helper method in views to display avatar image appropriately.
Also, you can do it within model:
class User < ApplicationRecord
has_one :avatar, class_name: 'User::Avatar', as: :parent, dependent: :destroy
accepts_nested_attributes_for :avatar, allow_destroy: true #, ...
def avatar
super || build_avatar
end
end
I am using a single Image model to store information about images used by different other models (via a polymorphic association).
I'd like to change the uploader on this model depending on the model associated to have different versions for different models.
For example, if the imageable is a Place, the mounted uploader would be a PlaceUploader. And if there is no PlaceUploader, it would be the default ImageUploader.
At the moment I have:
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
mount_uploader :image, ImageUploader
end
Ideally I'd like to have:
# This is not supported by CarrierWave, just a proof of concept
mount_uploader :image, -> { |model| "#{model.imageable.class.to_s}Uploader".constantize || ImageUploader }
Is there a way to achieve that? Or a better way to have different versions depending on the associated model?
Edit
I found another solution using one single ImageUploader:
class ImageUploader < BaseUploader
version :thumb_place, if: :attached_to_place? do
process resize_to_fill: [200, 200]
end
version :thumb_user, if: :attached_to_user? do
process :bnw
process resize_to_fill: [100, 100]
end
def method_missing(method, *args)
# Define attached_to_#{model}?
if m = method.to_s.match(/attached_to_(.*)\?/)
model.imageable_type.underscore.downcase.to_sym == m[1].to_sym
else
super
end
end
end
As you can see my 2 versions are named thumb_place and thumb_user because if I name them both thumb only the first one will be considered (even if it doesn't fill the condition).
I need to implement same logic that i have a single Image model and base on polymorphic association to mount different uploaders.
finally i come up below solution in Rails 5:
class Image < ApplicationRecord
belongs_to :imageable, polymorphic: true
before_save :mount_uploader_base_on_imageable
def mount_uploader_base_on_imageable
if imageable.class == ImageableA
self.class.mount_uploader :file, ImageableAUploader
else
self.class.mount_uploader :file, ImageableBUploader
end
end
end
In my existing Rails project, I create Picture model.
class Picture < ActiveRecord::Base
belongs_to :user
end
And then, when adding Ckeditor to my project, I have to create another Picture model under ckeditor directory like this
class Ckeditor::Picture < Ckeditor::Asset
...
end
In my user model, I have this association
class User < ActiveRecord::Base
has_many :pictures
end
However, I cannot use user.pictures. Whenever I make this statement, the following error comes up:
Expected /home/xxx/app/models/ckeditor/picture.rb to define Picture
How can I solve this issue?
try:
class User < ActiveRecord::Base
has_many :pictures,:class_name=> "::Picture"
end
I'm not sure but maybe:
module Ckeditor
class Picture < Ckeditor::Asset
...
end
end
I managed to resolve my issue by renaming the Picture class into UserPicture and use table_name to set its corresponding table in database. And then in User model:
has_many :pictures, class_name: 'UserPicture'
Simple:
1) rename models/ckeditor/picture.rb to models/ckeditor/epicture.rb
2) in models/ckeditor/epicture.rb change to this:
class Ckeditor::Epicture < Ckeditor::Asset
has_attached_file :data,
url: "/ckeditor_assets/epictures/:id/:style_:basename.:extension",
path: ":rails_root/public/ckeditor_assets/epictures/:id/
3) in config/initializers/ckeditor.rb uncomment row and change to this:
config.picture_model { Ckeditor::Epicture }
4) for correct work in _asset.html.erb change bug:
polymorphic_path(asset, format: :json) to picture_path(asset)
in my case dir of this file
\usr\local\rvm\gems\ruby-1.9.3-p545\gems\ckeditor-4.1.2\app\views\ckeditor\shared\_asset.html.erb
Restart server.
Work's fine!
You can change Dafault Picture Model Name in config/initializers/ckeditor.rb like this:
Ckeditor.setup do |config|
...
config.picture_model { Ckeditor::EditorPicture }
...
end
remove autogenerated defined model from models/ckeditor/picture.rb and add new model /ckeditor/editor_picture.rb
insert to editor_picture.rb
class Ckeditor::EditorPicture < Ckeditor::Asset
mount_uploader :data, CkeditorPictureUploader, :mount_on => :data_file_name
def url_content
url(:content)
end
end