carrierwave not creating method - ruby-on-rails

I am trying to add an image upload to a form through carrierwave and that displaying the picture through an image tag.
Therefore I generated a Picture Uploader and a migration
$ rails generate uploader Picture
$ rails generate migration add_picture_to_cpostings picture:string
and migrated.
I also added
mount_uploader :picture, PictureUploader
to the cpostings model.
In the controller I permitted the picture attribute
def cposting_params
params.require(:cposting).permit(:content, :spots, :class_date, :class_time, :title, :picture)
end
The upload with
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
in the form works, but when I try to display the picture in the posting with
<%= image_tag cposting.picture.url if cposting.picture? %>
I get the following error:
undefined local variable or method `cposting' for #<#:0x007f9629cc76f0>
According to railstutorial.org, the method should be created automatically by carrierwave. Do you have any idea why it doesn't work here?

undefined local variable or method `cposting' for #<#:0x007f9629cc76f0>
From the error message it's clear that, your view does not find the cposting variable. You have to declare this cposting variable inside the corresponding controller's action as an instance variable (#cposting) and then it will be available in your view file and you will not get this error.
Update
Change:
<%= image_tag cposting.picture.url if cposting.picture? %>
To:
<%= image_tag #cposting.picture.url if #cposting.picture? %>

Related

Rails: Undefined method `[]=' for nil:NilClass with active record when no attachments

I am using the wicked gem for a wizard form. In one of the steps of my form I have fields to upload attachments. I'm using active storage and Rails 6.
In my view
<%= f.file_field :plan %>
<%= f.file_field :appraisal %>
<%= f.file_field :flow %>
In my sale_steps controller
def show
#sale = current_user.sales.find(params[:sale_id])
render_wizard
end
def update
#sale = current_user.sales.find(params[:sale_id])
params[:sale][:status] = step.to_s **(the error is on this line)**
#sale.update(sale_params)
render_wizard #sale
end
def sale_params
params.require(:sale).permit(.... :plan, :appraisal, :flow)
end
In my model
has_one_attached :plan
has_one_attached :appraisal
has_one_attached :flow
validates :location, presence: true, if: -> { status?(:second_step) }
def status?(step_key)
status == step_key.to_s (this is to allow validations on each step - I have no validations defined for my attachments)
end
In my form when I reach the last step of the wizard to upload attachments it works fine so long as at least one of the attachments are present. However, if no attachments are present I get the following no method error on update click:
undefined method `[]=' for nil:NilClass
{"_method"=>"put", "authenticity_token"=>"[FILTERED]", "commit"=>"Continue", "sale_id"=>"37", "id"=>"fourth_step"}
I'm just wondering why this might be (is it something to do with the wicked gem or something else?) and is there a solution I can try? ty
Based on the error, the value of params[:sale] is nil when no attachment is uploaded. So when you try to set params[:sale][:status], it gives undefined method []=' for nil:NilClas error. You should add a presence check on params[:sale] before assigning a value to params[:sale][:status].
I got this working.
If anyone just happens to stumble on this issue - on my last step page of the wizard I had only file upload fields. When I put in an additional random input field now it works fine.
Thanks for guidance.

Active storage has_many_attached is purging previous uploads

I'm trying to add more files to a has_many_attached, but when I upload a new file the previous file is purged. Uploading multiple files does add multiple files, but they are all purged on the next upload as well. Is this intended behavior? if so, how do I prevent the purging?
log.rb
class Log < ApplicationRecord
has_many_attached :uploads
end
_form.html.erb
<%= form_for #log, remote: true do |f| %>
<%= f.file_field :uploads, multiple: true %>
<% end %>
You can prevent overwriting the existing attachments by adding the following line to config/environments/development.rb, config/environments/test.rb and config/environments/production.rb, as kindly indicated by quantavi in this issue: https://github.com/richardvenneman/trestle-active_storage/issues/41
config.active_storage.replace_on_assign_to_many = false
Apparently in Rails 6 the default behavior when uploading files again is to purge the previously uploaded files. You can find a longer thread about it in this Rails issue which Aarthi linked in a comment. The line above changes this setting so that consecutive uploads append files instead of overwriting the old ones.
(As you may have inferred from the link to the issue I ran into the same issue when using the Trestle admin panel with the complementary trestle-active_storage gem, that adds active storage field support.)
I kept my old assets with an hidden_field tag like this:
<% #product.photos.each do |ph| %>
<%= f.hidden_field :photos, multiple: true, value: ph.signed_id %>
<% end %>
This worked for me

Get path to ActiveStorage file on disk

I need to get the path to the file on disk which is using ActiveStorage. The file is stored locally.
When I was using paperclip, I used the path method on the attachment which returned the full path.
Example:
user.avatar.path
While looking at the Active Storage Docs, it looked like rails_blob_path would do the trick. After looking at what it returned though, it does not provide the path to the document. Thus, it returns this error:
No such file or directory # rb_sysopen -
Background
I need the path to the document because I am using the combine_pdf gem in order to combine multiple pdfs into a single pdf.
For the paperclip implementation, I iterated through the full_paths of the selected pdf attachments and load them into the combined pdf:
attachment_paths.each {|att_path| report << CombinePDF.load(att_path)}
Use:
ActiveStorage::Blob.service.path_for(user.avatar.key)
You can do something like this on your model:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_on_disk
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
I'm not sure why all the other answers use send(:url_for, key). I'm using Rails 5.2.2 and path_for is a public method, therefore, it's way better to avoid send, or simply call path_for:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_path
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
Worth noting that in the view you can do things like this:
<p>
<%= image_tag url_for(#user.avatar) %>
<br>
<%= link_to 'View', polymorphic_url(#user.avatar) %>
<br>
Stored at <%= #user.image_path %>
<br>
<%= link_to 'Download', rails_blob_path(#user.avatar, disposition: :attachment) %>
<br>
<%= f.file_field :avatar %>
</p>
Thanks to the help of #muistooshort in the comments, after looking at the Active Storage Code, this works:
active_storage_disk_service = ActiveStorage::Service::DiskService.new(root: Rails.root.to_s + '/storage/')
active_storage_disk_service.send(:path_for, user.avatar.blob.key)
# => returns full path to the document stored locally on disk
This solution feels a bit hacky to me. I'd love to hear of other solutions. This does work for me though.
You can download the attachment to a local dir and then process it.
Supposing you have in your model:
has_one_attached :pdf_attachment
You can define:
def process_attachment
# Download the attached file in temp dir
pdf_attachment_path = "#{Dir.tmpdir}/#{pdf_attachment.filename}"
File.open(pdf_attachment_path, 'wb') do |file|
file.write(pdf_attachment.download)
end
# process the downloaded file
# ...
end

Saving Images using Paperclip

I am just looking for some clarification with using paperclip for saving images. I am grabbing all my images stored in Flickr using the Flickraw Gem. I am grabbing the url for the image and saving that to my model,
Model
class Portfolio < ActiveRecord::Base
attr_accessible :taken, :title, :url, :url_large
end
then rendering using the image_tag helper..
Like so
<%= #portfolio.each do |p| %>
<%= image_tag(p.url_large, :size => "480x480") %>
<%= p.title %>
<% end %>
So this shows all my photos at 480 x 480.. However I understand that paperclip handles images better?
So i can install paperclip, add :avatar column to my Portfolio model (though ill prob call it photo) and its the next part i want to clarify.
Do i save the url to the image within the :avatar column and then use the paperclip helpers as normal? Im used to uploading physical images to my model using paperclip which generates a file name within that column (well thats from what I can see)
I save the attributes like so at the moment
flickr.photos.search(:user_id => FLICKR_USER_ID).each do |p|
info = flickr.photos.getInfo(:photo_id => p.id)
title = info.title
taken = info.dates.taken
square_url = FlickRaw.url_s(info)
original_url = FlickRaw.url_o(info)
Portfolio.where(title: title, url: square_url, taken: taken, url_large: original_url).first_or_create!
end
So where to save FlickRaw.url_o ?
Can anyone advise if im thinking about this correctly or do i have some things wrong?
Any help appreciated
You could check this other post save image from url by paperclip they explain how to insert a picture from an url.
But actually I think this will try to upload the image.

Problem with file uploads in a nested form using Rails3 with Mongoid and Carrierwave

Im having a problem transferring an SQLlite Rails 3 app over to a Mongoid Rails 3 app. In the SQLlite version, I am easily able to include an image upload form (using Paperclip) from one model ('image') within a nested form from another model ('product'). Here's my 'new' product form:
<%= form_for #product, :html => {:multipart => true} do |f| %>
<% f.fields_for :images do |image_form| %>
<%= f.label :productphoto %>
<%= f.file_field :productphoto %><br />
<% end %>
<% end %>
And here's the 'show' view:
<% #product.images.each do |image| %>
<%= image_tag image.productphoto.url(:gallerythumb) %><br />
<% end %>
When I try to use the same product views in my Mongoid Rails 3 app (using Carrierwave), I get the following error:
TypeError in Stores#show:
can't convert nil into String
<%= image_tag product.image.url(:gallerythumb) %>
Im pretty sure my models in the Mongoid version are correct because if I add a string (like 'name') to my 'image' model and nest that in the 'Product' form, it works. Also, Im able to upload an image into a non-nested model form.
Any help would be greatly appreciated!
I just had a similar problem myself. The problem is not the image upload I think, but the problem is that Rails doesn't recognize :images as being an Array. If you look into the Rails source of the fields_for helper you see that it checks for a method "_attributes=". If that's not there the form will be posted as normal fields and not as an array (params will be "images" instead of "images[0]")
You have to add the following line to your model:
accepts_nested_attributes_for :images
It is carrierwave or mongoid bug
https://github.com/jnicklas/carrierwave/issues#issue/81
This is most likely the issue that Lewy linked to -- that problem is specific to arrangements where your Carrierwave uploader is mounted on a child document in an embedded association and you are saving the parent, and though you don't explicitly show if this is how your data is modeled, I suspect that's the case since you noted that it works with a non-nested form (presumably saving the child document then, not the parent).
If you dig around in the discussions linked from that issue, you'll find some proposed workarounds. Here's what I ended up with to get Carrierwave working in this situation for me:
https://gist.github.com/759788
Full credit is to due to zerobearing2 whose gist I forked, I just made minor changes to get it working in Rails 3.0.3 and commented on my gist with summary info on the relevant discussions.

Resources