How to send multiple files using dropzone.js with Rails? - ruby-on-rails-4

Using Rails 4 Paperclip and SimpleForm.
I'm trying to make a multiple file upload. For this I am using Dropzone.js upon customer request.
It is a form where I have: Name, Address, Phone, Documents, Certificates.
So I created 2 separate dropzones for documents and certificates.
This is my code on the form:
= simple_form_for(#user, html: {multipart: true, autocomplete: 'off' }) do |f|
= f.input :name, label: false # Column name in table User
= f.input :address, label: false # Column address in table User
#attachments-documents.dropzone # Column document in table User
#attachments-certificates.dropzone # Column certificate in table User
:javascript
var attachments_1 = new Dropzone("div#attachments-documents", { url: "#{upload_file_biddings_path}"});
var attachments_2 = new Dropzone("div#attachments-certificates", { url: "#{upload_file_path}"});
Dropzone.options.attachmentsDocuments = {
paramName: 'user[document]',
maxFilesize: 20,
parallelUploads: 3,
addRemoveLinks : true,
autoProcessQueue: false
}
Dropzone.options.attachmentsCertificates = {
paramName: 'user[certificate]',
maxFilesize: 20,
parallelUploads: 3,
addRemoveLinks : true,
autoProcessQueue: false
}
Controller:
Class User < ApplicationController
[...]
def create
work = Work.new(work_params)
if work.save
flash[:notice] = "Se ha creado correctamente la Obra."
redirect_to :action => :index
else
work.errors.messages.each do |attribute, error|
puts error.to_s
puts error
end
flash[:error] = "Ha ocurrido un error en el sistema."
redirect_to :action => :index
end
end
def update
work = Work.find(params[:id])
if work.update_attributes(work_params)
flash[:notice] = "Se ha actualizado correctamente los datos."
redirect_to :action => :index
else
work.errors.messages.each do |attribute, error|
flash[:error] = attribute " " + flash[:error].to_s + error.to_s + " "
end
# Load new()
#work = work
render :edit, layout: false
end
end
def upload_file
puts params
render :json => params
end
private
def work_params
params.require(:user).permit(:name, :address, :document, :certificate)
end
end
But here I have 2 problems.
1) When I put a file into the Dropzone, by default calls the "upload_file" function when the files should be uploaded when you click on my submit button and GO to create function.
2) I did a test upload multiple files with another JS, however only managed to climb the last of them. Ie:
I put in my file_field 3 files: file1, file2, file3. But in DB, only recorded the file3.
Has anyone uploaded multiple files with Paperclip? Using JS Dropzone.js or other successfully?
If so, I'd like to share knowledge.

Related

ActiveAdmin form validation before save

I have form fields from one model status_history that I am including in my member edit. I am wanting to make it so that if the fields for status_history are empty, then it will not save. Currently it is saving blank items to status_history when I save a members edit.
My member form looks like this
form(:html => { :multipart => true }) do |f|
f.semantic_errors *f.object.errors.keys
columns do
column do
...
end
column do
f.inputs "Status" do
f.semantic_fields_for :status_histories, StatusHistory.new do |sh|
sh.inputs :class => "" do
sh.input :status, as: :select, collection: {Active: "active", Inactive: "inactive", Separated: "separated"}
sh.input :date, :as => :datepicker
sh.input :reason
end
end
table_for member.status_histories do
column "status" do |status_histories|
status_histories.status
end
column "date" do |status_histories|
status_histories.date
end
column "reason" do |status_histories|
status_histories.reason
end
end
end
...
end
end
f.actions
end
models/status_histories
class StatusHistory < ActiveRecord::Base
belongs_to :member
STATUS_TYPES = [ "active", "inactive", "separated" ]
validates :status, inclusion: STATUS_TYPES
validates :date, :presence => true
validates :reason, :presence => true
end
Even adding a button that would toggle the semantic_fields_for would work but currently if I leave them blank I get validates errors.
How would I override the save method to check if status and date are present and if so save the status_history and if not, do not save the status_history but save the rest of the member fields?
Try this:
in Member ActiveRecord model
accept_nested_attributes_for :status_histories, reject_if: :all_blank
http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for

Rails 4 paperclip Background job

