Strange Routing Behavior in Rails on Image Upload Using Carrierwave - ruby-on-rails

I'm working on creating a new edit/update view and routes for a fairly large Rails (v4.0.3) app. I'm using remote: true to submit sections of the form without reloading the whole page, and then simply replacing the form HTML with the response HTML (which is the form partial). The URL (.../edit) should remain the same.
It works fine to update text fields, but when I upload an image (using Carrierwave) I get this strange behavior where the image uploads fine but then I get redirected to the controller action URL (.../update) and the browser displays the raw partial out of context.
Here's the relevant method from my resource controller:
def update
respond_to do |format|
if #provider.update_attributes(admin_provider_params)
if params[:provider][:logo]
#provider.logo = params[:provider][:logo]
#provider.save
elsif params[:provider][:remove_logo] == '1'
#provider.remove_logo!
#provider.save
end
format.html { render partial: "provider_form", locals: { provider: #provider } }
format.json { render json: #provider }
else
format.html { render action: "edit" }
format.json { render json: #provider.errors, status: :unprocessable_entity }
end
end
end
And here's some selected code from my view partial:
= simple_form_for [:admin, #provider],
remote: true,
html: { class: 'form-horizontal', multipart: true, id: 'provider-data-form' },
authenticity_token: true,
wrapper: :horizontal_form do |f|
-# (SOME OTHER FORM FIELDS HERE)
-# LOGO UPLOAD ELEMENTS
.form-group.file.optional.provider_logo
= f.input :logo, input_html: { id: "provider-logo-upload-real", class: "hidden"}, :as => :file, wrapper: false
%button#provider-logo-upload-btn= #provider.logo_url.nil? ? "Upload Logo" : "Replace Image"
%img#logo-upload-img{src: #provider.logo_url}
-# SUBMIT FORM
= f.button :submit, translate_helper(:save), id: "save-provider-form-btn"
:javascript
$(document).ready(function() {
// Handle logo upload via proxy button
$('#provider-logo-upload-btn').on("click", function(e) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
$('#provider-logo-upload-real').click();
});
// Preview logo before upload
$('#provider-logo-upload-real').change(function(e) {
$('#logo-upload-img').attr("src", URL.createObjectURL(event.target.files[0]));
});
// Handle Form submit
$('#provider-data-form')
.on("ajax:success", function(evt, data, status, xhr) {
console.log("AJAX Success!", arguments);
// On success, refresh just the provider form partial
$('#provider-data-form').replaceWith(xhr.responseText);
});
});
I think this is all the relevant code, but I'm happy to provide more on request. There's not much additional logic tacked on the CarrierWave Uploader classes.
Finally, here are my server logs (edited slightly for brevity) when I submit the form with a new image for upload:
I, [2016-10-17T10:09:24.745387 #1219] INFO -- : Started PATCH "/en/admin/providers/8" for 127.0.0.1 at 2016-10-17 10:09:24 -0400
I, [2016-10-17T10:09:24.754005 #1219] INFO -- : Processing by Admin::ProvidersController#update as JS
I, [2016-10-17T10:09:24.754133 #1219] INFO -- : Parameters: application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01", "locale"=>"en", "id"=>"8"}
I, [2016-10-17T10:09:24.754187 #1219] INFO -- : Parameters: {
// LISTS PARAMETERS, INCLUDING IMAGE UPLOAD DATA
}
I, [2016-10-17T10:09:25.105809 #1219] INFO -- : Rendered admin/providers/_provider_form.html.haml (52.9ms)
I, [2016-10-17T10:09:25.106381 #1219] INFO -- : Completed 200 OK in 352ms (Views: 43.8ms | ActiveRecord: 51.8ms)
// IF NO IMAGE WAS INCLUDED, NORMALLY IT STOPS AT THIS POINT, BUT...
I, [2016-10-17T10:09:25.111315 #1219] INFO -- : Started PATCH "/en/admin/providers/8" for 127.0.0.1 at 2016-10-17 10:09:25 -0400
I, [2016-10-17T10:09:25.122752 #1219] INFO -- : Processing by Admin::ProvidersController#update as HTML
I, [2016-10-17T10:09:25.122925 #1219] INFO -- : Parameters: {
// LISTS PARAMETERS, NO IMAGE UPLOAD DATA
}
I, [2016-10-17T10:09:25.268027 #1219] INFO -- : Rendered admin/providers/_provider_form.html.haml (43.9ms)
I, [2016-10-17T10:09:25.268360 #1219] INFO -- : Completed 200 OK in 145ms (Views: 35.2ms | ActiveRecord: 39.5ms)
// PAGE DISPLAYS PARTIAL ONLY, URL IS FOR UPDATE ROUTE RATHER THAN EDIT
Sorry for such a long question; I've been puzzled by this for over a week. I'd appreciate any help I can get!

I guess that is because Rails remote form with file falls back to HTML submission instead of regular JS submission . Try using remotipart Gem.

Okay, I think I've been able to solve it. Thanks to #sajan, I found this issue on the Remotipart github page: https://github.com/JangoSteve/remotipart/issues/129
By preventing default on my submit button click events and then submitting the form itself, I was able to stop the multiple form submit issue.

Related

Simple_form_for not using AJAX?

I got a weird problem with my form:
= simple_form_for([#item, #item_comment], :remote => true, id: "new_item_comment", :url => item_item_comments_path(#item)) do |f|
= f.input :comment, :label => false
= f.submit "Save", :class => "btn_save left"
Which in my opinion should call:
Started POST "/de-de/items/20150423/item_comments" for 127.0.0.1 at 2015-04-23 12:29:33 +0200
Processing by ItemCommentsController#create as JSON
but instead I get it as HTML:
Started POST "/de-de/items/20150423/item_comments" for 127.0.0.1 at 2015-04-23 12:29:33 +0200
Processing by ItemCommentsController#create as HTML
It used to work but without changing these parts, it only uses HTML.
Does anyone have an idea on how to solve this?
--- Update 1 ---
I added these lines to my coffeescript:
$('form[data-remote]').submit (e)->
e.preventDefault()
$.rails.handleRemote $('form[data-remote]')
And it works but I'm not really satisfied with this solution since I don't know what caused the problem.
Usually it happened to me in 2 cases:
I had a file input on the form (which forces ruby to skip remote: true option)
I had troubles with jquery-ujs javascript file (which actually processes rails html attrs)
So please check your generated html if it has <form .... data-remote='true'..> and check that jquery-ujs (or whatever handler you want to use) is included in a page javascripts.
If you are still having troubles after this, you can put a breakpoint somewhere in jquery-ujs

Ajax request returns (406 Not Acceptable )

In my Rails app javascript template profile.js.erb don't get rendered. I get error code 406 Not Acceptable. This template is supposed to append a partial in a view star#profile. Ajax request is made by jquery infinite scroll plugin. Here is my code
.
action
def profile
#page=params[:page] ||1
#videos=Video.all(:page=>#page)
respond_to do |format|
format.js
format.html
end
end
view Stars#profile
<div id="content">
<div class="post">
<%=render 'videos'%>
</div>
profile.js.erb
$('div.post').append("<%=escape_javascript(render 'videos')%>");
routes
match "stars/profile/:page"=> "stars#profile", :via => :get
log in console
Started GET "/stars/profile?page=2" for 127.0.0.1 at 2013-06-20 00:05:58 -0500
Processing by StarsController#profile as application/JavaScript
Parameters: {"page"=>"2"}
Completed 406 Not Acceptable in 65836ms
Ajax setup
jQuery.ajaxSetup({
'beforeSend': function(xhr, settings) {
xhr.setRequestHeader("Accept", "application/javascript");
var token=$('meta[name="csrf-token"]').attr('content');
xhr.setRequestHeader('X-CSRF-Token',token );
settings['dataType'] = "javascript";
settings['contentType'] = "application/javascript";
}
});
From the log:
GET "/stars/profile?page=2" does not include .js after profile. So rails does not handle it at javascript.
GET "/stars/profile.js?page=2" should be the call..

Rails jQuery adapter rendering thrice

I'm following the Beginning Rails 3, Updated book from Apress 2010. The problem I'm having is with loading a Template dynamically with Ajax using the jQuery adapter. Everything works but it appears to be rendering three times on the page.
Here is how I dynamically load it when the user clicks the "new comment" link.
views/articles/show.html.erb
<%= link_to "new comment",
new_article_comment_path(#article, :format => :js),
:remote => true,
:id => 'new_comment_link' %>
Then I render it as such.
views/comments/new.js.erb
$("<%= escape_javascript render :file => 'comments/new'," +
" :formats => [:html], :handlers => [:erb] %>")
.insertAfter('#comments');
Then I see this in the log.
Started GET "/articles/1/comments/new.js" for 127.0.0.1 at 2013-01-18 14:51:05 -0600
Processing by CommentsController#new as JS
Parameters: {"article_id"=>"1"}
Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT 1 [["id", "1"]]
Rendered comments/new.html.erb (53.8ms)
Rendered comments/new.js.erb (54.6ms)
Completed 200 OK in 57ms (Views: 55.9ms | ActiveRecord: 0.2ms)
Notice it renders my erb and the js file? Somehow that ends up with showing up three times on my page.
Any clues on how to fix this? I'm using Rails 3.2.9, rails.js (latest), and jquery-1.9.0.
Thanks!
Solved it!
Turns out I was adding rails.js and jquery.js TWICE!
Here is the skinny: assets/javascripts/application.js is crucial for including javascript files into your app. Basically whatever gets defined there gets pushed out to the page. You simply need to make sure that it gets defined at least once in the app as such.
views/layouts/application.html.erb
<%= javascript_include_tag "application" %>
And that's it! The Ajax jQuery adapter should just work. Becareful not to add any additional files to the javascript folder as those will get pushed out as well and that's exactly what you don't want. Basically I was defining the adapter both through application.html.erb and manually by downloading both files. Hopefully this will help a lost poor soul somewhere along the way.
Happy Hacking.

Devise warden 401 Unauthorized when wrong credentials

I have a quite standard Devise login procedure with:
View:
resource_name, :url => session_path(resource_name)) do |f| %>
<%= f.input :password, input_html: {class: "span6"} %>
<% if devise_mapping.rememberable? -%>
<p><%= f.check_box :remember_me %> Remember me</p>
<% end -%>
<input type="hidden" name="after_sign_in_page" value="<%=#after_sign_in_page%>">
<p><%= f.submit "Sign in", class: "btn btn-success" %></p>
And I just created a sessioncontroller to downcase the email:
class SessionsController < Devise::SessionsController
def create
params[:user][:email].downcase!
super
logger.debug "Errors: #{resource.errors}"
end
A login with good credentials happens fine.
With wrong credentials, It redirects to the sign-in page with this log:
Started POST "/users/sign_in" for 127.0.0.1 at 2013-01-10 09:59:44 +0100
Processing by SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8eytQkr20JOOOdDvpCWakbmUzNoaHMxK9/BSEVxETik=", "user"=>{"email"=>"nicolas#demoreau.be", "password"=>"[FILTERED]", "remember_me"=>"0"}, "after_sign_in_page"=>"", "commit"=>"Sign in"}
Time zone: (GMT+00:00) UTC, current area: , user to register: , current controller: sessions
Completed 401 Unauthorized in 69ms
Processing by SessionsController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8eytQkr20JOOOdDvpCWakbmUzNoaHMxK9/BSEVxETik=", "user"=>{"email"=>"nicolas#demoreau.be", "password"=>"[FILTERED]", "remember_me"=>"0"}, "after_sign_in_page"=>"", "commit"=>"Sign in"}
Rendered devise/sessions/_new.html.erb (17.8ms)
Rendered devise/sessions/new.html.erb within layouts/application (19.7ms)
Rendered layouts/_header.html.erb (66.1ms)
Completed 200 OK in 173ms (Views: 98.3ms | ActiveRecord: 0.9ms)
Apparently the 401 is dropped by Warden but I couldn't figure out why.
The user is correctly redirected back to the login page but there is no error message displayed (which is normal as they are wiped out by the redirect)
What am I doing wrong?
thanks!
EDIT 1:
For now, I found a quick hack. I added this in SessionsController#new
if params[:user]
flash[:alert] = "Incorrect login or password"
end
Not very elegant but at least, I have something.
First of all, let me advice you against overriding Devise controllers:
In this case, Devise takes care of transforming the email to lower
case for you, so there's really no need to overwrite the create method.
Your app will support Devise updates seamlessly if you stick to the
standard.
Also, Devise should set a flash error automatically, make sure you're displaying it in your view.
The status code 401 is just a standard response for unauthorized requests.
401 Unauthorized is similar to 403 Forbidden, but specifically for use
when authentication is required and has failed or has not yet been
provided
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
You should definitely consider dropping your custom controller,
Cheers
Your flash message is not going to be set because Devise::SessionsController#create calls warden for the authentication which, in case of failures, will call Devise::FailureApp. Your controller never handles the failure scenarios.
If you want a custom message, you can customize the failure app for that, there are some articles in the Devise wiki explaining how to do so.
But in general, you can customize your failure messages via I18n and there is probably a better way to achieve what you want without a need to override Devise controllers.
I agree with jassa, just update your Devise version (with bundle update devise).
Case insensitive emails are already present in Devise, just make sure you have this config:
# devise.rb
Devise.setup do |config|
config.case_insensitive_keys = [:email ]
end
In any case, since you seem to be missing some flash messages and this config, perhaps it would better if you just re-ran the generator:
rails generate devise:install
You should then let Devise overwrite some files, just make sure you backup yours first.

Error during failsafe response: Ruby on Rails 3

I have a form_tag that works fine using html, but when I use ajax with the remote => true I am getting this error:-
My terminal log shows:-
Started GET "/" for 127.0.0.1 at 2010-11-01 01:19:49 +0000
Processing by HomepagesController#index as HTML
Homepage Load (0.6ms) SELECT "homepages".* FROM "homepages"
Rendered homepages/index.html.erb within layouts/application (23.0ms)
Completed 200 OK in 40ms (Views: 27.3ms | ActiveRecord: 0.6ms)
Error during failsafe response: incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string)
* then a load of cleaner.rb stuff
then:-
Started GET "/homepages?utf8=%E2%9C%93&search=hom" for 127.0.0.1 at 2010-11-01 01:19:56 +0000
Processing by HomepagesController#index as JS
Parameters: {"utf8"=>"✓", "search"=>"hom"}
Homepage Load (0.5ms) SELECT "homepages".* FROM "homepages" WHERE (section LIKE '%hom%')
Rendered homepages/index.js.erb (2.9ms)
Completed in 19ms
In my index.js.erb I have:-
$("testsearch").update("<%= escape_javascript(render(#homepages))%>");
and in my Controller I have:-
def index
#homepages = Homepage.search(params[:search])
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #homepages }
format.js { render :layout => false }
end
in my view I have:-
which prints #homepages using a table using <% #homepages.each do |homepage| %> which is not being updated.
Anyone have any ideas as to why I get this error.
I have cracked it by going onto an IRC chat room (irc.freenode.net RubyonRails) and a ProjectZen (human being somewhere out there in the ether) helped me to get it working.
Apparently what was happening was that I was following Ryan Bates who does many extremely good Railcast videos, but he builds on previous Railcast. Therefore in his 205 Railscast, which deals with Ajax calls, he did not mention that you must have:-
format.js in the action in the controller.
His xxxx.searchxxxxx needs to be created in the controller or model.
And that when I did :-
<%= render(#homepages)%> <!-- (in his case <%= render(#products)%>) -->
The render was looking for a partial called "_homepage" (not "homepages") (I did not even have a partial therefore I got the UTF8 to ASCII error).
And then in "_homepage" I would add my code to render the results.
What I have now done in my index.html.erb is to put <%= render(#homepages)%>, in the (div id = testsearch) in place of the code I use to render #homepages and then place that code in a partial "_homepage". Now I can use "_homepage" for the html and the Ajax call.
At the moment I have a slight problem in that it is rendering all the data in the"#homepages" as many times as the number of records. At the moment I do not know why, but at least the Ajax call is working.

Resources