ruby on rails 3.1 global exception handler - ruby

I'm developing an app with Rails 3.1.2 but I can't find some documentation that works with errors / exception (like 404) on this version of rails.
i have tried things like:
In application controller
rescue_from ActiveRecord::RecordNotFound,ActionController::RoutingError,
ActionController::UnknownController, ActionController::UnknownAction, :NoMethodError, :with => :handle_exception
def handle_exception
render :template => 'error_pages/error'
end
environment/development.rb
config.consider_all_requests_local = false
Where can I find a solution?
Thanks in advance...

This should work:
In application controller
class NotFound < StandardError; end
rescue_from NotFound, :with => :handle_exception
def handle_exception
render :template => 'error_pages/error'
end

Look at action_dispatch/middleware/show_exceptions.
From the documentation in the source:
# This middleware rescues any exception returned by the application
# and wraps them in a format for the end user.
Short story short, it renders ActionDispatch::ShowExceptions.render_exception when the wrapped application (Rails, in your case), encounters an unrescued exception.
If you look through the default implementation, it ends up rendering something like public/500.html, which is what you see in the production environment. Overwrite the method or method chain it as you see fit to add your own implementation.

Related

Paperclip in Rails 4 - Strong Parameters Forbidden Attributes Error

Having a problem with a Paperclip upload in Rails 4 - failing on ForbiddenAttributesError (strong parameters validation). Have the latest paperclip gem and latest rails 4 gems.
I have a model "Image" with an attached file "upload" in the model:
has_attached_file :upload, :styles => { :review => ["1000x1200>", :png], :thumb => ["100x100>", :png]}, :default_url => "/images/:style/missing.png"
The image model was created with a scaffold, and I added paperclip migrations. The form partial was updated to use
f.file_field :upload
the form generates what appears to be a typical set of paperclip params, with the image param containing the upload. I am also passing a transaction_id in the image model, so it should be permitted. But that's it - the image and the transaction ID.
I expected to be able to write the following in my controller to whitelist my post - but it failed:
def image_params
params.require(:image).permit(:transaction_id, :upload)
end
So I got more explicit - but that failed too:
def image_params
params.require(:image).permit(:transaction_id, :upload => [:tempfile, :original_filename, :content_type, :headers])
end
I'm a bit frustrated that Rails 4 is not showing me what ForbiddenAttributesError is failing on in a development environment - it is supposed to be showing the error but it does not - would be a nice patch to ease development. Or perhaps everyone else is getting something that I am missing! Thanks much for the help.
I understand what happened here now - and will leave this up in the hope it helps someone else. I was porting code from a rails 3 project and missed the line that created the image:
#image = current_user.images.new(params[:image])
In rails 4 this is incorrect (I beleive). I updated to
#image = current_user.images.new(image_params)
and that solved my problem.
It looks like your first one should have worked. This is what I use for my projects.
class GalleriesController < ApplicationController
def new
#gallery = Gallery.new
end
def create
#user.galleries.new(gallery_params)
end
private
#note cover_image is the name of paperclips attachment filetype(s)
def gallery_params
params.require(:gallery).permit(:cover_image)
end
end

Confusion about ways to use JSON in ruby sinatra application

