Undefined method `<<` in image upload - ruby-on-rails

def create
#product = Product.find(1)
#product.images << params[:image]
#product.save
end
The schema is
t.string "images", default: [], array: true
I want the assign the image coming in params to the images array, if the image is already there. The new image should append, otherwise it should be the first one
Here is the Product model
class Product < ActiveRecord::Base
mount_uploader :images, ProductUploader
validates :name, presence: true
validates :price, presence: true
validates :availability, presence: true
validates :about, presence: true
validates :ref, presence: true
validates :texture, presence: true
validates :shipping, presence: true
validates :category, presence: true
validates :notes, presence: true
end
I generated the ProductUploader through carrierwave uploader command
class ProductUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# 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
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process scale: [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process resize_to_fit: [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_whitelist
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
After uploading the image, the image shows up in params. But does not get saved in the table. Here is the screenshot of byebug
Edit:
I have made some progress
I updated the string [] to json for images like this
And instead of files coming like string, it's coming as file. Now the only problem is, I am unable to access the name properly name=\"image[avatar_1]\" in the params
"utf8"=>"✓", "authenticity_token"=>"JbtcVdUvPJIzSHycX0wdr349zcATDN51vSewI33nvHIKQdhxxgxWUEJz9ZsubNrgvq2ftO+DosHXWM8Tipce7w==", "image"=>{"avatar_1"=>#<ActionDispatch::Http::UploadedFile:0x007f2ba8377f78 #tempfile=#<Tempfile:/tmp/RackMultipart20160704-20497-1czlw3s.png>, #original_filename="no-save.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"image[avatar_1]\"; filename=\"no-save.png\"\r\nContent-Type: image/png\r\n">}, "commit"=>"submit", "controller"=>"upload", "action"=>"create"}
Here is the view file
<%= simple_form_for :image, method: :post, url: save_image_path do |f| %>
<%= f.file_field :avatar_1 %>
<%= f.submit :submit %>
<% end %>
here it is avatar_1. I need to change it in create code, but I am not accessing params itself properly

Let's divide your question into subproblems:
Database & controller
Your database and controller are working properly. No need to change anything!
Uploaded image
You uploaded image (in params[:image]) is a String, instead of a ActionDispatch::Http::UploadedFile. You should probably add the multipart form data to your form.
Change filename
You can access the uploaded filename with params[:image].original_filename. If you want to change the filename, then you can redefine the filename method for your CarrierWave handler (the last one, which is commented).

Related

CKEDITOR + CARRIERWAVE + S3 no image

I can't figure out why the heck CKEDITOR is not displaying my images...I am using Carrierwave as my uploader to S3, which obviously have been set to all have their own 'uploader.rb' If I upload directly through Carrierwave I have no problems retrieving said image back from S3 to display. BUT if I upload said image through CKEDITOR or attach it inside CKEDITOR it will only display it's source link. This happens in both production and dev localhost.
uploaders/ckeditor_attachement_file_uploader.rb
# encoding: utf-8
require 'carrierwave'
class CkeditorAttachmentFileUploader < CarrierWave::Uploader::Base
include Ckeditor::Backend::CarrierWave
# Include RMagick or ImageScience support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# include CarrierWave::ImageScience
# Choose what kind of storage to use for this uploader:
if Rails.env.production?
storage :fog
else
storage :file
end
# 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/ckeditor/attachments/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
Ckeditor.attachment_file_types
end
end
uploaders/ckeditor_picture_uploader.rb
# encoding: utf-8
class CkeditorPictureUploader < CarrierWave::Uploader::Base
include Ckeditor::Backend::CarrierWave
# Include RMagick or ImageScience support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# include CarrierWave::ImageScience
# Choose what kind of storage to use for this uploader:
if Rails.env.production?
storage :fog
else
storage :file
end
# 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/ckeditor/pictures/#{model.id}"
end
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process scale: [200, 300]
#
# def scale(width, height)
# # do something
# end
process :extract_dimensions
# Create different versions of your uploaded files:
version :thumb do
process resize_to_fill: [118, 100]
end
version :content do
process resize_to_limit: [800, 800]
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
Ckeditor.image_file_types
end
end
models/picture.rb
class Ckeditor::Picture < Ckeditor::Asset
mount_uploader :data, CkeditorPictureUploader, mount_on: :data_file_name
def url_content
url(:content)
end
end
models/attachement_file.rb
class Ckeditor::AttachmentFile < Ckeditor::Asset
mount_uploader :data, CkeditorAttachmentFileUploader, mount_on: :data_file_name
def url_thumb
#url_thumb ||= Ckeditor::Utils.filethumb(filename)
end
end
I know I am answering my own question but hopefully this will help you. If you are reading this then you obviously have all your storage set up correctly such as S3 (in my case). Your views must require the html_safe method when attempting to display your contents via ckeditor ex.
<div class="col-xs-12">
<h1>Webur Blog</h1>
<h2><%= #blogpost.title %></h2>
<span><p><%= image_tag #blogpost.picture.url if #blogpost.picture? %></p></span>
<p><%= #blogpost.content.html_safe %></p>
<% if is_an_admin %>
<%= link_to "Return to blog", blogposts_path, class: 'btn btn-primary' %>
<%= link_to "Edit Post", edit_blogpost_path, class: 'btn btn-primary' %> |
<%= link_to "Delete Post", blogpost_path(#blogpost),
method: :delete, data: {confirm: "Are you sure?"}, class: 'btn btn-danger' %>
<% else %>
<%= link_to "Return to blog", blogposts_path, class: 'btn btn primary' %>
<% end %>
</div>
Hopefully this helps others. CKeditor does not mention this in their setup guide, and if you are a beginner such as myself you may not catch subtleties like this

undefined method `url' for "#<ActionDispatch::Http::UploadedFile:0x007f8b6134d610>":String

I am using Carrierwave to upload picture, after uploaded, I got the error when go to show page:
undefined method `url' for "#ActionDispatch::Http::UploadedFile:0x007f8b6134d610>":String
<%= image_tag #product.picture.url if #product.picture? %>
Here is my code:
_form.html.erb
<div class="picture">
<%= f.file_field :picture %>
</div>
product.rb
class Product < ApplicationRecord
has_many :reviews, dependent: :destroy
mount_uploader :picture, PictureUploader
end
show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #product.name %>
</p>
<strong>Picture:</strong>
<%= image_tag #product.picture.url if #product.picture? %>
</p>
<%= link_to 'Edit', edit_product_path(#product) %> |
<%= link_to 'Back', products_path %>
Anyone know how to solve the problem?
Update:
picture_uploader.rb
# encoding: utf-8
class PictureUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# 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
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process :resize_to_fit => [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_white_list
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
Here is the version in Gemfile:
gem 'carrierwave', '0.11.2'
gem 'mini_magick', '4.5.1'
gem 'fog', '1.38.0'
I would check first strong params and add :picture attribute if it hasn't been added.
Then I would try to add this in your show views:
show.html.erb
<%= image_tag(#product.picture_url.to_s) %>
instead of this line of code:
<%= image_tag #product.picture.url if #product.picture? %>
In case that you have permissions problem, you can create config file:
config/initializers/carrierwave.rb
and add permissions:
CarrierWave.configure do |config|
config.permissions = 0666
config.directory_permissions = 0777
config.storage = :file
end
in model, you need add
Blockquote
mount_uploader :picture, PictureUploader
Blockquote

CarrierWave version path

I have model Catagory
class Category < ActiveRecord::Base
mount_uploader :image, CategoryImageUploader
end
I need to save the image with the name (id saved category)
class CategoryImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
mounted:
def store_dir
#"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def store_path(for_file = filename)
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}/#{for_file}"
end
version :small do
process resize_to_fit: [48, 48]
def filename(uploaded_file = file)
if uploaded_file.present?
"small_#{model.id}.#{uploaded_file.extension}"
end
end
end
version :big do
process resize_to_fit: [200, 200]
def filename(uploaded_file = file)
if uploaded_file.present?
"big_#{model.id}.#{uploaded_file.extension}"
end
end
end
def filename(uploaded_file = file)
if uploaded_file.present?
"#{model.id}.#{uploaded_file.extension}"
end
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
I'm use https://coderwall.com/p/mulldw/custom-file-names-with-carrierwave-and-amazon-s3 to save image like this 3.png 4.png
version :small and version :big aslo save (like big_3.png small_3.png )
but
**Category.last.image_url => "/uploads/category/image/10/10.png"
Category.last.image_url(:small) = > "/uploads/category/image/10/10.png"
Category.last.image_url(:big) = > "/uploads/category/image/10/10.png"**
All links lead to one image 10.png
I need
**Category.last.image_url => "/uploads/category/image/10/10.png"
Category.last.image_url(:small) = > "/uploads/category/image/10/small_10.png"
Category.last.image_url(:big) = > "/uploads/category/image/10/big_10.png"**

Carrierwave not utilizing default_url when version not present

Essentially, the photo_url(:thumb) displays the intended thumb url even though the file doesn't physically exist.
# encoding: utf-8
class WinePhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
# So the :thumb will be stored in "public/assets/wines/thumb/2008-meritage-750mL.jpg" for example.
def store_dir
"assets/wines/#{version_name}"
end
def cache_dir
Rails.root.join 'tmp/uploads'
end
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
# If no version name is present (original), just provide the thumb path
# This method is never called!!! Why not?
def default_url
ActionController::Base.helpers.asset_path("pages/wines/#{version_name || :thumb}/default.png")
end
# To save disk space, make sure the original image is no larger than the croppable + a few pixels
process :resize_to_fit => [2000, 2000]
version :croppable, :if => :not_cropping? do
process :common
process :resize_and_pad => [1200, 1200, :white]
process :convert => "jpg"
# Override the default naming convention...I store in folders,
# so I don't want the version prepended to the file name.
def full_filename(for_file = model.photo.file)
the_filename
end
end
version :show, :if => :not_cropping? do
process :resize_to_show
process :convert => "jpg"
def full_filename(for_file = model.photo.file)
the_filename
end
end
version :thumb, :from_version => :croppable, :if => :viewing_or_cropping? do
process :custom_thumbnail => [200, 200]
def full_filename(for_file = model.photo.file)
the_filename
end
end
def common(q=100, s='1.2x1+0.75+0.05')
manipulate! do |img|
img.combine_options do |i|
i.quality q
i.antialias
i.background :white
i.flatten
i.density 72
i.profile "#{Rails.root}/lib/color_profiles/sRGB_v4_ICC_preference_displayclass.icc"
i.strip
end
img = yield(img) if block_given?
img.combine_options do |i|
i.unsharp s
end
img
end
end
def resize_to_show
common do |img|
img.combine_options do |i|
i.trim
i.thumbnail 110
i.background :white
i.gravity :center
i.extent 170
end
img
end
end
def custom_thumbnail(width, height)
manipulate! do |img|
img.combine_options do |i|
i.distort :srt, "#{model.crop_x},#{model.crop_y} 1 -30 600,600"
i.repage.+
i.gravity :center
i.crop "#{model.crop_w}x#{model.crop_h}+0+0"
i.repage.+
i.thumbnail "#{width}x#{height}"
i.unsharp '1.2x1+0.75+0.05'
end
img
end
end
def not_cropping? picture
!model.cropping?
end
# For some stupid reason, if I only check for 'model.cropping?' and
# it returns false, the photo_url(:thumb) will always show the default_url
# and never show the actual url! This is terrible logic. The version declarations
# above should only affect the generation of thumbnails, not whether the call
# to photo_url(:thumb) works.
def viewing_or_cropping? picture
model.cropping? or model.changes.merge(model.previous_changes).empty?
end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png tif tiff)
end
# Only modify the version names, not the original uploaded file name
def filename
"#{ model.get_permalink(:normalized => true) }#{ File.extname original_filename }" if original_filename
end
# The filename should be the wine's permalink name (ex., 2008-merlot-reserve-750mL.jpg)
def the_filename
"#{ model.get_permalink(:normalized => true) }.jpg"
end
# Remove this when https://github.com/carrierwaveuploader/carrierwave/issues/1164 is solved.
def recreate_versions!(*versions)
if versions.any?
from_versions = versions.group_by { |v| self.class.versions[v][:options][:from_version] }
from_versions.each do |from, versions|
next if from.nil?
file = CarrierWave::SanitizedFile.new(self.versions[from.to_sym].file)
store_versions!(file, versions)
end
else
super(*versions)
end
end
end
So a call to #wine.photo_url(:thumb) produces
http://dev.mydomain.com/assets/wines/thumb/2006-cabernet-sauvignon-750mL.jpg
when it should be producing
http://dev.mydomain.com/assets/pages/wines/thumb/default.png
because the file doesn't exist.
My best guess is that Carrierwave generates the file names based on the existence of the model's attribute in the database and not whether or not the specific version of the file exists. Not what I was expecting from default_url but it still makes sense in its limited way.

Why is my image attribute showing up as nil in post request?

Here's what shows in the server window when submitting the form:
Started POST "/seniors" for 127.0.0.1 at 2013-08-21 17:27:34 -0400
Processing by SeniorsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"zWeveRhstI178IawtOka6TV2wdKwnpKq2J74VYYNf1U=", "senior"=>{"first_name"=>"Jane", "last_name"=>"dOE", "employer _id"=>"1"}, "commit"=>"Create Senior"}
(0.0ms) begin transaction
SQL (3.0ms) INSERT INTO "seniors" ("created_at", "employer_id", "first_name", "image", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Wed, 21 Aug 2013 21:27:34 UTC +00:00], ["employer_id", 1], ["first_name", "Jane"], ["image", nil], ["last_name", "dOE"], ["updated_at", Wed , 21 Aug 2013 21:27:34 UTC +00:00]]
(107.7ms) commit transaction
Redirected to http://localhost:3000/seniors
Completed 302 Found in 120ms (ActiveRecord: 110.7ms)
The form:
<%=content_for :page do%>
<div class="menuTitle"><h1><%if #senior.new_record?%>New<%else%>Edit<%end%> Senior</h1></div>
<%=form_for #senior, :html=>{:multipart => true} do |f|%>
<div class="login form">
#<p>First Name: <%=f.text_field :first_name%></p>
<p>Last Name: <%=f.text_field :last_name%></p>
<p>Facility: <%=f.collection_select :employer_id, #employers, :id, :name%></p>
<p>Image: <%=f.file_field :image%></p>
<p><%=f.submit :class => "button red"%></p>
</div>
<%end%>
<%end%>
and model:
class Senior < ActiveRecord::Base
mount_uploader :image, ImageUploader
#attr_accessible :image
#attr_accessible :first_name, :last_name, :street1, :street2, :city, :state, :zipcode, :country, :phone, :image, :employer_id, :phin
attr_protected :nil
has_many :senior_users
has_many :short_forms
has_one :long_form
has_many :users, :as => :workers, :through => :senior_users
belongs_to :employer
def long_name
"#{self.last_name}, #{self.first_name}"
end
end
and image_uploader.rb:
# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
#include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
# 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
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process :scale => [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
# def extension_white_list
# %w(jpg jpeg gif png)
# end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
Problem solved thanks to the answer here: Rails file_field does not upload anything
The site I was brought on board to work on uses jQuery mobile which by default submits all forms using ajax, however files cannot be submitted via ajax. The solution was to add
:html => { :'data-ajax' => false }
to the form.
Im leaving this answer if anyone comes here again. Although it's true rails form_for POST via ajax does not support sending files via AJAX even with multipart: true, it can be done with the gem "remotipart". This way, there's no need to fallback to submit the form with full page refresh. In response to #xxyyxx answer.

Resources