Ruby on Rails: Action without View - ruby-on-rails

I have what I think is a very simple problem. I'm coming from a PhP background, and used to do this all the time, so I may be looking at this the wrong way.
I am trying to create an ajax handler in RoR. When the user clicks a button, javascript fires off a POST, and gives the user feedback using the "success:" parameter of jQuery's ajax function.
The problem is, RoR is trying to load a view for the ajax handler, when I really just need a few lines in the controller to do the database work, and echo out a status code that will be interpreted by the user's javascript.
This is all just a mailchimp subscribe holding page, so I am only using the 'home' controller.
My Routes;
map.root :controller => 'home'
map.connect '/mcsubscribe', :controller => 'home', :action => 'mcsubscribe'
My Home Controller;
class HomeController < ApplicationController
def index
# no content
end
def mcsubscribe
print params[:email]
end
end
And my testing javascript, just so you understand what's going on;
function mcSubscribe() {
var email = jQuery("#signup_input_email").val();
jQuery.ajax({
type: "POST",
url: "http://domain.com/mcsubscribe",
data: "email=" + email,
cache: false,
success: function(result) {
alert(result);
}
});
}
I thought this would be a common problem, but I've googled around and only managed to find suggestions to redirect, as the user will never visit the /mcsubscribe page, that doesn't seem appropriate.

Have a look at http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
You might need render :nothing => true
EDIT: misread your question, render :text => "yourtext" should fit your needs

Using
print params[:email]
will just print that value to the application logs, not into the response.
You want this:
render :text => params[:email]

In Rails 5 you need to use 'plain':
render plain: params[:email]

You'd need something like this:
def mcsubscribe
# Do something to unsubscribe
respond_to do |format|
format.html { redirect_to(success_page) }
format.js { render :text => params[:email] }
end
end
If ajax is used, the params[:email] is send as text. If a HTML format is required (ie user clicked a link or filled in a regular form) a redirect is issued to tell the user the subscription has been successful.

Related

Render to named path

