Rails PaperClip drag and drop multiple files - ruby-on-rails

I am using PaperClip with Rails to upload files and it works fine, however it would like to implment a drag and drop fileupload that allows for uploading of multiple files. and that each file shoudn't be more than a certain size.
Edit:
Here is what is what i have so far, i have created the javascript part. However i am lost as how to create the controller part:
var $dropArea = $(".drop-area");
$dropArea.bind({
dragover: function () {
$(this).addClass('hover');
return false;
},
dragend: function () {
$(this).removeClass('hover');
return false;
},
drop: function (e) {
e = e || window.event;
e.preventDefault();
e = e.originalEvent || e;
var files = (e.files || e.dataTransfer.files);
var $img = $('<img src="" class="uploadPic" title="" alt="" />');
for (var i = 0; i < files.length; i++) {
(function (i) {
var reader = new FileReader();
reader.onload = function (event) {
var newImg = $img.clone().attr({
src: event.target.result,
title: (files[i].name),
alt: (files[i].name)
});
$("body").append(newImg);
};
reader.readAsDataURL(files[i]);
var xhr = new XMLHttpRequest();
var fd = new FormData();
fd.append(files[i].name, files[i]);
xhr.open("POST", 'url', true);
xhr.send(fd);
})(i);
}
return false;
}
});
And this is the basic controller part:
def create
#image = Image.new(params[:image])
if #image.save
respond_to do |format|
format.html { redirect_to action: 'index', :notice => 'Image saved'}
format.js { redirect_to action: 'index', :notice => 'Image saved'}
format.xml { redirect_to action: 'index', :notice => 'Image saved'}
end
else
flash[:notice] = "Error, Please try again"
redirect_to action: 'new'
end
end
How can i do this?
Thanks

After some research, i found that it could be easily done like this:
var dropArea = document.getElementById("droparea"),
viewArea = document.getElementById("previewarea");
dropArea.addEventListener("drop", function(evt){
evt.preventDefault();
evt.stopPropagation();
previewFiles(evt.dataTransfer.files);
return false;
}, false);
function previewFiles(files){
for (i=0; i < files.length; i++){
if (typeof FileReader != "undefined"){
var img = document.createElement("img");
viewArea.appendChild(img);
var reader = new FileReader();
reader.onload = (function(theImg){
return function(evt){
theImg.src = evt.target.result;
}
}(img));
reader.readAsDataURL(files[i]);
}
}
uploadFiles(files);
}
function uploadFiles(files){
var fd = FormData();
var position = 0;
var max = files.length;
if (typeof fd != "undefined"){
function queue(){
if (max >= 1 && position <= max - 1){
fd.append("file", files[position]);
upload();
}
}
function upload(){
$.ajax({
url: '/boxes/hiThere',
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data){
position = position + 1;
queue();
}
});
}
queue();
}
}
dropArea.addEventListener("dragenter", function(evt){
if (evt){
this.className = "drag-enter";
}
endFn(evt);
}, false);
dropArea.addEventListener("dragleave", function(evt){
this.className = "";
endFn(evt);
}, false);
dropArea.addEventListener("dragover", function(evt){
endFn(evt);
evt.dataTransfer.dropEffect = 'move';
return false;
}, false);
function endFn(evt){
evt.preventDefault();
evt.stopPropagation();
}
and simply add the normal save in rails like this:
def hiThere
box = Box.new(params[:box])
box.user_id = current_user.id
box.file_path = params[:file]
if box.save!
set_flash "File saved sucessfully"
else
set_flash "Error, please try again"
end
respond_to do |format|
format.js { redirect_to action: :index, :notice => "Done" }
end
end

Related

Creating subscription in stripe

