Rails 3 rescue_from, with and working with custom modules - ruby-on-rails-3

I am writing a Rails app that I am wanting to DRY up just a tad bit and instead of calling my custom error class at the top of each controller I need it in, I placed it inside of a Module and just included that module.
Working code (Module):
module ApiException
class EmptyParameter < StandardError
end
end
Working code (Controller):
# include custom error exception classes
include ApiException
rescue_from EmptyParameter, :with => :param_error
# rescure record_not_found with a custom XML response
rescue_from ActiveRecord::RecordNotFound, :with => :active_record_error
def param_error(e)
render :xml => "<error>Malformed URL. Exception: #{e.message}</error>"
end
def active_record_error(e)
render :xml => "<error>No records found. Exception: #{e.message}</error>"
end
Here is my question, using the :with command, how would I call a method inside my custom module?
Something like this: rescue_from EmptyParameter, :with => :EmptParameter.custom_class

You could try something like this:
rescue_from EmptyParameter do |exception|
EmptyParameter.custom_class_method
end

Related

how can you test the 'catch all route' using ordinary controller tests?

Note: As per RafaeldeF.Ferreira's suggestion, this question has been heavily edited since its original form.
My JSON-based app needs to return something sensible when given a bad route. We already know that the following rescue_from ActionController::RoutingError doesn't work in Rails 3.1 and 3.2:
# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from ActionController::RoutingError, :with => :not_found
...
end
(This is well documented in https://github.com/rails/rails/issues/671.) So I implemented what José Valim describes in this blog entry (item 3), and details are provided below.
But testing it has been problematic. This controller rspec test:
# file: spec/controllers/errors_controller.rb
require 'spec_helper'
require 'status_codes'
describe ErrorsController do
it "returns not_found status" do
get :not_found
response.should be(StatusCodes::NOT_FOUND)
end
end
fails with:
ActionController::RoutingError: No route matches {:format=>"json", :controller=>"sites", :action=>"update"}
Yet this integration test calls ErrorsController#not_found and succeeds:
# file: spec/requests/errors_spec.rb
require 'spec_helper'
require 'status_codes'
describe 'errors service' do
before(:each) do
#client = FactoryGirl.create(:client)
end
it "should catch routing error and return not_found" do
get "/v1/clients/login.json?id=#{#client.handle}&password=#{#client.password}"
response.status.should be(StatusCodes::OK)
post "/v1/sites/impossiblepaththatdoesnotexist"
response.status.should be(StatusCodes::NOT_FOUND)
end
end
So: Is there a way to test the 'catch all route' using ordinary controller tests?
implementation details
If you want to see the implementation, here are the relevant code snippets
# config/routes.rb
MyApp::Application.routes.draw do
... all other routes above here.
root :to => "pages#home"
match "/404", :to => "errors#not_found"
end
# config/application.rb
module MyApp
class Application < Rails::Application
config.exceptions_app = self.routes
...
end
end
# config/environments/test.rb
MyApp::Application.configure do
...
config.consider_all_requests_local = false
...
end
# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def not_found
render :json => {:errors => ["Resource not found"]}, :status => :not_found
end
end

Rails, development env and error pages

I've made a simple app and I wanted to test pages for 404, 500 etc. http errors. I've changed config.consider_all_requests_local to false in my enviroments/development.rb but I've still got some problems so I would like to ask you a few questions...
If I type in my bowser something inappropriate like http://localhost:3000/products/dfgdgdgdgfd I still see the old "Unknown action" site. However if I type local ip adress of my computer for ex. http://192.168.1.106:3000/products/dfgdgdgdgfd I can see the 404 error page from public folder. Why is that happening?
I know that if I deploy my little project somewhere than my app will use the production mode and if any error would occure the 404 or 500 page will show up. But what if I want to make those error pages more dynamic ( for ex. rendering error message while using a layout with a list of popular products) or simply redirecting them to the main page?
2.1. The first solution that I found was to use rescue_from method in application controller:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, :with => :render_error
rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found
rescue_from AbstractController::ActionNotFound, :with => :render_not_found
rescue_from ActionController::RoutingError, :with => :render_not_found
rescue_from ActionController::UnknownController, :with => :render_not_found
rescue_from ActionController::UnknownAction, :with => :render_not_found
end
.
.
.
private
def render_error exception
Rails.logger.error(exception)
redirect_to root_path
#or
# render :controller=>'errors', :action=>'error_500', :status=>500
end
def render_not_found exception
Rails.logger.error(exception)
redirect_to root_path
#or
# render :controller=>'errors', :action=>'error_404', :status=>404
end
... but that code didn't work at any case.
2.2. The second solution was to place match "*path" , :to => "products#show", :id=>1 (that's the example main page in my silly app) or match "*path" , :to => "errors#error_404", :id=>1 at the end of the routes.rb file. That code works only for typos like http://192.168.1.106:3000/dfgdgdgdgfd because if I try http://192.168.1.106:3000/products/dfgdgdgdgfd (the controller exists but the action is not found) I still got the 404 page.
I've played a bit trying sth like match "*path/*act" , :to => "products#show", :id=>1 or match ":controller(/*act)" , :to => "products#show", :id=>8 but that didn't work either...
2.3. The third solution was to make controller for errors and a file in initializers folder with this code:
# initializers/error_pages.rb
module ActionDispatch
class ShowExceptions
protected
def rescue_action_in_public(exception)
status = status_code(exception).to_s
template = ActionView::Base.new(["#{Rails.root}/app/views"])
if ["404"].include?(status)
file = "/errors/404.html.erb"
else
file = "/errors/500.html.erb"
end
body = template.render(:file => file)
render(status, body)
end
end
end
That was quite useful because it would let me to render dynamic erb files but.. it's not rendering any layout. I've tried to change body = template.render(:file => file) to body = template.render(:partial => file, :layout => "layouts/application") but it was only cousing errors.
I know that I'm doing sth wrong and I belive that there is a working solution for those error pages so I hope that you can help...
Cheers.
In your application controller you need to override this method:
def method_missing(m, *args, &block)
Rails.logger.error(m)
redirect_to :controller=>"errors", :action=>"error_404"
# or render/redirect_to somewhere else
end
and then you have to combine it with this code:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, :with => :method_missing
rescue_from ActiveRecord::RecordNotFound, :with => :method_missing
rescue_from AbstractController::ActionNotFound, :with => :method_missing
rescue_from ActionController::RoutingError, :with => :method_missing
rescue_from ActionController::UnknownController, :with => :method_missing
rescue_from ActionController::UnknownAction, :with => :method_missing
end

Posting with Nested Resources

I have nested my resources (see below) and when I try to create a new entity, I get the following error. Does anyone know why I'm getting this error and how to solve it?
undefined method `applications' for nil:NilClass
resources careers do
resources applications
end
Within the 'Applications' controller I have:
before_filter [[:authenticate, :except => :new], :load_career]
def create
# The following line is where the error originates
#application = #career.applications.new(params[:application])
respond_to do |format|
...
end
end
private
def load_career
#career = Career.find(params[:career_id])
end
The Career and Application models have has_many :applications and belongs_to :career respectively.
And the '*_create_applications' migration has a career_id field.
I have never seen before_filters defined that way. I just tried it in Rails 3 and it doesn't seem to do anything. I would give each callback it's own before_filter call:
before_filter :authenticate, :except => :new
before_filter :load_career

NoMethodError - undefined method

I'm having problems displaying data from a separate controller. I have a number of users, each with many pages. I've followed this tutorial with a few minor adjustments.
The error that keeps appearing is:
NoMethodError in SitesController#show
undefined method `page' for #<ActionDispatch::Request:0x00000102452d30>
My routes.rb is as follows:
devise_for :users
resources :users, :only => [:index, :show] do
resources :pages, :shallow => true
end
match '/' => 'sites#show', :constraints => { :subdomain => /.+/ }
root :to => "home#index"
And I have a sites controller:
class SitesController < ApplicationController
def show
#site = Site.find_by_name!(request.page)
end
end
I've also tried:
def show
#site = Site.find_by_name!(params[:site])
end
Which gives a different error.
Am totally stuck trying to figure this out!
Looking forward to your assistance.
Bob
The problem is here: request.page
The request object is of the class ActionDispatch::Request, which does not have a page method.
To track down errors like this, you can try either looking at the docs or messing around in the debugger.
Try running your controller with --debugger enabled.
If you are running Ruby 1.8, install the ruby-debug gem.
If you are running Ruby 1.9, install the ruby-debug19 gem.
Add a debugger call here:
class SitesController < ApplicationController
def show
debugger
#site = Site.find_by_name!(request.page)
end
end
Run your server with the --debugger option.
See what p request.page does. I bet it will have an "undefined method" error, just you see when you try to view that controller action.
If you do a p request.class you can find out what class the object is, and then look up the docs to see how to use it.

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end