I have a rails project with the following route:
get 'login', to: 'user_sessions#new', as: :login
In my UserSessionsController I have
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
# Do all the happy stuff
else
format.html { render :action => 'new' }
format.xml { render xml: #user_session.errors, status: :unprocessable_entity }
end
end
end
That's working ok, except that when the user enters incorrect parameters the route is via /user_sessions instead of /login, which is untidy (and means my test assertions are confusing).
Obviously I could just redirect_to login_path, but then my #user_session.errors don't seem to be available so by page doesn't show what was wrong.
How do I redirect back to /login and still have the errors show?
Edit:
It looks as if Rails makes this difficult because it's something I shouldn't try to do. The RESTful path isn't really something the user cares about so I shouldn't be using it as part of my UI testing. Instead, I am looking at the actual content of the rendered page, which the user does care about. Thanks all.
You can add
post 'login', to: 'user_sessions#create', as: :post_login
and change the form action accordingly.
This is happening because when you get validation errors in your form then you are on create action and not new action. Your create action simply render your new actions template with errors, it doesn't send a request to server and hence your url remains same so to fix it you can simply change the route for your create action to this:
post 'login', to: 'user_sessions#create', as: :login
Update:
You'll just have to change your route for create action and then make changes in your form, something like this:
<%= from_for #resource, url: login_path do |f| %>
// form fields
<% end %>
If you'll inspect your form you'll see that its method is POST so when you'll submit it, your form will send a POST request and when you hit /login in your browsers address bar it'll send a GET request so in first case you'll go to create action and in second one you'll go to new action

RSpec controller testing: missing template on create

I have an interesting situation. I am testing the following simple create action:
# will only be accessed via Ajax
def create
click = Click.new(params[:click])
click.save # don't really care whether its success or failure
end
Then I have the following very simple controller spec:
require 'spec_helper'
describe ClicksController, "creating a click" do
it "should create a click for event" do
xhr :post, :create, :click => {:event_id => 1}
# more test to come...
end
end
Seems trivial, yet I get the following:
Missing template clicks/create
Any tips would be appreciated.
Add to the controller action:
render :nothing => true
This one will automatically create the appropriate server's respone. More here
You will get this error if your controller renders only JSON or XML, yet you don't specify a format in the spec; your request then defaults to unsupported HTML. In that case, simply specify the supported format when you invoke the controller method from your spec. For example, change this:
post :create, registration: #user_hash
to this:
post :create, registration: #user_hash, format: :json
If you do not render anything in a controller action, rails will attempt to default to rendering a template (in this case clicks/create). I'd suggest rendering back at least a success message like so:
render :json => {:success => true}
Building on megas's answer, if you're looking to test a controller action that's only accessed via a UJS link and only has a .js.erb template, I'd put this in the controller to avoid breaking your UJS functionality:
respond_to do |f|
f.html { render nothing: true } # prevents rendering a nonexistent template file
f.js # still renders the JavaScript template
end
This will enable you to call the controller action by simply calling ActionController::TestCase::Behavior's get/post/put/delete methods instead of needing to call xhr, because it will successfully call the method, render nothing, and continue on, while leaving your UJS behavior intact.

render :new not going to the right place after a validation

I have a 'new' form that gets validated in a post model. When the validator kicks in, it renders incorrectly.
The new post page path is at '/posts/new'
On validation, the new post page path is at '/posts' .. I need it to go back to '/posts/new'.
This is my controller:
def create
#post = current_user.posts.build(params[:post])
if #post.save
redirect_to public_post_page_path(#post.public_url)
else
render :action => :new
end
end
I have a feeling it might have to do with my form. So here is the formtastic first line:
<%= semantic_form_for [:student, post], :html => {:id => "post_form"} do |form| %>
This is the correct behavior from rails.
In the create action it simply renders the "new" view file. As such the url will be /posts but the view will correctly display the form. There is nothing wrong with this behavior; and in general rails convention is good form. Also the built in rails errors work if you just render new; however if you redirect they won't display.
If you really feel like you want to go back to that url you need to use:
redirect_to
instead of render.
If validation fails, user should see the form with the errors and stay at /posts/new. That's what you want, right?
There's a simple way to achieve this.
Set remote: true on the form to prevent the url from advancing. Handle ajax:success to replace the form on the page with the newly rendered one.
$('form[data-remote=true]').on 'ajax:success', (e, data, status, xhr) ->
if isHTML(data)
thisForm = "form[id=#{#getAttribute('id')}]"
$(thisForm).replaceWith $(data).find(thisForm)
isHtml() function is from this question.

How to use Rails respond_with with a create action and javascript?

I've been reading about respond_with. and used it before in a couple of tutorials but dont really think I understand it fully.
The functionality I am trying to implement right now is this: I have a list of items of class Article, and each of them has a link to the create Favorite action. When the user clicks on it it the favorite instance is created and the user is redirected to the home page. I want this to work with AJAX without a page refresh, and execute some JavaScript on the article item to let the user know it's been favourited successfully. I've used the :remote => true attribute in the relevant link, so the action is executing remotely without problem, but I am a bit stuck on how to execute the action's .js.erb file.
I've done the following in the controller:
class FavouritesController < ApplicationController
before_filter :authenticate
respond_to :html, :js
def create
#article = Article.find_by_id(params[:article_id])
current_user.Favourites.create(:article => #article)
redirect_to root_path
end
The problem is, I dont know what parameter should i pass to respond_with. I've tried replacing the redirect with both respond_with #article, and respond_with without parameters, and while both of them work (the create.js.erb is called), I dont understand why...
I dont want to 'respond_with' anything, but only that when the action is executed via javascript, the create.js.erb gets called. Can anyone explain a) why does respond_with work anyway whatever I pass to it, and b) what is the right way to do this?
BTW, I am using Rails 3.0
EDIT: I understand it better if I use respond_to like in the following code, but I would like to understand respond_with better.
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
I ran across this question several times trying to solve a similar problem. It turns out my controller wasn't inferring the format as I assumed it would from the content type or the fact that it's an xhr request.
In any case, it worked when I switched from this:
contacts_path
To this:
contacts_path format: :json
I didn't care for that per se, so I changed my ajax call to include the dataType option like this (per jQuery ajax documentation):
$.ajax({url: contactUrl, type: 'POST', data: data, dataType: 'json' });
Hi Oalo go through the link below, it concisely explains the respond_with and respond_to. It lists all the options that you can pass to respond_with
http://ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with
Hope this answers your query

Redirecting to a 500 page when an AJAX call fails in Ruby on Rails

I'm working with an application built in Ruby on Rails with very poor error handling right now. If a controller method is executed via ajax, and that method results in a 500 (or 404 or any other response) the 500.html page is rendered and returned as the result to the AJAX request. Obviously the javascript doesn't know what to do with that HTML and the web page looks like it's just waiting for a response.
Is there an easy way in rails to render an error.rjs template anytime an error occurs during an AJAX call?
You can use respond_to inside a controller's rescue_action or rescue_action_in_public method. Consider the following controller:
class DefaultController < ApplicationController
def index
raise "FAIL"
end
def rescue_action(exception)
respond_to do |format|
format.html { render :text => "Rescued HTML" }
format.js { render :action => "errors" }
end
end
end
I solved a similar problem with authorization. I created a simple authorization controller with this action:
def unauthorizedxhr
render :update do |page|
page.replace_html("notice", :partial=>"unauthorizedxhr")
page.show("notice")
end
end
Here's the template:
<% if flash[:notice] -%>
<div id="noticexhr"><%= flash[:notice] %></div>
<% end -%>
When the authorization failed in the controller, I'd redirect to the :controller=>"authorization", :action=>"unauthorizedxhr" after setting the flash[:notice] value. This allowed me to customize the message I sent to the user, and it handled the display of the message through the render :update code above.
You could adapt this to your problem by creating an errors controller, catching any raised errors in your other controllers, then simply redirecting to :controller=>"errors", :action=>"displayxhr" when the call fails. That way, you'll have standardized your error communication mechanism but allowed yourself the ability to customize error messages by each action.
You can still use cpm's idea above, but the error's display will be handled by separate and distinct controller logic. that should make it a little easier to maintain.
Hope that helps.
-Chris
This was my final solution:
def rescue_action_in_public(exception)
response_code = response_code_for_rescue(exception)
status = interpret_status(response_code)
respond_to do |format|
format.html { render_optional_error_file response_code}
format.js { render :update, :status => status do |page| page.redirect_to(:url => error_page_url(status)) end}
end
end
This basically forwards to the correct static HTML page no matter if the request was via AJAX or normal GET/POST.
This is NOT used for normal error handling like validation etc. It's only used when something really bad happens - like an unhandled exception.
You can do like below :
in allpication.js
$(document).ready(function(){
$(document).ajaxError( function(e, xhr, options){
if("500" == xhr.status)
{
$(location).attr('href','/users/sign_in');
}
});
})
Its working for me.....

Resources