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

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)

Related

Can't upload image to Cloudinary from Sinatra

I'm trying to upload an image to Cloudinary from within my Sinatra web app. I
got it saving the image locally by using CarrierWave and I know Cloudinary and
CarrierWave play nicely with each other but I've tried everything and no dice.
I've followed the docs but they are very Rails-centric. Damn you Rails!
# Model - User.rb
class ImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process convert: 'png'
version :face do
cloudinary_transformation gravity: :face
end
end
class User
include DataMapper::Resource
attr_accessor :image
property.............
mount_uploader :image, ImageUploader
end
# Config - app.rb
require 'carrierwave/datamapper'
require 'cloudinary'
# DataMapper code ...
# Some routes ...
post '/add' do
user = user.create(name: params[:name])
Cloudinary::Uploader.upload(params[:image], api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME')
# View (haml)
%head
%script{src: "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"}
%script{src: "js/cloudinary_js/jquery.ui.widget.js"}
%script{src: "js/cloudinary_js/jquery.iframe-transport.js"}
%script{src: "js/cloudinary_js/jquery.fileupload.js"}
%script{src: "js/cloudinary_js/jquery.cloudinary.js"}
# snip snip ...
%form{action: "/add", method: "POST", enctype: "multipart/form-data"}
%input{type: "file", name: "image"}
%button Submit
The error I'm getting when trying to submit the form is:
TypeError at /add
no implicit conversion of Hash into String
The line it gives me that is causing the error is the Cloudinary uploader line in the post method.
Here's my params[:image] hash
{:filename=>"batman_robin.jpg", :type=>"image/jpeg", :name=>"image", :tempfile=>#<Tempfile:/var/folders/rv/2w8gpstn4hb2lb__27n27ksh0000gn/T/RackMultipart20131223-36484-tgmaba>, :head=>"Content-Disposition: form-data; name=\"image\"; filename=\"batman_robin.jpg\"\r\nContent-Type: image/jpeg\r\n"}
In the docs it mentions a file signature then states that CarrierWave handles this. I think my problem lies with the upload form.
Where am I going wrong with this? Anyone know what 'cl_image_upload_tag' generates and what are Cloudinary's "special attributes" that I need to add to my file upload tag?
UPDATE: So I figured that and I can actually just pass the image directly like so:
Cloudinary::Uploader.upload(params[:image][:filename] ...
However it now says
No such file or directory - batman_robin.jpg
which is strange. I've hardcoded a URL in there and it gets uploaded to Cloudinary. Any ideas? Also, any idea about the Cloudinary jQuery upload?
params[:image] is a hash but Cloudinary::Uploader.upload expects a string as the first argument. All the examples in the docs give a name or a URL, so pass the :filename from the params, e.g.
post '/add' do
user = user.create name: params[:name]
image_meta = params[:image]
filename = image_meta.delete :filename
Cloudinary::Uploader.upload filename, {api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME'}.merge(image_meta)
I'd also put all those default settings into a settings hash (and have them read in from the environment if they're secret), e.g.
config do
set :cloudinary_api, {api_key: ENV['API_KEY'],
api_secret: ENV['API_SECRET'],
cloud_name: ENV['CLOUD_NAME']}
end
then the call becomes…
Cloudinary::Uploader.upload filename, settings.cloudinary_api.merge(image_meta)
Instead of the filename you need to pass the actual content which is hold in Tempfile So passing Cloudinary::Uploader.upload(params[:image][:tempfile]) should work.

Paperclip in Rails 4 - Strong Parameters Forbidden Attributes Error

Having a problem with a Paperclip upload in Rails 4 - failing on ForbiddenAttributesError (strong parameters validation). Have the latest paperclip gem and latest rails 4 gems.
I have a model "Image" with an attached file "upload" in the model:
has_attached_file :upload, :styles => { :review => ["1000x1200>", :png], :thumb => ["100x100>", :png]}, :default_url => "/images/:style/missing.png"
The image model was created with a scaffold, and I added paperclip migrations. The form partial was updated to use
f.file_field :upload
the form generates what appears to be a typical set of paperclip params, with the image param containing the upload. I am also passing a transaction_id in the image model, so it should be permitted. But that's it - the image and the transaction ID.
I expected to be able to write the following in my controller to whitelist my post - but it failed:
def image_params
params.require(:image).permit(:transaction_id, :upload)
end
So I got more explicit - but that failed too:
def image_params
params.require(:image).permit(:transaction_id, :upload => [:tempfile, :original_filename, :content_type, :headers])
end
I'm a bit frustrated that Rails 4 is not showing me what ForbiddenAttributesError is failing on in a development environment - it is supposed to be showing the error but it does not - would be a nice patch to ease development. Or perhaps everyone else is getting something that I am missing! Thanks much for the help.
I understand what happened here now - and will leave this up in the hope it helps someone else. I was porting code from a rails 3 project and missed the line that created the image:
#image = current_user.images.new(params[:image])
In rails 4 this is incorrect (I beleive). I updated to
#image = current_user.images.new(image_params)
and that solved my problem.
It looks like your first one should have worked. This is what I use for my projects.
class GalleriesController < ApplicationController
def new
#gallery = Gallery.new
end
def create
#user.galleries.new(gallery_params)
end
private
#note cover_image is the name of paperclips attachment filetype(s)
def gallery_params
params.require(:gallery).permit(:cover_image)
end
end

Datamapper + Carrierwave store method not being called, only cache

I have a really simple sinatra app which uses datamapper and carrierwave.
I've reduced the uploader to its minimum:
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
end
And have one simple model that uses it:
class Photo
include DataMapper::Resource
property :id, Serial
property :caption, String
property :position, Integer
property :created_at, DateTime
property :updated_at, DateTime
mount_uploader :source, PhotoUploader
belongs_to :album
end
It happens that when I use it:
post '/admin/album/:id/photo/create' do
album = Album.get(params[:id])
photo = Photo.create(params[:photo])
album.photos << photo
album.save!
redirect '/admin/album'
end
photo is being created normally on the database and the file is being uploaded normally to the cache directory but not being moved to the final storage destination.
I've got into the carrierwave code and could verify that the cache! method is being called but not the store! method.
I've tried to use the uploader without any association to the object:
get '/lorem' do
f = File.open('test.png')
u = PhotoUploader.new
u.store!(f)
"ok"
end
Everything works as expected in this case.
==================== UPDATE ====================
I belive I've found the source of the problem. While running debugger I got to this:
gems/dm-core-1.2.0/lib/dm-core/query.rb:1014
conditions.each do |condition|
(rdb:1)
gems/carrierwave-0.6.2/lib/carrierwave/sanitized_file.rb:290
def sanitize(name)
(rdb:4)
gems/carrierwave-0.6.2/lib/carrierwave/sanitized_file.rb:299
def split_extension(filename)
(rdb:4)
gems/carrierwave-0.6.2/lib/carrierwave/sanitized_file.rb:29
self.file = file
(rdb:4)
gems/carrierwave-0.6.2/lib/carrierwave/sanitized_file.rb:269
if file.is_a?(Hash)
(rdb:4)
gems/carrierwave-0.6.2/lib/carrierwave/sanitized_file.rb:274
#file = file
(rdb:4) pp file
"/Users/jvalente/Projects/dummy/public/uploads/{:filename=>\"Screen Shot 2013-01-24 at 4.57.15 PM.png\", :type=>\"image/png\", :name=>\"photo[source]\", :tempfile=>#<File:/var/folders/jp/wm1gxk8558d36rh9vzkmx4jw0000gn/T/RackMultipart20130128-27245-105ybbf>, :head=>\"Content-Disposition: form-data; name=\\\"photo[source]\\\"; filename=\\\"Screen Shot 2013-01-24 at 4.57.15 PM.png\\\"\\r\\nContent-Type: image/png\\r\\n\"}"
This clearly is wrong since it will fail the file.is_a?(Hash) condition and get into:
#file = file
#original_filename = nil
#content_type = nil
Still, I can't understand why this happens.

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. :)

Ruby: Paperclip, S3, and Deep-cloning

What I have is a Theme model, which contains many Assets. Assets are using Paperclip and storing their file content in my Amazon AWS-S3 system. I'm also using deep_clone because my customers have the ability to copy built in Themes and then modify them to their hearts content. All the deep_clone stuff is working great, but when I deep_clone the assets, the old file contents don't get added to my S3 buckets. The record gets saved to the database, but since the file-contents don't get saved with the new ID the file.url property points to a dead file.
I've tried calling paperclip's save and create method manually but I can't figure out how to get paperclip to "push" the file back to the bucket since it now has a new ID, etc....
require 'open-uri'
class Asset < ActiveRecord::Base
belongs_to :theme
attr_accessor :old_id
has_attached_file :file,
:storage => "s3",
:s3_credentials => YAML.load_file("#{RAILS_ROOT}/config/aws.yml")[RAILS_ENV],
:bucket => "flavorpulse-" + RAILS_ENV,
:path => ":class/:id/:style.:extension"
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
before_save :delete_assets_in_same_theme_with_same_name
after_create :copy_from_cloned_asset
private
def delete_assets_in_same_theme_with_same_name
Asset.destroy_all({:theme_id => self.theme_id, :file_file_name => self.file_file_name})
end
def copy_from_cloned_asset
if (!old_id.blank?)
if (old_id > 0)
old_asset = Asset.find(old_id)
if (!old_asset.blank?)
self.file = do_download_remote_image(old_asset.file.url)
self.file.save
end
end
end
end
def do_download_remote_image (image_url)
io = open(URI.parse(image_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Any ideas on how I can get paperclip to push the file? I also wouldn't be opposed to doing this using Amazon's aws-s3 gem but I couldn't seem to get that to work either.
According to this former question/answer, it should be possible with this simple line of code:
self.file = old_asset.file

Resources