I need to do API version checks and so I've implemented a check in my controller method like:
if request.header["api_version"] != 1
respond_to do |format|
format.json {render :json => expiredMessage}
end
return
end
Works fine, I'm returning in the control statement so Rails doesn't complain about double render. I tried to DRY these up into an application controller method as a before_filter, but then I get the multiple renders complaint from Rails. It seems that the scope has changed such that the 'return' in the control statement no longer returns from the original controller method?
I'm wondering how I can do a render from the application controller method, or if there's a better way how can I implement this functionality? Thanks!
A before filter is called before each controller action, returning from it just allows that action to proceed. I would use exceptions. (following code is untested)
class FrontEndController < ApplicationController
rescue_from Exception do |exception|
if exception.is_a? ExpiredException
respond_to do |format|
format.json {render :json => expiredMessage}
return
end
end
end
before_filter :check_api_version
def check_api_version
raise ExpiredException if request.header["api_version"] != 1
end
end
class ExpiredException < Exception; end
Related
I am currently working on a web application built on Rails 3 that heavily uses Ajax/REST for the client side. Thus, I often find myself writing controller actions like this:
def create
if !params[:name]
respond_to do |format|
format.html { render json: {}, status: :not_found }
format.json { render json: {}, status: :not_found }
end
return
end
account = ...
respond_to do |format|
format.html { render json: account }
format.json { render json: account }
end
end
Nearly all of my actions are returning a json object in a success case or an error code. However, I always have to write this verbose respond_to block and a return, if I want the action to return earlier.
Instead I would like to use something like this instead, or a similar alternative:
def create
if !params[:name]
throw :not_found
end
account = ...
return account
end
How can this be done with Rails 3+ ?
Have a look into inherited_resources. This will allow you to rewrite your controller as:
class SomeController < ApplicationController
inherit_resources
respond_to :html, :js, :json
end
That is it. All of your create/read/update/delete methods will be accessible as usual. You can, as I have in the past, inherit from a master resources controller which uses inherited_resources, and then you can tweak the responses in a more general way.
class ResourcesController < ApplicationController
inherit_resources
respond_to :html, :js
def create
create! do |format|
format.js do
# generic code here for managing all create methods initiated via js
# current model is avialbe via 'resource'
# e.g 'resource.errors'
end
end
end
Then simply inherit from that controller:
class SomeController < ResourcesController
end
This abstraction can be overkill for most purposes, but it has come in extremely handy when working 30 or 40 models which all require similar controllers.
Inherited_resources offers many helpers for accessing the current model (referred to as resource) to facilitate dynamic references, so you can, for example, return relevant forms, or partials based on resource/model name.
To give you an idea of how to use this, you could return forms for the current controller by using the controller name in the parameters. Should be noted that malformed controller names will not reach this method (as it will return 404), so it is safe to use:
format.js do
render "#{params[:controller]}/form"
end
Best of all, you can override any of the methods yourself by defining them in a particular controller.
If your are always returning json, you can ommit the respond_to block and write it like :
def create
if !params[:name]
render json: {}, status: :not_found
return
end
account = ...
render json: account
end
I am completely new to rails and playing with the code to make pages work.
The link localhost:3000/zombies/1 works (show action)
but localhost:3000/zombies (index action) doesn't. Below are my routes and controller:
ROUTES ARE:
resources :zombies
CONTROLLER is:
class ZombiesController < ApplicationController
before_filter :get_zombie_params
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: #zombies }
end
end
def show
#disp_zombie = increase_age #zombie, 15
#zombie_new_age = #disp_zombie
respond_to do |format|
format.html # show.html.erb
format.json { render json: #zombie }
end
end
def increase_age zombie, incr
zombie = zombie.age + incr
end
def get_zombie_params
#zombie=Zombie.find(params[:id])
#zombies = Zombie.all
end
end
Why is this?
Editing answer based on the comment
I get a page with the error: ActiveRecord::RecordNotFound in
ZombiesController#index Couldn't find Zombie without an ID Rails.root:
C:/Sites/TwitterForZombies Application Trace | Framework Trace | Full
Trace app/controllers/zombies_controller.rb:85:in `get_zombie_params'
The url, localhost:3000/zombies which calls index action does not include id parameter.
That's why the app is failing at #zombie=Zombie.find(params[:id]).
If you want to fix this issue, use before_filter on show action only.
before_filter :get_zombie_params, only: :show
And insert this into index action as I have originally suggested.
def index
#zombies = Zombies.all
...
end
This is happening because when you define resources :zombies, you get these routes :
/zombies
/zombies/:id
Therefore when navigating to /zombies you don't have a params[:id], it is nil
Zombie.find method will raise an error if it can't find any record with the given id and halt further processing of your code.
You can use Zombie.find_by_id if you don't want an exception raised when there is no result.
But I don't think that this what you want here, you'd rather define a get_zombie_by_id method and a get_all_zombies method and separate the code from your get_zombie_params
Then you would have to define which method should be called before what action by changing your before_filter like so, in your case :
before_filter :get_zombie_by_id, :only => :show
before_filter :get_all_zombies, :only => :index
This way Zombie.find(params[:id]) will only get called when on the show action.
You can also use :except to do the opposite.
it does work because you need to send back ( to your index view ) the list of your zombies.
The get_zombie_params() excutes right but does not send #zombies to the index() action.
you need to do :
def index
#zombies = Zombie.all
#... the rest of the code
end
In my application, I store the user's ID in session[]. At the beginning of every controller action, I'm calling a method defined in the ApplicationController called current_user:
def current_user
#current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
At the beginning of my controllers' methods, I have the following:
#current_user = current_user
if #current_user == nil
redirect_to :home
return
end
This is obviously repetitive code and should be a method somewhere. I read the answer for this question, and tried putting my method into a parent class that my controller classes now descend from, however it seems like I can't redirect from that method now.
In my parent class, I have:
def verify_user
user = current_user
if user == nil
redirect_to "/"
return
end
return user
end
And now I've changed my controller methods to this:
#current_user = verify_user
This doesn't work, and I think I know why. For one, I can't simply call return in my verify_user method, as that obviously will just return to the controller. The redirect doesn't seem to have any affect, probably because format.html is being called after the redirect call, which was the reason for the return in the original code.
So, what am I doing wrong here, and what suggestion do you have to solve it? Is this the wrong approach? My main goal is to keep the entire "check if user is logged in otherwise redirect" to one line of code per controller method.
Take a look at the devise gem https://github.com/plataformatec/devise. It handles a lot of this basic user authentication logic for you. This specific problem can we solved by adding before_filter :authenticate_user! to the controllers or actions that need to be guarded.
Add the following logic to the ApplicationController class:
class ApplicationController < ActionController::Base
def current_user
...
end
def logged_in?
current_user.present?
end
def require_user
return true if logged_in?
render_error_message("You must be logged in to access this page",
new_user_session_url)
return false
end
def render_message message
respond_to do |format|
format.html {
if request.xhr?
render(:text => message, :status => :unprocessable_entity)
else
redirect_to(root_url, :notice => message)
end
}
format.json { render :json => message, :status => :unprocessable_entity }
format.xml { render :xml => message, :status => :unprocessable_entity }
end
end
end
Now add a before_filter to your controller:
class OrdersController < ApplicationController
before_filter :require_user
end
I seem to have an authorization hiccup in my Ruby on Rails app. I have been using the following method in my application controller and it has been working beautifully.
def require_owner
obj = instance_variable_get("##{controller_name.singularize.camelize.underscore}") # LineItem becomes #line_item
return true if current_user_is_owner?(obj)
render_error_message("You must be the #{controller_name.singularize.camelize} owner to access this page", root_url)
return false
end
I then filter in the specific controllers by:
before_filter :require_owner, :only => [:destroy, :update, :edit]
I recently created a new controller which has a bit of a different naming convention that seems to be causing a problem. Normally my controllers read messages_controller or posts_controller. In this specific case I named the resource box_wod which generated box_wods_controller.
This is the only controller that seems to be having a problem with this filter so I bet I can tell it is in the naming of it and therefore the application_controller method is not recognizing the owner of the record.
I am not getting an error message but the application is not letting me edit, update or destroy a record because I am not the BoxWod owner. My routes are correct as are my associations and the correct information is getting passed to the box_wod table.
Is there a way to rewrite the application_controller method to recognize the additional underscore in the box_wod resource? Or is this even my problem?
UPDATE:
Here are the three methods in the BoxWodsController:
def edit
#workout_count = Workout.count
#box_wod = BoxWod.find(params[:id])
end
def update
#box_wod = BoxWod.find(params[:id])
respond_to do |format|
if #box_wod.update_attributes(params[:box_wod])
flash[:notice] = 'BoxWod was successfully updated.'
format.html { redirect_to(#box_wod) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #box_wod.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#box_wod = BoxWod.find(params[:id])
#box_wod.destroy
respond_to do |format|
format.html { redirect_to(box_wods_url) }
format.js
end
end
In situations like this, I like to create a controller method that I can override when necessary. For example:
# application_controller.rb
class ApplicationController
def require_owner
obj = instance_variable_get("##{resource_instance_variable_name}")
# Do your authorization stuff
end
private
def resource_instance_variable_name
controller_name.singularize.camelize.underscore
end
end
# box_wods_controller.rb
class BoxWodsController
private
def resource_instance_variable_name
'box_wod' # Or whatever your instance variable is called
end
end
Lastly, please post your BoxWodsController code so we can better diagnose the problem.
It would seem that the #box_wod instance variable is not created until the require_owner method is invoked so current_user_is_owner? is checking a nil value, resulting in it always returning false. Perhaps you need another before_filter to populate the instance variable before require_owner is invoked.
Sorry if the question is obvious, I am only starting to work with Rails.
I have a following code in several controller methods now:
respond_to do |format|
if #project.save
format.html { redirect_to(edit_project_url(#project), :notice => '#{user.name} added to #{role}.') }
format.js
else
format.html { render :action => "edit" }
format.js #...
end
end
So the question is, what is the best way to do the same thing for errors in all methods?
Is it recommended that I use save! and handle it in rescue_action?
Or should I do my own respond method and pass save in a block?
It's often more convenient to use the exception-raising variant of save and rescue that later in the block than to branch like that. The advantage to exceptions is they'll bust out of transactions.
def create
#project.save!
respond_to do |format|
format.html { redirect_to(edit_project_url(#project), :notice => '#{user.name} added to #{role}.') }
format.js
end
rescue ActiveRecord::RecordInvalid
respond_to do |format|
format.html { render :action => "edit" }
format.js #...
end
end
You'll find that it gets really tricky to wrangle your way out of a pile of nested if statements when trying to save more than one object at a time, but a simple rescue for exceptions will handle it neatly.
def create
Project.transaction do
#project.save!
#something_else.save!
#other_stuff.save!
end
# ...
rescue ActiveRecord::RecordInvalid
# ...
end
If any one of those saves blows up you'll get an exception. To ensure that all of them are displaying validation errors you might have to call .valid? on each to prime them or you will have those after the failure left untested.
It's not a bad thing to use the if #object.save pattern. However, if you are doing exactly the same for all your actions on your controller, you can define a rescue_from action.
Something like
class MyController < ActionController::Base
rescue_from ActiveRecord::RecordInvalid do
render :action => edit
end
end