I'm making a Ruby Sinatra application that uses mongomapper and most of my responses will be in the JSON form.
Confusion
Now I've come across a number of different things that have to do with JSON.
The Std-lib 1.9.3 JSON class: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html
The JSON Gem: http://flori.github.io/json/
ActiveSupport JSON http://api.rubyonrails.org/classes/ActiveSupport/JSON.html because I'm using MongoMapper which uses ActiveSupport.
What works
I'm using a single method to handle responses:
def handleResponse(data, haml_path, haml_locals)
case true
when request.accept.include?("application/json") #JSON requested
return data.to_json
when request.accept.include?("text/html") #HTML requested
return haml(haml_path.to_sym, :locals => haml_locals, :layout => !request.xhr?)
else # Unknown/unsupported type requested
return 406 # Not acceptable
end
end
the line:
return data.to_json
works when data is an instance of one of my MongoMapper model classes:
class DeviceType
include MongoMapper::Document
plugin MongoMapper::Plugins::IdentityMap
connection Mongo::Connection.new($_DB_SERVER_CNC)
set_database_name $_DB_NAME
key :name, String, :required => true, :unique => true
timestamps!
end
I suspect in this case the to_json method comes somewhere from ActiveSupport and is further implemented in the mongomapper framework.
What doesn't work
I'm using the same method to handle errors too. The error class I'm using is one of my own:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
attr_reader :action # :create, :update, :read or :delete
attr_reader :model # Model class
attr_reader :entity # Entity on which the error occured, or an ID for which no entity was found.
def initialize(action, model, entity = nil)
#action = action
#model = model
#entity = entity
end
end
Of course, when calling to_json on an instance of this class, it doesn't work. Not in a way that makes perfect sense: apparantly this method is actually defined. I've no clue where it would come from. From the stack trace, apparently it is activesupport:
Unexpected error while processing request: object references itself
object references itself
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:75:in `check_for_circular_references'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:46:in `encode'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `block in encode_json'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `each'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `map'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `encode_json'
But where is this method actually defined in my class?
The question
I will need to override the method in my class like this:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
def to_json
#fields to json
end
end
But I don't know how to proceed. Given the 3 ways mentioned at the top, what's the best option for me?
As it turned out, I didn't need to do anything special.
I had not suspected this soon enough, but the problem is this:
class EntityCrudError < StandardError
..
attr_reader :model # Model class
..
end
This field contains the effective model class:
class DeviceType
..
..
end
And this let to circular references. I now replaced this with just the class name, which will do for my purposes. Now to_json doesn't complain anymore and I'm happy too :)
I'm still wondering what's the difference between all these JSON implementations though.

Rails 3 AJAX: wrong constant name

I am trying to do Ajax login with Devise, as explained here: http://jessehowarth.com/2011/04/27/ajax-login-with-devise#comment-5 (see comment from jBeasley).
My controller is attempting to return
class Users::SessionsController < Devise::SessionsController
def failure
render :json => {:success => false, :errors => ["Login failed."]}
end
end
which results in this error:
NameError (wrong constant name ["{\"success\":false,\"errors\":[\"Login failed.\"]}"]Controller):
and Firebug showing [500 Internal Server Error].
How can I fix this? I am running Rails 3.1 and devise 1.4.5.
Thanks!!
Did you do the step recommended by Jeff Poulton in comment #4? The :recall option in 1.4.5 looks to be completely incompatible to older versions. It now requires you send the controller, whereas in the tutorial you're following he just sends the action (the old way).
In your case, :recall => :failure must be changed to :recall => "users/sessions#failure" in Devise 1.4.5.
This is because of the way the controller for the failure action is determined. In older versions, it was simply pulled from the params.
def recall_controller
"#{params[:controller]}.camelize}Controller".constantize
end
# called via recall_controller.action(warden_options[:recall]).call(env)
In 1.4.5, it expects a string specifying the controller and action, in the style of routes:
def recall_app(app)
controller, action = app.split('#')
controller_name = ActiveSupport::Inflector.camelize(controller)
controlller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller")
controller_klass.action(action)
end
# called via recall_app(warden_options[:recall]).call(env)
It would seem as though your app is actually passing the JSONified hash of options to recall_app, which, lacking a '#', isn't being split, and the entire string is concatenated to "Controller" to attempt to ascertain the failure controller's class.
You are missing the return in
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
Does that make a difference?

How does rescue_action in Rails3 work?