CURRENT LOGIC
First, I think I have read all the articles on paperclip and I'm still stucked despite all the infos learned... So I can say taht your help is really really precious.
Secondly, I do not use delayed_paperclip, nor s3_direct_upload (but jquery directUpload).
I have users profile with 4 differents pictures : logo, avatar, worka, workb
Each format (logo, avatar, worka, workb) has 3 styles (:small, :thumb, :medium)
User can update its pictures => so update action is concerned
The 4 files fields are in a form with other classic fields (name, email, ...)
Upload is managed by jQuery DirectUpload + Paperclip
When user click the file field to add an image:
jQuery DirectUpload uploads the file into a temp directory on s3
jQuery callbacks with the url(key)
The url is assigned as :temp to an hidden field generated in javascript
When form submit button is pressed:
I assign to paperclip the url of the file uploaded with direct upload, with the help of #user.logo.temp which contains the url(key)
Paperclip generates all styles
<input type="hidden" name="user[logo_attributes][temp]" value="https://bucketname.s3.amazonaws.com/temp/e4b46d01-5d69-483b.jpg">
MY PROBLEM: STYLES GENERATION BRINGS ME TO HEROKU IDLE and TIMEOUT #15 #12
.
ATTEMPTS: I tried to isolate the upload process to put it in a background job
.
I can't figure out how to block paperclip styles generation at the first upload and generate them after in a background job
before_post_process block all post process, even in background job
I didn't use .reprocess! Since paperclip 4, update is triggered and... infinite loop..., so I use .assign and .save instead
The file is correctly assigned from S3 hosted file, then processed by paperclip
I'm not sure about the file from file field, if it is uploaded or not (no trace of that in the console, but since the form is submited, the file too, even if unused.
.
NEED: STYLES BLOCKED THEN PROCESSING IN A BACKGROUND JOB
My Logo Model
class Logo < Document
S3_TEMP_URL_FORMAT = %r{\/\/bucketname\.s3\.amazonaws\.com\/(?<path>temp\/.+\/(?<filename>.+))\z}.freeze
has_attached_file :attachment,
styles: { medium: "300x300#" },
convert_options: { medium: "-quality 75 -strip" },
default_url: ":parent_type/:class/:style/missing.png",
path: "/documents/:parent_type/:id_partition/:class/:style/:basename.:extension"
validates_attachment :attachment,
content_type: { content_type: ["image/gif", "image/png", "image/jpg", "image/jpeg"] },
size: { less_than: 1.megabyte }
validates :temp,
# presence: true,
format: { with: S3_TEMP_URL_FORMAT }
before_save :set_attachment
after_save :set_remote_url
before_post_process :stop_process
def stop_process
false
end
def styles_process
self.attachment.assign(attachment)
self.save
end
def set_attachment
# puts "BEGIN -- SET ATTACHMENT"
tries ||= 5
s3_temp_url_data = S3_TEMP_URL_FORMAT.match(self.temp)
s3 = AWS::S3.new
s3_temp_head = s3.buckets[ENV['S3_BUCKET']].objects[s3_temp_url_data[:path]].head
self.attachment_file_name = s3_temp_url_data[:filename]
self.attachment_file_size = s3_temp_head.content_length
self.attachment_content_type = s3_temp_head.content_type
self.attachment_updated_at = s3_temp_head.last_modified
rescue AWS::S3::Errors::NoSuchKey => e
tries -= 1
if tries > 0
sleep(3)
retry
else
false
end
end
def set_remote_url
s3_temp_url_data = S3_TEMP_URL_FORMAT.match(self.temp)
s3 = AWS::S3.new
self.attachment = URI.parse(self.temp)
self.save
s3.buckets[ENV['S3_BUCKET']].objects.with_prefix(s3_temp_url_data[:path]).delete_all
end
end
My Controller
def update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
# Here is the styles processing
# This is where the Resque background job would go
#user.logo.styles_process
set_flash_message :notice, :updated
redirect_to after_update_path_for(#user)
else
render :edit
end
end
My Form
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), method: :put, html: { class: "form-horizontal directUpload", role: "form" }) do |f| %>
<%= f.fields_for :logo do |l| %>
<%= l.file_field(:attachment, accept: 'image/gif,image/png,image/jpg,image/jpeg') %>
<% end %>
<input type="hidden" name="user[logo_attributes][temp]" value="https://bucketname.s3.amazonaws.com/temp/e4b46d01-5d69-483b.jpg">
<% end %>

passing an id in Rails 4

