I have been beating my head into the wall for the last week trying to figure this problem out.
I have a Ruby on Rails application that has a form submission that has many file uploads as well as input fields. Upon successful submission the form info is saved to a Postgres DB and an email is sent out with the submitted info and the attached files. This works correct if either I disable validations or if the form doesn't fail an of the validations, however if a validation fails a problem crops up.
If a file is selected and then the submit button is clicked and the form fails validations some of the file fields will either by disabled or completely deleted from the html. It will also rearrange the field fields from the original. For example Additional Document(s) is upload49, but after failed validation the file field is now upload25.
I have tried every Google search I can think of to track down the problem. The closest that I have found is the problem with AJAX and file uploads not being allowed by default for security reasons, which is why Carrierwave is required. I noticed that when the error 'ajax:aborted:file' was occurring this was what was deleting the file fields. The other thing I found is that when the submit button is pushed and the form is being processed that all file fields are temporarily disabled and I can only assume that somewhere something was breaking that was preventing them being re-enabled when validations failed.
If I added a listener for 'ajax:aborted:file' and had it return false it would stop the file fields from being messed up, but it would cause the form submission and validations to stop working.
Any help would be extremely appreciated. Let me know if you have any questions or need more code.
Here are some code snippets.
# uploaders/AttachmentsUpload.rb
class AttachmentsUploader < CarrierWave::Uploader::Base
# Choose what kind of storage to use for this uploader:
# storage :fog
storage :file # for local testing, saving to S3 in production
# 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
# models/buyer_under_contract.rb
class BuyerUnderContract < ApplicationRecord
include AttributeOption
belongs_to :user
belongs_to :team
mount_uploader :upload1, AttachmentsUploader
mount_uploader :upload2, AttachmentsUploader
mount_uploader :upload3, AttachmentsUploader
mount_uploader :upload4, AttachmentsUploader
mount_uploader :upload5, AttachmentsUploader
mount_uploader :upload6, AttachmentsUploader
...
...
mount_uploaders :upload49, AttachmentsUploader
after_create :send_notification
# Agent Info Validations
validates :location, :agent_fname, :agent_lname,
:agent_email,
presence: true
validates :prior_contract,
presence: true,
if: :prior_contract_status?
validates :previous_transaction_coordinator,
presence: true,
if: :previous_contract?
....
....
....
def send_notification
NotificationMailer.buyer_under_contract_form(self).deliver
end
end
# controllers/buyer_under_contracts.rb
class BuyerUnderContractsController < ApplicationController
include ListItemsController
responders :ajax_modal, :collection
respond_to :js, only: %i[index new create edit update]
respond_to :html, only: [:index]
load_and_authorize_resource
before_action :restrict_users, only: %i[new create edit update]
def new
list_item.user = current_user
respond_with(list_item)
end
def create
puts '@create' + list_item.inspect
created = list_item.save
if created
redirect_to URI(request.referer).path
return
end
puts '@errors' + list_item.errors.inspect
respond_with(list_item)
end
def update
updated = list_item.update_attributes(resource_params)
return if resource_params[:_update_type] == 'inline'
if updated
target = URI(request.referer).to_s
redirect_to target
return
end
respond_with(list_item)
end
def buyer_under_contract_params
numeric = %i[location zip mls purchase_price broker_commission_percentage
broker_commission_fee buyer_transaction_fee]
params[:buyer_under_contract].each do |param, value|
if param.to_sym == :communication_pref
value.delete_if { |x| x == '' }
params[:buyer_under_contract][param] = value.join(', ')
end
if param.to_sym == :documents_present
value.delete_if { |x| x == '' }
params[:buyer_under_contract][param] = value.join(', ')
end
next unless numeric.include?(param.to_sym)
params[:buyer_under_contract][param] = value.gsub(/[^0-9.]/, '')
end
# I know this is bad practice, was having problems with
# unpermitted parameters even though I permitted them,
# just for testing now.
params.require(:buyer_under_contract).permit!
end
# views/_form.html.haml
= simple_form_for(@buyer_under_contract, remote: true) do |form|
-if @buyer_under_contract.errors.any?
.modal-header.error-container
-@buyer_under_contract.errors.messages.each do |attr, _msg|
.row
=I18n.t(:"simple_form.labels.buyer_under_contract.#{attr}") + " is required"
.modal-body.container
%div.hidden.container
= form.input :user_id, value: User
%div.agent_info.container
%h2 Agent Information
%div.container
%h3.form *Location
= form.input :location, placeholder:'Location', label:false, collection: Team.all, prompt: 'Location', wrapper_html: {class: 'col-sm-5'}, input_html: { data: { placeholder: 'Location', 'allow-clear': false } }
Select the city your office is located in
%div.container
%h3.form Agent Name (Your Name)
= form.input :agent_fname, label:'First', wrapper_html: { class: 'col-sm-5' }
= form.input :agent_lname, label:'Last', wrapper_html: { class: 'col-sm-5' }
...
...
...
%div.documents.container(id = 'doc_uploads')
%h3.form Documents Upload(s)
%div.container.hidden(id = 'doc_addendum_purchase_agree_arbitration')
%h5 Addendum to Purchase Agreement/Arbitration
= form.file_field :upload1, multiple: false, wrapper_html: { class: 'col-sm-6'}
%div.container.hidden(id = 'doc_addendum_purchase_agree_cic')
%h5 Addendum to Purchase Agreement/CIC (if applies)
= form.file_field :upload2, multiple: false, wrapper_html: { class: 'col-sm-6'}
%div.container.hidden(id = 'doc_addendum_purchase_agree_inspect')
%h5 Addendum to Purchase Agreement/Inspections
= form.file_field :upload3, multiple: false, wrapper_html: { class: 'col-sm-6'}
...
...
%div.container.hidden(id = 'doc_additional')
%h5 Additional Documents
= form.file_field :upload49, multiple: true, wrapper_html: { class: 'col-sm-6'}
.modal-footer
= form.button :submit, class: "btn btn-success contract_submit_button", data: {disable_with: "Please wait…"}, value: 'Submit'
= link_to "Cancel", "#", class: "btn btn-danger", data: {dismiss: "modal"}, type: "button"
# assets/buyer_under_contract.js.coffee
# Some complicated JS to hide and unhide fields, but no
# disabling of fields, I can attach if it's needed
# mailers/notification_mailer.rb
class NotificationMailer < ApplicationMailer
default from: "info@test.com"
def buyer_under_contract_form(submission)
@submission = submission
@user = User.find_by_id(@submission.user_id)
@address = @submission.address
@location = Team.find_by_id(@submission.location)
puts 'address: ' + @address.to_s
puts 'user: ' + @user.to_s
puts 'location: ' + @location.to_s
attachments.inline['white-logo.png'] = File.read('app/assets/images/white-logo.png')
(1..49).to_a.each do |value|
begin
if @submission.send("upload#{value}").is_a?(Array)
@submission.send("upload#{value}").each do |attachment|
file_url = attachment.to_s
next if file_url == ''
puts 'file url: ' + file_url
puts "#{Rails.root}/public/#{file_url}"
attachments.inline[attachment.filename] = File.read("#{Rails.root}/public/#{file_url}")
end
else
file_url = @submission.send("upload#{value}").to_s
next if file_url == ''
puts 'file url: ' + file_url
puts "#{Rails.root}/public/#{file_url}"
attachments.inline[@submission.send("upload#{value}").filename] = File.read("#{Rails.root}/public/#{file_url}")
end
rescue
puts 'Mailer/Attachment Failure'
puts $!.message
next
end
end
mail(to: "testuser@example.com",
subject: "New Buyer Closing File at #{@address} - #{@location}",
reply_to: @user.email
)
end
end