I would like to implement this
class SecurityTransgression < StandardError; end
def create
raise SecurityTransgression unless ...
end
class ApplicationController < ActionController::Base
def rescue_action(e)
case e
when SecurityTransgression
head :forbidden
end
end
end
from the this blogpost.
The problem is it does not work. I dont see a forbidden page but standard Rails error page "SecurityViolation in MyController#action". I digged that some rescue_action methods works only in the production mode. I tried that and it is the same. No change.
My question: is there any good documentation of the rescue_action method (and others)? Does this work under Rails 3.0? Because it seems this is some old
Take a look at rescue_from at the API documentation.
The rescue_action method is normally called internally with the #_env hash being passed as a parameter. The method is expecting the Exception instance to exist within the "action_dispatch.rescue.exception" key.
If you must use the rescue_action method directly, you can do the following:-
#_env[ "action_dispatch.rescue.exception" ] = exception
rescue_action( #_env )
or even more simple:-
rescue_action( { "action_dispatch.rescue.exception" => exception } )

Calling Sinatra from within Sinatra

I have a Sinatra based REST service app and I would like to call one of the resources from within one of the routes, effectively composing one resource from another. E.g.
get '/someresource' do
otherresource = get '/otherresource'
# do something with otherresource, return a new resource
end
get '/otherresource' do
# etc.
end
A redirect will not work since I need to do some processing on the second resource and create a new one from it. Obviously I could a) use RestClient or some other client framework or b) structure my code so all of the logic for otherresource is in a method and just call that, however, it feels like it would be much cleaner if I could just re-use my resources from within Sinatra using their DSL.
Another option (I know this isn't answering your actual question) is to put your common code (even the template render) within a helper method, for example:
helpers do
def common_code( layout = true )
#title = 'common'
erb :common, :layout => layout
end
end
get '/foo' do
#subtitle = 'foo'
common_code
end
get '/bar' do
#subtitle = 'bar'
common_code
end
get '/baz' do
#subtitle = 'baz'
#common_snippet = common_code( false )
erb :large_page_with_common_snippet_injected
end
Sinatra's documentation covers this - essentially you use the underlying rack interface's call method:
http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route
Triggering Another Route
Sometimes pass is not what you want, instead
you would like to get the result of calling another route. Simply use
call to achieve this:
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end
I was able to hack something up by making a quick and dirty rack request and calling the Sinatra (a rack app) application directly. It's not pretty, but it works. Note that it would probably be better to extract the code that generates this resource into a helper method instead of doing something like this. But it is possible, and there might be better, cleaner ways of doing it than this.
#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'
get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')
resource.upcase
end
get '/otherresource' do
"test"
end
If you want to know more about what's going on behind the scenes, I've written a few articles on the basics of Rack you can read. There is What is Rack? and Using Rack.
This may or may not apply in your case, but when I’ve needed to create routes like this, I usually try something along these lines:
%w(main other).each do |uri|
get "/#{uri}" do
#res = "hello"
#res.upcase! if uri == "other"
#res
end
end
Building on AboutRuby's answer, I needed to support fetching static files in lib/public as well as query paramters and cookies (for maintaining authenticated sessions.) I also chose to raise exceptions on non-200 responses (and handle them in the calling functions).
If you trace Sinatra's self.call method in sinatra/base.rb, it takes an env parameter and builds a Rack::Request with it, so you can dig in there to see what parameters are supported.
I don't recall all the conditions of the return statements (I think there were some Ruby 2 changes), so feel free to tune to your requirements.
Here's the function I'm using:
def get_route url
fn = File.join(File.dirname(__FILE__), 'public'+url)
return File.read(fn) if (File.exist?fn)
base_url, query = url.split('?')
begin
result = self.call('REQUEST_METHOD' => 'GET',
'PATH_INFO' => base_url,
'QUERY_STRING' => query,
'rack.input' => StringIO.new,
'HTTP_COOKIE' => #env['HTTP_COOKIE'] # Pass auth credentials
)
rescue Exception=>e
puts "Exception when fetching self route: #{url}"
raise e
end
raise "Error when fetching self route: #{url}" unless result[0]==200 # status
return File.read(result[2].path) if result[2].is_a? Rack::File
return result[2].join('') rescue result[2].to_json
end

Resources