If i start this question by showing you my routes: -
c:\Sites\work\easygifts>rake routes
Prefix Verb URI Pattern Controller#Action
writing_stores GET /stores/writing(.:format) stores#writing
office_stores GET /stores/office(.:format) stores#office
time_stores GET /stores/time(.:format) stores#time
home_stores GET /stores/home(.:format) stores#home
wellness_stores GET /stores/wellness(.:format) stores#wellness
travel_stores GET /stores/travel(.:format) stores#travel
bags_stores GET /stores/bags(.:format) stores#bags
leisure_stores GET /stores/leisure(.:format) stores#leisure
quote_stores GET /stores/quote(.:format) stores#quote
stores GET /stores(.:format) stores#index
POST /stores(.:format) stores#create
new_store GET /stores/new(.:format) stores#new
edit_store GET /stores/:id/edit(.:format) stores#edit
store GET /stores/:id(.:format) stores#show
PATCH /stores/:id(.:format) stores#update
PUT /stores/:id(.:format) stores#update
DELETE /stores/:id(.:format) stores#destroy
products GET /products(.:format) products#index
POST /products(.:format) products#create
new_product GET /products/new(.:format) products#new
edit_product GET /products/:id/edit(.:format) products#edit
product GET /products/:id(.:format) products#show
PATCH /products/:id(.:format) products#update
PUT /products/:id(.:format) products#update
DELETE /products/:id(.:format) products#destroy
root GET / stores#index
The issue i am having is getting the :id into the 'quote' view.
I am wanting to see in my routes; quote_stores GET /stores/quote/:id(.:format) stores#quote Or something like it.
Can :id only be passed through CRUD?? I thought i could pass instance variables through pretty much anywhere so I wrote this in my view as the link to the view with the :id info passed into it.
<% #products.each do |office| %>
<div class="item">
<%= link_to image_tag(office.image_url), image_path(office.image_url), class: 'fancybox' %>
<p><strong><%= office.item_code%></strong>
</br><em><%= truncate(office.title, length: 18) %></em></p>
<p class="showArticle"><%= link_to 'Show Article', store_path(office) %></p>
<p class="addTo"><%= link_to 'Price Info', quote_stores_path(office) %></p>
</div>
<% end %>
I am referring to the <%= link_to 'Price Info', quote_stores_path(office) %> which upon click takes you to the correct view and in the URI path it even lists the correct :id however it does not pass into the view that :id's information.
My controller code is as follows: -
class StoresController < ApplicationController
add_breadcrumb 'home', :stores_path
def index
#products = Product.all
end
def show
#products = Product.find(params[:id])
if #products.nil?
redirect_to action: :index
end
add_breadcrumb 'Back', #products.section
end
def writing
#products = Product.where(:section => 'writing').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'writing', writing_stores_path
end
def office
#products = Product.where(:section => 'office').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'office', office_stores_path
end
def time
#products = Product.where(:section => 'time').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'time', time_stores_path
end
def home
#products = Product.where(:section => 'home').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'home', home_stores_path
end
def wellness
#products = Product.where(:section => 'wellness').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'wellness', wellness_stores_path
end
def travel
#products = Product.where(:section => 'travel').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'travel', travel_stores_path
end
def bags
#products = Product.where(:section => 'bags').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'bags', bags_stores_path
end
def leisure
#products = Product.where(:section => 'leisure').paginate(:per_page => 5, :page => params[:page])
add_breadcrumb 'leisure', leisure_stores_path
end
def quote
#products = Product.find_by(params[:id])
if #products.nil?
redirect_to action: :index
end
end
end
So apart from my code not being DRY, what am i missing here please? What am i not understanding about :id's?
In your config/routes.rb, you probably have this line:
resources :stores
Which creates routes for the standard CRUD actions of the stores resource. You can define additional actions for this resource which either apply to the collection (multiple products) or the members individually (a single product).
Please note that it would probably more logical to name the resource products rather than stores, since it seems to be handling Products.
In your case, you'd want to define an additional member action. Since it applies to a single product, Rails will define a route which takes an id parameter:
resources :stores do
member do
get 'quote'
end
end
This will generate the following route (rake routes):
quote_store GET /stores/:id/quote(.:format) stores#quote
Note that the route is called quote_store, the singular form rather than the plural.
Also see the Rails guides for more information about collection and member routes.

Full Calendar and auto Filtering Events Rails 4

