File uploading Mailboxer Gem - ruby-on-rails

I'm trying to send a message with an attachment in Mailboxer Gem.
My stack is : Rails 4 and Ruby 2.1.1
However, I can see that attachment using CarrierWave is already supported as the link in the code below.
https://github.com/ging/mailboxer/blob/4b2681c1790b823f7b493fb00b41e9899bb90ebe/app/models/message.rb#L13
However, I did my setup exactly like that. Normal message without an attachment is going fine.
This is my code :
Controller :
def create_message
if params[:user].present? & params[:message].present? & params[:subject].present?
current_user.send_message(User.find(params[:user]), params[:message], params[:subject])
redirect_to inbox_path
end
end
This is my view code :
<%= form_tag do %>
<%= select_tag 'user', options_from_collection_for_select(User.all, :id, :fullname) %><br/>
<%= text_field_tag 'subject' %><br/>
<%= text_area_tag 'message' %><br/>
<%= submit_tag 'Send' %>
<% end %>
The above code is working fine and the messages are getting sent, however, when I try to add the file field to it like so and try changing the controller code, the attachment is not getting uploaded :
def create_message
if params[:user].present? & params[:message].present? & params[:subject].present?
current_user.send_message(User.find(params[:user]), params[:message], params[:subject], true , params[:attachment])
redirect_to inbox_path
end
end
View :
<%= form_tag do %>
<%= select_tag 'user', options_from_collection_for_select(User.all, :id, :fullname) %><br/>
<%= text_field_tag 'subject' %><br/>
<%= text_area_tag 'message' %><br/>
<%= file_field_tag 'attachment' %>
<%= submit_tag 'Send' %>
<% end %>
I think this is a problem with the strong params. In Rails 3 I could have used attr_accessible. However how do I ensure that the attachment field is not being blocked and allowed?
P.S - I have the carrierwave gem installed and I have even restarted my server multiple times.
Thanks.

I am changing my previous answer, because in fact it wasn't correct.
Basically, if your problem is like mine, it could easily be solved by adding :multipart => true to your form. At least in my case, that's why carrierwave was not picking up the attachments.
I tried to solve the problem by extending the mailboxer Message class and setting up and mounting a completely new carrierwave object with a different attribute name. Basically, this allowed me to avoid working with the attachment attribute defined in mailboxer and to customize the attachments.
But this got very messy with the extension of the mailboxer message.rb Class. So I finally abandoned that course. Still, having your own uploader instead of relying on the mailboxer attachment is very convenient, especially if you want or need to upload your files to a different directory or to the cloud.
So finally, I have created a new model for my attachments and mounted a new carrierwave uploader on it. In that way, I can customize it as I want without having to tweak mailboxer, which has very little in terms of documentation or support.
Probably this is not useful to you anymore, but might help others!

Related

How to set mail attachments without saving file in database?

I have created simple application form and i was trying to upload file and send it as email attachment. And i did it with this approach:
class ApplyController < ApplicationController
def prepare_email_content
ApplyMailer.with(params).apply.deliver_now
end
end
class ApplyMailer < ApplicationMailer
def apply
#company = Company.find(params[:company_id])
#candidate = params[:name]
#candidate_mail = params[:email]
#email = #company.email
attachments[params[:cv].original_filename] = params[:cv].read
mail to: #email, subject: 'Hello'
end
end
<h1>APPLY</h1>
<%= form_tag(apply_path, method: :post, multipart: true) do %>
<%= label_tag(:name, "First and last name:") %>
<%= text_field_tag(:name) %>
<%= label_tag(:email, "Email:") %>
<%= text_field_tag(:email) %>
<%= hidden_field_tag :company_id, params[:company_id] %>
<%= file_field_tag 'cv' %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<%= link_to 'All offers', hello_path %>
Everything was working fine - i have tested application and it was fine. Then i have developed my application and when i come back to testing i started getting this error:
On the way i have installed some gems and updated few of them. I was checking out to commit where this feature was working and it is. But i'm not able to find any differences in my code. There were some changes in /.idea folder but i don't know if any of this files could trigger this issue.
I'm using rails 6.0.3 and ruby 2.5.8
EDIT
I can see that there is a problem inside called methods. Looks like it cannot find #sort_order value and it sets data value as nil. But i have no idea how to change working of this.
Replace the line you're attaching the file with
attachments["#{params[:cv].original_filename}"] = File.read(params[:cv].path)
Even though you're not saving the file, there is a still a local path to grab the file from.
Also, you should really consider passing the parameters to the mailer from the controller, rather than passing params in its entirety. That way if the information or format of the upload is incorrect, you can redirect back to the form and bypass sending the email.

