Mapping through Paperclip image styles - activerecord

I have an Album that uses the Paperclip gem to store artwork image uploads. The artwork attribute has a list of styles of different image sizes: thumb, medium, and large.
class Album < ActiveRecord::Base
has_attached_file :artwork, styles: { thumb: "50x50#", medium: "160x160#", large: "300x300#" }
...
end
In my active model serializer I want to create an array-based images attribute that stores the urls of each of these sizes. Traditionally I would do something like this:
class AlbumSerializer < ActiveModel::Serializer
attributes :id, :name, :images
def images
[object.artwork.url(:thumb), object.artwork.url(:medium), object.artwork.url(:large)]
end
end
Is there a way I could just map through each style instead of having to write the same call to the artwork url? I tried getting the hash of styles as so but got it returned nothing:
album.artwork.styles
=> {}
Any idea on getting this to work?

Not sure about the paperclip part (the "styles array"), but you could do:
def images
[:thumb, :medium, :large].map { |style| object.artwork.url(style) }
end

Related

Attach images in seed.rb file in rails using active storage

I have a class called vehicles which can have an image attached to it. I have made a default image to display if no other image was uploaded in the vehicles.rb file.
I would like to include images in the seed.rb file so I don't have to manually upload all images. Is this possible?
Help is much appreciated thanks.
Here is my vehicle.rb:
class Vehicle < ApplicationRecord
belongs_to :make
belongs_to :model
accepts_nested_attributes_for :make
accepts_nested_attributes_for :model
has_one_attached :image
after_commit :add_default_image, on: %i[create update]
def add_default_image
unless image.attached?
image.attach(
io: File.open(Rails.root.join('app', 'assets', 'images', 'no_image_available.jpg')),
filename: 'no_image_available.jpg', content_type: 'image/jpg'
)
end
end
end
Here is how i am creating records in my seed file but I would like to include the images as well:
v = Vehicle.create(
vin: '1QJGY54INDG38946',
color: 'grey',
make_id: m.id,
model_id: mo.id,
wholesale_price: '40,000'
)
You could use the Ffaker gem to easily generate fake data and finally after creating the vehicle record you can update your record image attribute from the instance variable. Check Attaching File/IO Objects
This would be the code of db/seed.rb file:
if Vehicle.count.zero?
10.times do
v = Vehicle.create(
vin: FFaker::Code.ean,
color: FFaker::Color.name,
maker_id: m.id,
model_id: m.id,
wholesale_price: '40,000'
)
v.image.attach(
io: File.open(File.join(Rails.root,'app/assets/images/photo.jpg')),
filename: 'photo.jpg'
)
end
end
Don't forget to add the ffaker gem to your Gemfile file.
As the above answer states it's in the Edge Guides, for people having trouble getting the path right, this is an example of a line in the seeds.rb file attaching an avatar to the 1st created user:
User.first.avatar.attach(io: File.open(File.join(Rails.root,'app/assets/images/avatar.jpg')), filename: 'avatar.jpg')
This approach worked for me on Rails 7:
I put my images in public/images folder.
Seed the DB
db/seeds.rb
post = Post.create!(title: "My Title", description: "My description")
post.image.attach(io: File.open(Rails.root.join("public/images/sample.jpg")), filename: "sample.jpg")
Then in my views:
app/views/posts/show.html.erb
# to get the image URL:
polymorphic_url(#post.image)
# to display the image
image_tag post.image if post.image.attached?
All this assuming you installed ActiveStorage:
In your terminal:
$ rails active_storage:install
$ rails db:migrate
Then in your model:
models/post.rb
has_one_attached :image
And in your controller. Add image to permitted params:
params.require(:post).permit(:image, :everything_else)

Where does CarrierWave stores uploads

Please help me understand how does CarrierWave works.
I'm using minimal Sinatra/DataMapper app with following contents:
class VideoUploader < CarrierWave::Uploader::Base
storage :file
end
class Video
include DataMapper::Resource
property :id, Serial
property :name, String
property :desc, Text
mount_uploader :file, VideoUploader
end
get '/' do
slim :form
end
post '/' do
video = Video.new
video.name = params[:name]
video.desc = params[:desc]
video.file = params[:file]
video.save
redirect '/'
end
As I understood mount_uploader :file, VideoUploader string in Video definition adds .video method to Video instance, and I can store uploads assigning params[:file] to it. When I'm trying to send form from browser, the request successfully creates record in DB table, but I can't find any signs of file existence either in DB and public_directory. What I'm doing wrong?
You probably should define the store_dir method inside the VideoUploader class:
class VideoUploader < CarrierWave::Uploader::Base
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
....
end

Mongoid: Retrieves all embedded documents

Suppose we have these models:
class Person
include Mongoid::Document
embeds_many :albums
end
class Album
include Mongoid::Document
embeds_many :photos
end
class Photo
include Mongoid::Document
end
What I want is to retrieves all Photo of a particular Person. Is there a mongoid/mongodb shortcuts or the only way is to iterate over person.albums and store all album.photos in a new array?
Thanks.
You have 2 ways to do this, one is through Mongoid, which, AFAIK, will inflate all objects.
Something like:
Person.only("albums.photos").where(id: '1').albums.map(&:photos).flatten
Or you can do it in Moped(driver) which will return only an array of photos.
Person.collection.find(id: "1").select("albums.photos" => 1).
first["albums"].map { |a| a["photos"] }.flatten
On the DB load, both dont make any difference, as they will yield the same query, only difference is that the first one will create way more objects than the second one.

Image upload in ruby with sinatra and datamapper and Carrierwave?

I want to upload a single image with Carrierwave, the thing is, it is uploading the file and saving it into the directory uploads, in this folder it creates another folder called tmp, and in the tmp folder it creates another folder which includes the image.
The thing is it doesn't seem to stop it is uploading and uploading and uploading all the time even if the image is fully uploaded into that folder and I can open this image already with feh(or anyother image viewer).
This is what my controller looks like.
get "/new" do
protect!
erb :new
end
post "/new" do
protect!
#user = User.get(session[:user_id])
image = #user.image.new(
:description => params[:description],
:image => params[:image]
)
# image.save
"NEVER REACHED!"
end
(The text "NEVER REACHED!" won't be displayed at all and I don't know why...)
This is my model:
class ImageUploader < CarrierWave::Uploader::Base
def extensions_white_list
%w(jpg jpeg gif png)
end
storage :file
end
class Image
include DataMapper::Resource
property :id, Serial
property :description, Text
property :image, String, :auto_validation => false
mount_uploader :image, ImageUploader
end
class User
include DataMapper::Resource
property :id, Serial
has n, :post
end
So as already mentioned the Text "NEVER REACHED" is never reached. Any Ideas why?
Besides I always get the warning:
Defining property for an uploader is deprecated at /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
And I do not know why...
UPDATE: It is somehow working now as long as I do not uncomment image.save in the main controller, but I actually need to uncomment this, any Ideas how to fix this?
Remove property :image line in your Image model as:
class Image
include DataMapper::Resource
property :id, Serial
property :description, Text
mount_uploader :image, ImageUploader
end
mount_uploader by itself defines the :image as String type.
Try to run your application after this and let me know if the condition remains the same. :)

Polymorphism unimplemented method. What people usually do?

If I have a model like this:
class Media
attr_reader :title, :created_at
end
class Video < Media
end
class Picture < Media
attr_reader :image_url
end
So, in the Picture class there is one attribute that should not exist in the superclass. What people usually do when you want to retrieve all the media? Would you want to throw exception on the method image_url? Or how would you re-model it?
If you're interested in all media, then you shouldn't care if it's a video or a picture. In other words, a method that accepts a collection of Media should not call any methods from Media descendants. If you need all pictures, then work with Picture, don't overgeneralize.
If you still do need to process all media while telling the types apart, you can branch on a type.
def process_media media
media.each do |m|
if m.is_a? Picture
# work with picture
elsif m.is_a? Video
# work with video
end
end
end
An alternative is to use duck typing. That is, if it has a method image_url, then it must be some kind of a picture.
def process_media media
media.each do |m|
if m.respond_to? :image_url
# work with picture
elsif m.respond_to? :bitrate
# work with video
end
end
end

Resources