Rails - Controller Action can only be executed by JS call - ruby-on-rails

In my view file, I use a collection_select with an :onchange => remote_function that execute another action from the same controller (the action will eventually update the page content)
I'd like this action to be only accessible when called by the JS but not when the corresponding route is directly entered as an URL in the browser.
Any idea how I could do this ?

You can use request.xhr? to check the request type, either its AJAX request or others (post, get). It returns true or false. If true you can perform the action.

You could use respond_with and respond_to
class MyController
respond_to :js
def index
respond_with(#users = User.all)
end
end

Related

render_to_string breaks action rendering

In my controller I want to render one of the HTML partials into a string variable, then send it with the Pusher and finalize the action with a regular rendering of the JS file (this is AJAX request).
I'm making an AJAX call and my action looks like this:
def create
#my_Object = ...
#html_content = escape_javascript(render_to_string :partial => 'my_partial', :object => #my_object )
Pusher.trigger(
'my-channel',
'my-event',
{ message: #html_content }
)
end
Given my understanding I would expect to see the "create.js" file being rendered at the end of this action, but this is not the case.
If in the "create.js" I put:
alert("test");
I do not see the dialog even though in rails logs I do see that the "create.js" partial is rendered.
When I remove the render_to_string part all works fine and the "create.js" gets properly rendered.
Can you help? What am I missing?
It seems that the fix is to explicitly call
respond_to do |format|
format.js
end
after the render_to_string.
This is strange as without the render_to_string call you do no thave to do that...

Ruby on Rails: how to create an object without switching the view

I am incredibly new to rails, so I think my questions is pretty simple. I essentially have an object submit button on a view page (so runs create and save for the specified object) but on selecting the button I don't want the view to change at all (so just stay with the same page that was already loaded). I'm having trouble editing the create action in my object controller so that nothing happens to the view (is this even possible? or is the idea that a new view must occur for every action).
Thanks for all your help!
Lisa
This can be done by creating an action in the controller,
class YourController < ApplicationController
def index
end
def youraction
end
end
,then set a route to it in the routes.rb
and then simply set your form to point to that url and add the
remote: true
option to the form tag.
So you want to store the object and you dont want the URL to change.
In your create action, after the object is saved. Redirect to that view.
so something like:
$ if #object.save
redirect_to :action => "index"
# or
# redirect_to object_path # you can set this in config/routes.rb
# or
# redirect_to :action => "show", :id => #object.id
You can do this by using ajax request:
in your controller create an action that does the job of creating the object you want:
class SomeController < ApplicationController
def index
#this is your view controller
end
def youraction
#create an object
render :nothing => true
end
end
and then bind your button with a javascript function that does the ajax request:
HTML
<button id="submit_button">Submit</button>
Javascript
$(document).ready(function (){
$('#submit_button').click(function(){
$.ajax({
url: 'url_to_the_controller/youraction',
type: 'POST'
success: function (){
alert('success');
},
error: function (){
alert('error');
}
});
});
}

Calling methods that render views conditionally in a Rails Controller

I am writing a Ruby on Rails application with a controller called "pages_controller" that is responsible for displaying pages to users. There are 3 different types of pages that can be displayed, and different things have to happen on the back end in each case, so I decided to break the functionality out into 3 methods within the controller. When the user requests a page, the "show" method is called, which figures out whether the page:
1. Belongs to the user
2. Belongs to another user, and can be viewed by the user requesting it
3. Belongs to another user, and cannot be viewed by the user requesting it (unauthorized)
The appropriate method is then called from there to display the page. The code looks something like this:
def show
if (something)
showMine
elsif (something else)
showAnother
else
showUnauthorized
end
end
def showUnauthorized
respond_to do |format|
format.html # showUnauthorized.html.erb
end
end
def showMine
respond_to do |format|
format.html # showMine.html.erb
end
end
def showAnother
respond_to do |format|
format.html # showAnother.html.erb
end
end
I am getting a template missing error because rails wants to render a view when "show" is called, but I do not want any views to be rendered when "show" is called. I simply want "show" to call the correct method from there, and the corresponding view for that method (showMine, showAnother, or showUnauthorized) to be rendered. How can I do this? Or am I going about this the wrong way entirely?
You need to declare these new actions that you have created in the routes file, as they don't belong to the RESTful routes.
I sugest to keep only the show action in your controller and create the IFs in the show view using the render method to include the partials(_showMine.html.erb, showAnother.html.erb, showUnauthorized)
example:
show view:
if (something)
<%= render 'showMine' %>
elsif (something else)
<%= render 'showAnother' %>
else
<%= render 'showUnauthorized' %>
end
I hope it helps...
I basically agree with Samy's comment, but here's some background:
The method that tells Rails what view to use is render. If there's no call to that method in your show method, Rails assumes you have a view called show.xxx.xxx, e.g. show.html.erb, that is supposed to be rendered. Note that it doesn't assume template will be prefixed with show because that's the name of the method. It assumes it will be show because that's the name of the action. The name of the action is passed to the controller as part of the request; it's not simply derived from the name of whatever method has a respond_to block in it.
All the respond_to blocks do is specify different view templates based on the MIME type of the request, but since you never call render, all of those extra methods are still trying to call the show view (show.html.erb in every case), because you never told Rails to render any other view, and the action name is show.
So, instead of the respond_to blocks, just call render [some_view] in each of your other methods.
This might not be the clearest answer, but I'd suggest also reading the following:
http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/
It describes what respond_to does, in particular how it keys off the action name to determine what view to render.

Rails 3 Custom Method Location

I am currently trying to add some parsing methods to a controller method in a Rails 3 application.
I have a controller action as follows:
def control
#device = Device.find(params[:id])
<do things>
parse_return(#returned_data)
end
and I added a custom method to the controller as below (this method would not have any routes and would only be accessible to controller actions):
def parse_return
<parse data>
end
but this does not appear to allow the parse_return method to be used. Is there somewhere else in the Rails app that I can put re-usable methods?
Thanks!
At a first glance it seems that you fail to render a response. Is it true that control action doesn't have an associated view?
In this case you have to manually call render in your action. For example, to render JSON response you can do this:
def control
# ...
render :json => parse_return(#returned_data),
:content_type => 'application/json',
:layout => false
end
You should include what the errors are.
What happens if you try this?
def parse_return(returned_data)
<parse data>
end
Perhaps the method is not expecting an parameter to be passed along with it.

How do I check if a controller action is already redirecting?

OK, as is often the case, I have a controller action in my app that is protected from unauthorized access by a before_filter. The only thing is that I need to redirect this action if another condition is true:
class Payment < ApplicationController
before_filter login_required
def new
redirect_to some_other_path if #order.is_free?
#payment = Payment.new
end
end
In my testing, I check to make sure that the action is correctly protected, however, it is also the case that the #order.is_free statement is true. When this is the case, I get the following error:
`render_with_no_layout': Can only render or redirect once per action
Is there any way of checking to make sure I'm not already redirecting or to override an existing redirect?
I am assuming that the login_required method performs a redirect if the user is not logged in. In which case:
Your before filter should return false after calling redirect. This will prevent the new action from ever being called. Later versions of rails automatically do this if you call render or redirect in a before_filter, so maybe you are using an older version.
Also you should return after the call to redirect in the new handler, unless you want to always create a new Payment object.
Your class should be PaymentController, not Payment. The reason for this is so the controller class and model class do not clash.
I don't think your before filter is what causes the double render error. Take a look at this example:
class PostsController < ApplicationController
before_filter :perform_a_redirect, :except => [:wtf]
def index
redirect_to 'http://google.com'
end
def wtf
render :text => 'wtf'
end
private
def perform_a_redirect
redirect_to :action => 'wtf'
end
end
When visiting /posts, I get redirected to /posts/wtf. No double render error. Assuming your 'login_required' method only redirects/renders once, I suspect that the code you are posting here is not the problem, but that something else is causing this.
The before filter is a red herring. When #order_is_free? the code is both setting up a redirect and falling through to a rendering of new. The redirect statement doesn't control the flow of the method. Add a return statement after the redirect, or you can even return the redirect, as in return(redirect_to :action => :show, :id => #order, :controller => :free_orders)

Resources