I am getting this error
`Stripe::InvalidRequestError (This customer has no attached payment source):
app/controllers/subscriptions_controller.rb:24:in `create`
when I try to subscribe to a plan.
here is my code
`
class SubscriptionsController < ApplicationController
layout "subscribe"
before_action :authenticate_user!, except: [:new, :create]
def new
if user_signed_in? && current_user.subscribed?
redirect_to root_path, notice: "You are already a subscriber"
end
end
def create
Stripe.api_key = Rails.application.credentials.stripe_api_key
plan_id = params[:plan_id]
plan = Stripe::Plan.retrieve(plan_id)
token = params[:stripeToken]
customer = if current_user.stripe_id?
Stripe::Customer.retrieve(current_user.stripe_id)
else
Stripe::Customer.create(email: current_user.email, source: token)
end
subscription = customer.subscriptions.create(plan: plan.id)
options = {
stripe_id: customer.id,
stripe_subscription_id: subscription.id,
subscribed: true
}
options.merge!(
card_last4: params[:user][:card_last4],
card_exp_month: params[:user][:card_exp_month],
card_exp_year: params[:user][:card_exp_year],
card_type: params[:user][:card_type]
) if params[:user][:card_last4]
current_user.update(options)
redirect_to root_path, notice: "Your subscription was setup successfully!"
end
def destroy
customer = Stripe::Customer.retrieve(current_user.stripe_id)
customer.subscriptions.retrieve(current_user.stripe_subscription_id).delete
current_user.update(stripe_subscription_id: nil)
current_user.subscribed = false
redirect_to root_path, notice: "Your subscription has been canceled."
end
end
`
stripe.js
document.addEventListener("turbolinks:load", () => {
const publishableKey = document.querySelector("meta[name='stripe-key']").content;
const stripe = Stripe(publishableKey);
const elements = stripe.elements({
fonts: [{
cssSrc: "https://rsms.me/inter/inter-ui.css"
}],
locale: "auto"
});
const style = {
base: {
color: "#32325d",
fontWeight: 500,
fontFamily: "Inter UI, Open Sans, Segoe UI, sans-serif",
fontSize: "16px",
fontSmoothing: "antialiased",
"::placeholder": {
color: "#CFD7DF"
}
},
invalid: {
color: "#E25950"
}
};
const card = elements.create('card', {
style
});
card.mount("#card-element");
card.addEventListener('change', ({
error
}) => {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = "";
}
});
const form = document.getElementById('payment-form');
form.addEventListener('submit', async(event) => {
event.preventDefault();
const {
token,
error
} = await stripe.createToken(card);
if (error) {
const errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
stripeTokenHandler(token);
}
});
const stripeTokenHandler = (token) => {
const form = document.getElementById('payment-form');
const hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
["type", "last4", "exp_month", "exp_year"].forEach(function(field) {
addCardField(form, token, field);
});
form.submit();
}
function addCardField(form, token, field) {
let hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', "user[card_" + field + "]");
hiddenInput.setAttribute('value', token.card[field]);
form.appendChild(hiddenInput);
}
});
I have all the plans and stripe Api configured correctly but something is wrong with the code. i am not very good in Js. So most of the code is a copy and paste and modified to fit my needs
i have search all over i can't find a solution. i need help.

Rails upload large file

I ran into some difficulties while uploading larges files using rails 5.
File is uploaded using ajax and simply grab inside a rails controller.
Used server is : puma.
The file transfer is speed (followed by ajax xhr progress, in a local network (Gigabit)).
But the save of the file /tmp/RackMultipart* took a long time.
I suppose that the file is loaded in memory by Rack, that process and save it in /tmp/. After that, the controller is proceed.
The code work perfectly for small files like images.
But for large file > 100 Mo the completed execution take around 1 minute ...
My code :
The upload area :
views/_attachments.html.erb
<div class="card">
<div class="card-header">
Fichiers
</div>
<div class="card-block">
<span id="attachment-area-message"></span>
<div id="attachment-area">
Déposez vos fichiers ici
</div>
<!-- Area for progress bar -->
<div id="progress-wrapper"></div>
<script>
var attachment_token = '<%= form_authenticity_token %>';
var attachment_model_name = '<%= fileable.class.name %>';
var attachment_model_id = '<%= fileable.id %>';
</script>
</div>
<div class="card-block">
<div class="attachfiles-wrapper">
<div id="attachfiles">
<% fileable.attachments.includes('user').order(created_at: :asc).each do |attachment| %>
<%= render partial: 'app/attachments/attachment', locals: { attachment: attachment } %>
<% end %>
</div>
</div>
</div>
</div>
JS file which launch the upload :
$(document).on('turbolinks:load', function() {
new Clipboard('.btn-clipboard');
var upload_mime = [
'application/zip',
// Image
'image/png',
'image/jpeg',
'image/gif',
'image/tiff',
'image/svg+xml',
];
var upload_maxSize = 3000000000;
var server_url = '/app/attachments/upload.js'; // Route for upload file, .js for the js call back
var element = $("#attachment-area");
// EVENTS
// ----------------------------------------------------------------------------
element.on('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
});
element.on('dragenter', function(e) {
element.addClass('active');
e.preventDefault();
e.stopPropagation();
});
element.on('dragleave', function(e) {
element.removeClass('active');
e.preventDefault();
e.stopPropagation();
});
element.on('drop', function(e) {
element.removeClass('active');
e.preventDefault();
e.stopPropagation();
if (e.originalEvent.dataTransfer){
if (e.originalEvent.dataTransfer.files.length > 0) {
console.log(e.originalEvent.dataTransfer.files);
upload(e.originalEvent.dataTransfer.files);
}
}
return false;
});
// UPLOADS
// ----------------------------------------------------------------------------
var upload = function(files) {
// Send each file
$.each(files, function(key, file) {
// TEST THE FILE
// ----------------------
var FileValidate = true;
// Size
if(file.size > upload_maxSize) {
$('#attachment-area-message').append(file.name + " : Fichier trop lourd (3 Go maximum) : " + file.size);
FileValidate = false;
}
// Mime type
if( upload_mime.indexOf(file.type) == -1 ) {
$('#attachment-area-message').append( file.name + " : Type de fichier non authorisé : " + file.type);
$('#attachment-area-message').append( "<br>Essayez de zipper le fichier");
FileValidate = false;
}
if(!FileValidate) return true; // Continue to next iteration
// SEND FILE
// ----------------------
console.log(file);
var formData = new FormData();
formData.append('attachment[file]', file );
formData.append("attachment[model_name]", attachment_model_name);
formData.append("attachment[model_id]", attachment_model_id);
console.log(formData);
// Progress Bar Name
var progress_name = file.name.replace(/[^a-zA-Z]/g,'-').toLowerCase();
// Send the request :)
$.ajax({
url: server_url,
data: formData,
type: 'POST',
beforeSend: function(request) {
request.setRequestHeader('X-CSRF-Token', attachment_token);
console.log('BEFORE SEND');
},
contentType: false, // NEEDED, DON'T OMIT THIS (requires jQuery 1.6+)
processData: false, // NEEDED, DON'T OMIT THIS
xhr: function() {
// create an XMLHttpRequest
var xhr = new XMLHttpRequest();
console.log('xhr');
xhr.upload.onprogress = function (e) {
console.log('xhr progress');
if (e.lengthComputable) {
var percente = Math.round( ( e.loaded * 100 ) / e.total );
$('.' + progress_name + ' .progress-bar').width(percente + "%");
}
};
xhr.onloadstart = function (e) {
console.log('xhr onloadstart');
$('#progress-wrapper').append('<div class="' + progress_name + '" style="margin-top:5px;">'
+ '<span class="description">' + file.name + '</span>'
+ '<div class="progress" id="file-upload-bar">'
+ '<div class="progress-bar bg-info" role="progressbar" style="width:0%; height:10px;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>'
+ '</div></div>');
};
xhr.onload = function (e) {
console.log('xhr onload');
if (xhr.status === 200) eval(xhr.responseText); // Grab the return of rails controller (format.js)
};
xhr.onloadend = function (e) {
console.log('xhr onloadend');
$('.' + progress_name).remove();
};
return xhr;
}
});
});
};
});
And the controller :
(Attachment model is polymorphic base on fileable).
class App::AttachmentsController < AppController
before_action :find_fileable
def upload
# Get the File
uploaded_io = attach_params[:file]
logger.debug '---------'
logger.debug params.inspect
logger.debug '---------'
# Define file destination
dest = Rails.root.join('public', 'uploads', 'attachments', attach_params[:model_name], attach_params[:model_id], uploaded_io.original_filename)
file_name = uploaded_io.original_filename
file_basename = File.basename(uploaded_io.original_filename, '.*')
file_extname = File.extname(uploaded_io.original_filename)
# Make dir
dir = File.dirname( dest )
FileUtils.mkdir_p(dir) unless File.directory?(dir)
# Test if file exist (and update version if needed)
if File.exist?(dest)
version = 0
loop do
version += 1
file_name = file_basename + '-' + version.to_s + file_extname
dest = Rails.root.join('public', 'uploads', 'attachments', attach_params[:model_name], attach_params[:model_id], file_name )
break if !File.exist?(dest)
end
end
# Copy file to dest
#FileUtils.cp uploaded_io.path, dest
File.open( dest, 'wb') do |file|
file.write(uploaded_io.read)
end
# Save in database
#attach = #fileable.attachments.new
#attach.user_id = #current_user.id
#attach.name = file_name
#attach.size = uploaded_io.size
#attach.mime = uploaded_io.content_type
#attach.key = Digest::SHA1.hexdigest([Time.now, rand].join)
respond_to do |format|
if #attach.save
flash[:success] = "Fichier ajouté"
format.js # upload.js callback add new file to the list of files
else
flash[:warning] = "Fichier non enregistré :("
end
end
end
private
def attach_params
params.require( :attachment ).permit( :model_id, :model_name, :file )
end
def find_fileable
#fileable = Task.find_by_id( attach_params[:model_id] ) if attach_params[:model_name] == 'Task'
end
end
I have tested different file management solutions : CarrierWave, Shrine, ...
Unfortunately the problem is still there. Always the rack save in front.
Any help or any idea ? I want to eat this "Rack"
Thanks,
Seb.
The issue is that Rack multipart parser writes to disk (that's the fast part), but that its implementation is slow and expensive (see rack/rack#1075). However, the Rack master has huge performance improvements to the multipart parser, so using master should solve your problem.
gem "rack", github: "rack/rack"
We can verify this by running the following script:
require "rack"
require "rack/test_app" # https://github.com/kwatch/rack-test_app
require "benchmark"
app = -> (env) do
puts Benchmark.realtime { Rack::Request.new(env).params } # trigger multipart parsing
[200, {}, []]
end
File.write("file.txt", "a" * 100*1024*1024)
test_app = Rack::TestApp.wrap(app)
test_app.post("/", multipart: {file: File.open("file.txt")})
Rack 2.0.3:
$ ruby multipart.rb
62.617582999984734
Rack master:
$ ruby multipart.rb
0.3564810000243597
I have test with chunk method. Create part of 1 Mo of my file and send them in binary. It's better but not the perfection.
With this method rails do not create a MultiRack* file in tmp, but it use memory in both sides, server and client.
The javascript file :
$(document).on('turbolinks:load', function() {
new Clipboard('.btn-clipboard');
var upload_url = '/app/attachments/upload'; // Route for upload file, .js for the js call back
var upload_part_url = '/app/attachments/upload/part/';
var upload_mime = [
'application/zip',
// Vidéo
'video/mp4',
'video/mpeg',
'video/x-flv',
// Audio
'audio/mpeg',
// Image
'image/png',
'image/jpeg',
'image/gif',
'image/tiff',
'image/svg+xml',
// Text
'text/csv',
'text/html',
// Application
'application/pdf',
'application/msword',
'application/excel',
'application/mspowerpoint',
// Adobe
'application/vnd.adobe.indesign',
'application/x-indesign',
'application/indesign',
'image/vnd.adobe.photoshop',
'application/x-photoshop',
'application/photoshop',
'application/psd',
'image/psd',
'application/illustrator',
'application/postscript'
];
var upload_maxSize = 3000000000;
// EVENTS on DROP AREA
// ----------------------------------------------------------------------------
var element = $("#attachment-area"); // Drop area
element.on('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
});
element.on('dragenter', function(e) {
element.addClass('active');
e.preventDefault();
e.stopPropagation();
});
element.on('dragleave', function(e) {
element.removeClass('active');
e.preventDefault();
e.stopPropagation();
});
element.on('drop', function(e) {
element.removeClass('active');
e.preventDefault();
e.stopPropagation();
if (e.originalEvent.dataTransfer){
if (e.originalEvent.dataTransfer.files.length > 0) {
// We upload the files
$.each(e.originalEvent.dataTransfer.files, function(key, file) {
// Test the file
var FileValidate = true;
// Size
if(file.size > upload_maxSize) {
$('#attachment-area-message').append(file.name + " : Fichier trop lourd (3 Go maximum) : " + file.size);
FileValidate = false;
}
// Mime type
if( upload_mime.indexOf(file.type) == -1 ) {
$('#attachment-area-message').append( file.name + " : Type de fichier non authorisé : " + file.type);
$('#attachment-area-message').append( "<br>Essayez de zipper le fichier");
FileValidate = false;
}
// Begin the upload
if(FileValidate) upload(file);
});
}
}
return false;
});
// UPLOAD
// ----------------------------------------------------------------------------
var upload = function(file) {
console.log(file);
var formData = new FormData();
formData.append("attachment[model_name]", attachment_model_name);
formData.append("attachment[model_id]", attachment_model_id);
formData.append('attachment[file_name]', file.name );
formData.append('attachment[file_size]', file.size );
formData.append('attachment[file_mime]', file.type );
// Progress Bar Name
// var progress_name = file.name.replace(/[^a-zA-Z]/g,'-').toLowerCase();
// Send the file infos
var req = new XMLHttpRequest();
// Request events
req.upload.onprogress = function (e) {
console.log('xhr progress');
};
req.onloadstart = function (e) {
console.log('xhr onloadstart');
};
// Error
req.onerror = function (e) {
}
// Success
req.onload = function (e) {
console.log('xhr onload');
if (req.status === 200) {
attach = JSON.parse(req.responseText);
if(typeof attach.id !== 'undefined') uploadFileData(file, attach.id ); // Send the data
}
};
// Complete
req.onloadend = function (e) {
console.log('xhr onloadend');
};
// Send the file infos Request
req.open("POST", upload_url);
req.setRequestHeader('X-CSRF-Token', attachment_token);
req.send(formData);
};
// UPLOAD FILE CHUNKS
// ----------------------------------------------------------------------------
var uploadFileData = function(file, id) {
var reader = new FileReader();
// Process after the file is read
reader.onload = function (e) {
var chunkSize = 1*1024*1024;
var buffer = this.result;
var fileSize = buffer.byteLength;
var segments = Math.ceil(fileSize / chunkSize);
var count = 0;
var fileId = id;
// Send part
(function sendPart() {
var segSize = Math.min(chunkSize, fileSize - count * chunkSize);
var returnFormat = segSize < chunkSize ? '.js' : '.json' ;
if (segSize > 0) {
var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize); // get a chunk
// update progress bar
var req = new XMLHttpRequest();
// Request events
req.upload.onprogress = function (e) {
console.log('part progress : ' + count );
};
req.onloadstart = function (e) {
console.log('part onloadstart : ' + count );
};
// Error
req.onerror = function (e) {
}
// Success
req.onload = function (e) {
console.log('part next : ' + count );
sendPart(); // Success -> Next part
};
// Send the file part data
req.open("POST", upload_part_url + fileId + returnFormat);
req.setRequestHeader('X-CSRF-Token', attachment_token);
req.setRequestHeader('Content-Type', 'application/octet-stream');
req.send(chunk);
}
else {
// hide progress bar
console.log("part Done : " + count);
}
})()
};
// Read the file
reader.readAsArrayBuffer(file);
reader.onprogress = function(e) {
// loaded += e.loaded;
// progress.value = (loaded/total) * 100;
};
}
});
The controller :
class App::AttachmentsController < AppController
before_action :find_fileable, only: [:upload]
def upload
# Define file destination
model_name = attach_params[:model_name]
model_id = attach_params[:model_id]
file_name = attach_params[:file_name]
file_basename = File.basename(file_name, '.*')
file_extname = File.extname(file_name)
dest = Rails.root.join('public', 'uploads', 'attachments', model_name, model_id, file_name)
# Make dir
dir = File.dirname( dest )
FileUtils.mkdir_p(dir) unless File.directory?(dir)
# Test if file exist (and update version in name if needed)
if File.exist?(dest)
version = 0
loop do
version += 1
file_name = file_basename + '-' + version.to_s + file_extname
dest = Rails.root.join('public', 'uploads', 'attachments', model_name, model_id, file_name )
break if !File.exist?(dest)
end
end
# Save in database
#a = #fileable.attachments.new
#a.user_id = #current_user.id
#a.name = file_name
#a.size = attach_params[:file_size]
#a.mime = attach_params[:file_mime]
#a.key = Digest::SHA1.hexdigest( [Time.now, rand].join )
#a.completed = false
#a.save
logger.debug '----'
logger.debug #a.to_json
logger.debug '----'
render status: 200, json: #a.to_json
end
def upload_part
#attach = Attachment.find(params[:id])
logger.debug '----'
logger.debug #attach.inspect
logger.debug '----'
dest = #attach.path
# Copy file to dest
File.open( dest, 'ab') do |file|
file.write(request.raw_post)
end
logger.debug '----'
logger.debug File.size(dest)
logger.debug #attach.size
logger.debug '----'
respond_to do |format|
format.js
format.json { render status: 200, json: { "status": "yop"} }
end
end
private
def attach_params
params.require( :attachment ).permit( :model_name, :model_id, :file_name, :file_size, :file_mime )
end
def find_fileable
#fileable = Task.find_by_id( attach_params[:model_id] ) if attach_params[:model_name] == 'Task'
end
end
See you in next episode. I keep searching ...

Sending an image via a post request from an Ionic app to a rails endpoint

I am currently working on an Ionic mobile application which will eventually take photos, attach a location and send them inside a post request to a rails endpoint. After looking at this link and this link and countless others, I have been unable to find any solid information on implementing this particular feature.
I can upload photos through the browser using a html input form, which is then added to the database and is displayed on the app via a get request.
However at the moment when taking a photo on the phone and attempting to send it via a post request directly from the app, only the location information is being received, the image is not being correctly encoded.
Here is the jSON data that has been received, its returning "image_url":"/images/main/missing.png".
{ "id":6,"city":"Greater London",
"country":"United Kingdom","created_at":"2015-05-14T21:22:22.825Z",
"updated_at":"2015-05-14T21:22:22.825Z","image_file_name":null,
"image_content_type":null,"image_file_size":null,
"image_updated_at":null,"image_url":"/images/main/missing.png" }
Here is the code:
Angular factory making post request:
.factory('Posts', function($http) {
var o = { posts: [] };
o.getAll = function() {
return $http.get('http://localhost:8100/posts').success(function(data) {
angular.copy(data, o.posts);
});
};
o.addPost = function(post) {
return $http.post('https://shielded-hamlet-4665.herokuapp.com/posts', post);
};
return o;
})
Angular Controller taking photo:
.controller("CameraCtrl", function($scope, $cordovaCamera, $http, Posts) {
var id = 0;
var options = {
quality : 75,
destinationType : Camera.DestinationType.FILE_URI,
sourceType : 1,
allowEdit : true,
encodingType: 0,
targetWidth: 380,
targetHeight: 450,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
function getLocCoords(position) {
$scope.lat = position.coords.latitude;
$scope.lon = position.coords.longitude;
$http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + $scope.lat +',' + $scope.lon + '&sensor=true')
.success(function(data) {
var home = data.results[0].address_components;
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf("administrative_area_level_2") > -1) {
$scope.city = home[i].long_name;
break;
};
};
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf('country') > -1) {
$scope.country = home[i].long_name;
break;
};
};
})
};
$scope.takePicture = function() {
navigator.geolocation.getCurrentPosition(getLocCoords);
$cordovaCamera.getPicture(options).then(function(imageData) {
$scope.imgURI = imageData;
id ++;
var post = { id: id, country: $scope.country, city: $scope.city, image: $scope.imgURI, likes: 0, comments: [] }
Posts.addPost(post);
}, function(err) {
});
}
Post Controller from the Rails Database:
class PostsController < ApplicationController
skip_before_filter :verify_authenticity_token
def index
#posts = Post.all
render json: #posts, :callback => params['callback'], :content_type => 'application/javascript', :methods => [:image_url]
end
def new
#post = Post.new
end
def create
Post.create(post_params)
redirect_to '/posts'
end
def post_params
params.require(:post).permit(:city, :country, :image)
end
end
I have not done a great deal of work with the ionic framework so please forgive my ignorance. Any help would be greatly appreciated.
Managed to solve this using the cordovaFileTransfer.upload method.
The rails end point was also filtering params and looking for a post object, with a image string, and only an image string was being provided.
The following code is now working
Angular factory making post request:
.factory('Posts', function($http, $cordovaFileTransfer) {
var o = { posts: [] };
o.getAll = function() {
return $http.get('https://shielded-hamlet-4665.herokuapp.com/posts').success(function(data) {
angular.copy(data, o.posts);
});
};
o.addPost = function(post) {
var options = {
fileKey: "image",
fileName: "image.jpeg",
chunkedMode: false,
mimeType: "image/jpeg",
params: { city: post.city, country: post.country, lat: post.lat, lon: post.lon }
};
$cordovaFileTransfer.upload('http://shielded-hamlet-4665.herokuapp.com/posts', post.image, options)
.then(function(result){
console.log("Code = ok");
}, function(error){
console.log("Code = " + error);
}, function(progress){});
};
return o;
})
Angular Controller taking photo:
.controller("CameraCtrl", function($scope, $cordovaCamera, $http, Posts) {
post = {};
var options = {
quality : 75,
destinationType : Camera.DestinationType.FILE_URI,
sourceType : 1,
allowEdit : true,
encodingType: 0,
targetWidth: 380,
targetHeight: 450,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
function getLocCoords(position) {
post.lat = position.coords.latitude;
post.lon = position.coords.longitude;
$http.get('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + post.lat +',' + post.lon + '&sensor=true')
.success(function(data) {
var home = data.results[0].address_components;
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf("administrative_area_level_2") > -1) {
post.city = home[i].long_name;
break;
};
};
for (var i = 0; i < home.length; i++) {
if(home[i].types.indexOf('country') > -1) {
post.country = home[i].long_name;
break;
};
};
})
};
$scope.takePicture = function() {
navigator.geolocation.getCurrentPosition(getLocCoords);
$cordovaCamera.getPicture(options).then(function(imageData) {
post.image = imageData;
Posts.addPost(post);
}, function(err) {});
};
});
Post controller from rails database:
class PostsController < ApplicationController
skip_before_filter :verify_authenticity_token
def index
#posts = Post.all
render json: #posts, :callback => params['callback'], :content_type => 'application/javascript', :methods => [:image_url]
end
def new
#post = Post.new
end
def create
Post.create(post_params)
redirect_to '/posts'
end
def post_params
params.permit(:city, :country, :image, :lat, :lon)
end
end

image upload through json in rails3

I'm using carrier wave for image upload and trying to do upload
image through json,but its not going to save value into database
.While image name is coming in controller correct here
#Employee_events.external_doc=params[:external_doc] .But its not
going to set in this #Employee_events.external_doc variable , as i
thing may be issue will be multipart. But d'not know how to use in
json.
.js file
$(document).ready(function() {
var evnt = {};
$("#Event_dialog-form").dialog({
autoOpen : false,
height : 400,
width : 600,
modal : true,
buttons : {
"Enter" : function() {
evnt.external_doc = $('#imgid').val();
$.post("/employee_management/add_event", evnt, function(data) {
if (data) {
alert("Data entered successfully");
} else {
alert("Oops! some error occured");
}
});
return false;
},
Cancel : function() {
$(this).dialog("close");
}
},
close : function() {
//allFields.val( "" ).removeClass( "ui-state-error" );
}
});
$(".EventEntryDialog").click(function() {
var Ed = $(this).attr("id");
$('#user_id').val(Ed);
$("#Event_dialog-form").dialog("open");
});
});
Controller
def add_event
result=TRUE
#Employee_events=EmployeeEvent.new
#Employee_events.external_doc= params[:external_doc]
puts #Employee_events.external_doc.to_s
respond_to do |format|
if #Employee_events.save
format.json { render :json => result }
else
return FALSE
end
end
end

Backbone.js router instance is somehow invalid

I have the following chunk of code. It works perfectly.
<div id="restaurant_locations"></div>
<script type="text/javascript">
$(function() {
window.router = new Lunchhub.Routers.RestaurantLocationsRouter({restaurantLocations: <%= #restaurant_locations.to_json.html_safe -%>});
var Foo = Backbone.Router.extend({routes: {"foo":"bar"}});
r = new Foo();
Backbone.history.start();
});
</script>
However, THIS does NOT work:
<div id="restaurant_locations"></div>
<script type="text/javascript">
$(function() {
window.router = new Lunchhub.Routers.RestaurantLocationsRouter({restaurantLocations: <%= #restaurant_locations.to_json.html_safe -%>});
// All I did was delete the two lines that used to be here
Backbone.history.start();
});
</script>
The latter snippet gives me this error:
Uncaught TypeError: Cannot call method 'start' of undefined
So my Foo router instance triggers a proper initialization of Backbone.history, just like you would expect a router instance to do, but my Lunchhub.Routers.RestaurantLocationsRouter instance does not.
Here's my router definition in CoffeeScript (generated automatically by the backbone-rails gem):
class Lunchhub.Routers.RestaurantLocationsRouter extends Backbone.Router
initialize: (options) ->
#restaurantLocations = new Lunchhub.Collections.RestaurantLocationsCollection()
#restaurantLocations.reset options.restaurantLocations
routes:
"new" : "newRestaurantLocation"
"index" : "index"
":id/edit" : "edit"
":id" : "show"
".*" : "index"
newRestaurantLocation: ->
#view = new Lunchhub.Views.RestaurantLocations.NewView(collection: #restaurant_locations)
$("#restaurant_locations").html(#view.render().el)
index: ->
#view = new Lunchhub.Views.RestaurantLocations.IndexView(restaurant_locations: #restaurant_locations)
$("#restaurant_locations").html(#view.render().el)
show: (id) ->
restaurant_location = #restaurant_locations.get(id)
#view = new Lunchhub.Views.RestaurantLocations.ShowView(model: restaurant_location)
$("#restaurant_locations").html(#view.render().el)
edit: (id) ->
restaurant_location = #restaurant_locations.get(id)
#view = new Lunchhub.Views.RestaurantLocations.EditView(model: restaurant_location)
$("#restaurant_locations").html(#view.render().el)
And here's the compiled JavaScript:
(function() {
var __hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
Lunchhub.Routers.RestaurantLocationsRouter = (function(_super) {
__extends(RestaurantLocationsRouter, _super);
function RestaurantLocationsRouter() {
RestaurantLocationsRouter.__super__.constructor.apply(this, arguments);
}
RestaurantLocationsRouter.prototype.initialize = function(options) {
this.restaurantLocations = new Lunchhub.Collections.RestaurantLocationsCollection();
return this.restaurantLocations.reset(options.restaurantLocations);
};
RestaurantLocationsRouter.prototype.routes = {
"new": "newRestaurantLocation",
"index": "index",
":id/edit": "edit",
":id": "show",
".*": "index"
};
RestaurantLocationsRouter.prototype.newRestaurantLocation = function() {
this.view = new Lunchhub.Views.RestaurantLocations.NewView({
collection: this.restaurant_locations
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.index = function() {
this.view = new Lunchhub.Views.RestaurantLocations.IndexView({
restaurant_locations: this.restaurant_locations
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.show = function(id) {
var restaurant_location;
restaurant_location = this.restaurant_locations.get(id);
this.view = new Lunchhub.Views.RestaurantLocations.ShowView({
model: restaurant_location
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.edit = function(id) {
var restaurant_location;
restaurant_location = this.restaurant_locations.get(id);
this.view = new Lunchhub.Views.RestaurantLocations.EditView({
model: restaurant_location
});
return $("#restaurant_locations").html(this.view.render().el);
};
return RestaurantLocationsRouter;
})(Backbone.Router);
}).call(this);
What could be going wrong here?
EDIT: I've figured out part of the problem. In the CoffeeScript, it was using restaurant_locations in some places where it should have been using restaurantLocations. I'm having a strange problem now, but potentially an easier one: when I copy and paste the compiled JavaScript directly into <script> area, right before the window.router = assignment, everything works perfectly. However, when I try to use the compiled JS as an external file (and I know it's being included - I checked), I get that same Cannot call method 'start' of undefined error.
Here's my updated CoffeeScript:
class Lunchhub.Routers.RestaurantLocationsRouter extends Backbone.Router
initialize: (options) ->
#restaurantLocations = new Lunchhub.Collections.RestaurantLocationsCollection()
#restaurantLocations.reset options.restaurantLocations
routes:
"new" : "newRestaurantLocation"
"index" : "index"
":id/edit" : "edit"
":id" : "show"
".*" : "index"
newRestaurantLocation: ->
#view = new Lunchhub.Views.RestaurantLocations.NewView(collection: #restaurantLocations)
$("#restaurant_locations").html(#view.render().el)
index: ->
#view = new Lunchhub.Views.RestaurantLocations.IndexView(restaurantLocations: #restaurantLocations)
$("#restaurant_locations").html(#view.render().el)
show: (id) ->
restaurant_location = #restaurantLocations.get(id)
#view = new Lunchhub.Views.RestaurantLocations.ShowView(model: restaurant_location)
$("#restaurant_locations").html(#view.render().el)
edit: (id) ->
restaurant_location = #restaurantLocations.get(id)
#view = new Lunchhub.Views.RestaurantLocations.EditView(model: restaurant_location)
$("#restaurant_locations").html(#view.render().el)
And here's my updated compiled JavaScript:
(function() {
var __hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
Lunchhub.Routers.RestaurantLocationsRouter = (function(_super) {
__extends(RestaurantLocationsRouter, _super);
function RestaurantLocationsRouter() {
RestaurantLocationsRouter.__super__.constructor.apply(this, arguments);
}
RestaurantLocationsRouter.prototype.initialize = function(options) {
this.restaurantLocations = new Lunchhub.Collections.RestaurantLocationsCollection();
return this.restaurantLocations.reset(options.restaurantLocations);
};
RestaurantLocationsRouter.prototype.routes = {
"new": "newRestaurantLocation",
"index": "index",
":id/edit": "edit",
":id": "show",
".*": "index"
};
RestaurantLocationsRouter.prototype.newRestaurantLocation = function() {
this.view = new Lunchhub.Views.RestaurantLocations.NewView({
collection: this.restaurantLocations
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.index = function() {
this.view = new Lunchhub.Views.RestaurantLocations.IndexView({
restaurantLocations: this.restaurantLocations
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.show = function(id) {
var restaurant_location;
restaurant_location = this.restaurantLocations.get(id);
this.view = new Lunchhub.Views.RestaurantLocations.ShowView({
model: restaurant_location
});
return $("#restaurant_locations").html(this.view.render().el);
};
RestaurantLocationsRouter.prototype.edit = function(id) {
var restaurant_location;
restaurant_location = this.restaurantLocations.get(id);
this.view = new Lunchhub.Views.RestaurantLocations.EditView({
model: restaurant_location
});
return $("#restaurant_locations").html(this.view.render().el);
};
return RestaurantLocationsRouter;
})(Backbone.Router);
}).call(this);
Okay, this turned out to be a pretty esoteric problem. I had had a leftover backbone-min.js sitting in my app/assets/javascripts directory, even though I had switched to using a different Backbone file. This "old" backbone-min.js was getting loaded after my route definition and that's what was messing things up. After I deleted backbone-min.js, things started working.

Resources