Bates' nested_form with nested resource doesn't name new fields properly

I'm building a stackoverflow clone now, and trying to implement multiple file uploads on question and answer creation.
I'm using carrierwave with nested_form gem.
File upload on question creation works fine, but i can't make it work for answers.
Here's my form for new answer:
= nested_form_for [#question, #answer], remote: true do |f|
= render 'shared/error_messages', object: f.object
div.form-group
= f.label :body
= f.text_area :body, class: 'form-control'
div.form-group
= f.fields_for :attachments do |field|
= field.label :file
= field.file_field :file
= field.link_to_remove "Remove this attachment"
p
= f.link_to_add "Moar files!", :attachments
div.form-group
= f.submit 'Post answer', class: 'btn btn-primary'
It works OK when I'm trying to upload single file. The name of nested file_field goes like this:
name="answer[attachments_attributes][0][file]"
But when I click on 'Moar files' and another file field appears, it happens to be named
name="question[attachments_attributes][1429430703012][file]"
And thus in params I get second file tied to question, not the answer. And since request is being handled by answers_controller, second file is just being ignored.
How can I make nested_form_for name new field properly?
UPD
So I've tried to explicitly state parent object for attachments like this:
= f.fields_for :attachments, #answer.attachments do |field|
Still no luck
Turns out this is an old issue with nested_form, when it's being used more than one time on a page for two different objects with same polymorphic association.
Here data-blueprint-id conflict on Nested form for polymorphic associations is another question describing same issue.
Hope it will help someone.

Uploading multiple images to S3 in nested Rails form

I'm trying to get file uploading to work with a nested fields_for tag in a Rails 4 app. I've followed several Railscasts, namely: 253, 381, 383, but still can't quite get it fully functioning. Also using Carrierwave & jquery file upload.
Basic app structure is as follows:
blogpost.rb
class Blogpost < ActiveRecord::Base
has_many :blogpics
end
blogpic.rb
class Blogpic < ActiveRecord::Base
belongs_to :blogpost
end
blogposts_controller.rb
def new
#blogpost = Blogpost.new
blogpic = #blogpost.blogpics.build
end
blogpost_form.html.erb
<div>
<%= form_for #blogpost do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.hidden_field :post_id %>
<%= f.text_field :title %>
<%= f.text_field :location %>
<%= f.text_area :content %>
<%= f.fields_for :blogpics do |builder| %>
<%= builder.file_field :image %>
<%= builder.hidden_field :blogpost_id %>
<% end %>
<p><%= f.submit %></p>
<% end %>
Uploading a single file works. But, adding ":multiple => true, :name => 'blogpic[image]'" to the file field breaks functionality and no files upload.
When I edit blogposts_controller.rb as such:
def new
#blogpost = Blogpost.new
3.times do
blogpic = #blogpost.blogpics.build
end
end
I am able to input three files individually, then upload successfully. Is there any way I can achieve this functionality while being able to drag & drop multiple files into one input?
I really appreciate any help and direction, thanks.
Your blogpost model is missing an accepts_nested_attributes association.
class Blogpost < ActiveRecord::Base
has_many :blogpics
accepts_nested_attributes_for :blogpics
end
I'm not quite sure how handling multiple files in one dialog box. I'd imagine you'd be using some javascript to detect that multiple files were selected, and creating field forms for each of them.
You can pass :multiple => true as a param on builder.file_field :image. See http://apidock.com/rails/ActionView/Helpers/FormTagHelper/file_field_tag for details
With the multiple attribute on the file input, you can drag and drop ONTO the input element
See for details http://www.html5rocks.com/en/tutorials/file/dndfiles/#toc-selecting-files
I've run into the same issue (where multiple: true breaks the nested form) and my understanding is that you have to manually transform the params before the controller receives it. If you inspect (using pry or debugger) the params hash, you need to compare between submitting Parent Model with several input files (on individual inputs) VERSUS Parent Model with multiple input files (in one input). The former creates an array of child objects (each with their own file), while the latter creates only one child object that has all the images in one array.

Trouble using Simple Form on a rails starter project

I'm getting the following error while trying to create a form, undefined method 'model_name' for NilClass:Class
This project of mine is to capture my ideas. I could use a google doc but figured this should be easy enough to try and code an app for myself. Below is the code as I think it should be from what I read on the simple form site in my newopp.html.erb (in my opps view folder.
I created a controller and a model and I am certain part of my problem is the fact that I can't figure out what I need to code or what to add for code to properly complete this step. All the tutorials I have looked at gave me a couple ideas to play with and try to make work to no avail.
So basically I am sitting on a rails generated controller named opps_controller.rb and a model called opp.rb. Both of these are nothing more than what the generator provided since I had to go back to square one
Simple form code that I have started with
<%= simple_form_for #opp do |f| %>
<%= f.input :title %>
<%= f.input :subtitle %>
<%= f.input :description %>
<%= f.input :idea %>
<%= f.input :added %>
<%= f.button :submit %>
<% end %>
opp.rb file
class Opp < ActiveRecord::Base
attr_accessible :added, :description, :idea, :subtitle, :title
end
If it makes a difference, I have migrated the database, I ran the simple form install script with the bootstrap configuration. I'm using rails 3.2.9 and ruby 1.9.3p362 (2012-12-25 revision 38607) [x86_64-darwin12.2.1]
As I mentioned I just have a blank controller that was created using a generator and I need to get the CRUD functionality working. Everything I have tried has failed at this point. I appreciate any assistance you can provide.
It shouldn't be newopp.html.erb, just new.html.erb. So the path would be /views/opps/new.html.erb
If you're still getting an error then make sure #opp is defined in the controller:
class OppsController < ApplicationController
def new
#opp = Opp.new
end
def create
#opp = Opp.new
if #opp.update_attributes(params[:opp])
...
else
render 'new'
end
end
end

rails simple_form fields not related to the model

I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.
But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.
If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.
In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:
<%= simple_form_for #user do |f| %>
<%= f.input :agreement, as: :fake %>
....
Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.
UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
end
end
You can use attr_accessor
class Order < ActiveRecord::Base
attr_accessor :card_number
end
Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.
See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor
and here for a useful stackoverflow question What is attr_accessor in Ruby?
Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible
The best way to handle this is to use simple_fields_for like so:
<%= simple_form_for #user do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= simple_fields_for :other do |o| %>
<%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
<% end %>
<% end %>
In this example, I have added a new field called change_password which is not part of the underlying user model.
The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by #baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.
Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.
I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...
Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Example:
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
<%= devise_error_messages! %>
<% resource.class.invite_key_fields.each do |field| -%>
<%= f.input field %>
<%= hidden_field_tag :object_name, #object.class.name %>
<%= hidden_field_tag :object_id, #object.id %>
<% end -%>
I found a very simple (and somewhat strange) workaround.
Just add the input_html option with any value key inside. E.g:
= simple_form_for #user do |f|
= f.input :whatever, input_html: {value: ''}
Tested simple_from versions: 3.2.1, 3.5.1

Resources