I am building a calendar application which requires certain events to display on the calendar based on the location that is being viewed. I have full calendar working in which it displays ALL the events in the database. I am trying to achieve an auto filter to only show the events that pertain to the location being viewed.
Current Setup (My Events Model is called "Campaigns" to align with my application)
Campaign Controller
def index
#campaigns = Campaign.all
#campaigns = #campaigns.after(params['start']) if (params['start'])
#campaigns = #campaigns.before(params['end']) if (params['end'])
respond_to do |format|
format.html # index.html.erb
format.json { render json: #campaigns }
end
end
Campaign Model
belongs_to :location
scope :before, lambda {|end_time| {:conditions => ["ends_at < ?", Campaign.format_date(end_time)] }}
scope :after, lambda {|start_time| {:conditions => ["starts_at > ?", Campaign.format_date(start_time)] }}
# need to override the json view to return what full_calendar is expecting.
# http://arshaw.com/fullcalendar/docs/event_data/Event_Object/
def as_json(options = {})
{
:id => self.id,
:title => self.name,
:description => "",
:start => starts_at.rfc822,
:end => ends_at.rfc822,
:allDay => false,
:recurring => false,
:url => Rails.application.routes.url_helpers.campaign_path(friendly_id)
}
end
def self.format_date(date_time)
Time.at(date_time.to_i).to_formatted_s(:db)
end
Script inside "Location" show.html.erb
$(document).ready(function() {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
$('#calendar').fullCalendar({
editable: true,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
defaultView: 'month',
loading: function(bool){
if (bool)
$('#loading').show();
else
$('#loading').hide();
},
// a future calendar might have many sources.
eventSources: [{
url: '/campaigns',
data: { <%= #locations %> }, #added but not filtering as I had hoped
color: 'blue',
textColor: 'white',
ignoreTimezone: false
}],
timeFormat: 'h:mm t{ - h:mm t} ',
dragOpacity: "0.5",
});
});
Currently I am able to achieve in displaying the campaigns that belong to the given location on the display side in the location show with:
<strong>Campaigns:</strong>
<%= render partial: #campaigns %>
</p>
And then in the location controller
def show
#campaigns = #location.campaigns
end
I have tried for hours to figure this out with no luck in getting the same result to the calendar. Can someone please help me in figuring out what is required to filter the "Campaigns" that pertain to the viewing location??
Thanks!
Have you tried this ?
eventSources: [{
url: '/campaigns',
data: { 'location': <%= #locations %> }, <- try this like so
color: 'blue',
textColor: 'white',
ignoreTimezone: false
}],

Rails 4 testing controller spec ,devise helper sign_in doesn't work

I have Rails 4.2.6 and rspec 3.3.0 , devise version 3.5.8 and i have trouble to test my controller specs always getting error saying ("expected the response to have a success status code (2xx) but it was 401")
Later when i try different spec I am always getting failure message ("Your account is not enabled yet !"), any ideas ?, any help much appreciated ..
my spec:
require 'rails_helper'
describe MyController, :type => :controller
let(:user) { create(:user) }
let(:campaign) { create(:campaign, user: user) }
let!(:placement) { create(:placement, user: user, campaign: campaign, end_date: Date.today) }
before(:each) do
sign_in user
end
context "when we have no data" do
before do
get :graph_data, format: :json
#json = JSON.parse(response.body)
end
it "should be a success" do
expect(response).to have_http_status(:success)
end
end
end
I have following spec_helper:
config.include Devise::TestHelpers, :type => :controller
config.before(:each) do
DatabaseCleaner.start
end
factory defined:
FactoryGirl.define do
factory :unconfirmed_user, class: User do
sequence(:email) { |n| "john#{n}#email.com" }
sequence(:name) { |n| "John Nice #{n}" }
password 'password'
password_confirmation 'password'
factory :user do
confirmed_at Time.now.utc
factory :admin do
organisation { create(:organisation, :some_organisation) }
end
end
end
controller:
MyController < ApplicationController
def graph_data
items = current_user.items //outputs [1,2,3,45,5]
render json: items, status: :ok
end
end
routes:
GET /mycontroller/graph_data(.:format)
i found what was the issue,on User model had a method 'active_for_authentication?'
def active_for_authentication?
super && (group.include?('company-group) || is_admin? )
end
Method 'active_for_authentication?' is a Devise public method
and when it is defined in your model like (User), you change behaviour of it, and in my case it was overwritten with different behaviour,
checking for belonging user to the 'company-group' or not ..
That's why i had failure message ("Your account is not enabled yet !")