Rendering 404 in sinatra if file not found - ruby

I have a basic sinatra app that renders files from a directory. What I'd like is returns 404 if page does not exist. Currently it raise 500 error.
get '/:page' do
erb :"pages/#{params[:page]}", layout: :"layouts/application"
end

Try this ;)
# 404 Error!
not_found do
status 404
erb :oops
end
Make yourself a 404 page with whatever name you like (mine is oops.erb, for example), and this should work just fine.
not_found is Sinatra's error-handling helper for grabbing error 500s and 404 not-founds that it returns. You can then change the HTTP status and corresponding view using it. Check out the documentation for all of Sinatra's error handler's: they're super useful!

You could do something like:
get '/:page' do
requested_erb = File.join(root, 'pages', params[:page])
pass unless File.exists?(requested_erb)
erb :"#{requested_erb}", :layout: :"layouts/application"
end
I haven't tested this, so there might be some issues with the above code, but that's the general idea in my head.

Related

Can't get custom error pages to work in Padrino

I started to build a website with padrino. At the moment the main class of my app is the simplest thing in the world:
class App < Padrino::Application
enable :sessions
get :index do
send_file 'public/view/index.html'
end
error 404 do
send_file 'public/view/errors/404.html'
end
end
So the views are simply htmls - the idea behind it is to use angularjs to render all the thingies provided by a rest api. I guess that's fairly standard.
My problem is - although it works fine for rendering the home page (localhost:3000/), the custom error doesn't work at all; let's say I try localhost:3000/test - the standard "Sinatra doesn’t know this ditty" page is rendered instead.
I'm running padrino 0.12.4 with WEBrick 1.3.1. What am I doing wrong here?
I believe what's going on here is that when you go to localhost:3000/test, your Sinatra app is looking for the "test" action under your App Controller. Obviously this action is not being found because it's not listed as a route! Therefore explicitly tell Sinatra to return a 404 page if the diddy wasn't found:
error Sinatra::NotFound do
content_type 'text/plain'
[404, 'Not Found']
end

Jekyll site via Sinatra and Heroku - can't route to new posts

I created a 'Hello, World' app using Sinatra and then pushed to Heroku and all worked.
I've since created a basic Jekyll blog, and am trying to access it via Heroku using the following routes:
get '/?' do
file.read("_site/index.html")
end
get '/.*.*' do
file.read("_site/#{params[:splat]}")
end
not_found do
file.read("_site/error/index.html")
end
The route to the index works fine link to my site
but as soon as I click to the first post it always fails.
I have tried so many variations of different routes for the :splat and get, just can't seem to get it to work? Any ideas?
In the route that's failing, before the file.read statement, add warn "splat = #{params[:splat]}" and that will output the result to the terminal, and you can see what it's actually getting, e.g.
get '/.*.*' do
warn "splat = #{params[:splat]}"
file.read("_site/#{params[:splat]}")
end
You could also try using an absolute path to the files, though if you're getting the index page then it suggests it's not needed:
config do
set :statics, File.expand_path(File.join(settings.root, "_site"))
end
get '/.*.*' do
file.read( File.join settings.statics, params[:splat] )
end
Unless there's something else you were planning to use Sinatra's routes for, you could probably remove the Sinatra routes entirely and just make the "_site" folder the public_folder, and then Sinatra will do the serving of the static files for you:
config do
set :public_folder, File.expand_path(File.join(settings.root, "_site"))
end
# no more to do...

How to test 404 Sinatra::NotFound errors with Rack::Test?

I have an application that raises 404 Sinatra::NotFound errors on production when the route is missing.
However, the following test passes:
it 'should not raise error' do
expect{ get '/unknown_path' }.to_not raise_error
end
Why don't 404 Sinatra::NotFound errors get raised in the test?
The following does raise the error and cause the test to fail:
get '/unknown_path' do
raise 'error'
end
How to test for 404 Sinatra::NotFound errors?
The issue is that get /unknown_path' is not actually raising an error – it's only receiving a 404 request.
From Sinatra: README
The error handler is invoked any time an exception is raised from a route block or a filter. The exception object can be obtained from the sinatra.error Rack variable...
Your test is testing for an actual error (this is what raise_error does), while Sinatra is squashing the error – otherwise, every time someone 404'd the server would crash!
Check out the Sinatra testing guide for better direction on forming your tests. The basic idea is that using get ... in a test sets the last_response local variable, which can then be tested for equality, etc.
last_response has a status attribute, so (like was mentioned in another answer) you can just test to make sure that last_response.status equals 404.
Edit:
I wasn't really clear about this. In testing mode, the application does actually raise errors.
From Sinatra Settings
raise_errors
raise exceptions (will stop application). Enabled by default when environment is set to "test", disabled otherwise.
So you don't actually want to raise just any error, but raise Sinatra::NotFound.new("Not found!") to raise the specific type of error. The Sinatra::NotFound error will trigger the 404 handler. Sinatra Error Handling
Try this:
get '/unknown_path'
assert_equal last_response.status, 404

Ruby and Sinatra

I started writing a simple Sinatra app today and I am trying to understand the error reporting but for some reason I can't get it to work correctly.
I know here, http://railsapi.com/doc/sinatra-v1.0/, it talks about working with error reporting/handling but when I run their examples I can't get it to work.
require 'sinatra'
error 400..510 do
'Boom'
end
get '/say/*' do
params[:splat]
end
When I run the app on my computer I get a 404 error code, but the 'Boom' text does not display in the browser, just the browser 404 page. I am sure I am doing something wrong, but just can't figure it out.
I will wager its your browser. On my MacBook Pro:
Chrome "helpfully" displays a "Oops! This link appears to be broken." page.
Safari displays the expected Boom text.
Firefox displays the expected Boom text.
It seems that Sinatra throws Sinatra::NotFound exception (404) to a specific handler. Simply modify the code as follows,
require 'sinatra'
not_found do
'Boom in NOT_FOUND.'
end
error 400..510 do
'Boom'
end
get '/say/*' do
params[:splat]
end
It works well in Chrome and Firefox on Mac OSX.

Can not access response.body inside after filter block in Sinatra 1.0

I'm struggling with a strange issue. According to http://github.com/sinatra/sinatra (secion Filters) a response object is available in after filter blocks in Sinatra 1.0. However the response.status is correctly accessible, I can not see non-empty response.body from my routes inside after filter.
I have this rackup file:
config.ru
require 'app'
run TestApp
Then Sinatra 1.0.b gem installed using:
gem install --pre sinatra
And this is my tiny app with a single route:
app.rb
require 'rubygems'
require 'sinatra/base'
class TestApp < Sinatra::Base
set :root, File.dirname(__FILE__)
get '/test' do
'Some response'
end
after do
halt 500 if response.empty? # used 500 just for illustation
end
end
And now, I would like to access the response inside the after filter. When I run this app and access /test URL, I got a 500 response as if the response is empty, but the response clearly is 'Some response'.
Along with my request to /test, a separate request to /favicon.ico is issued by the browser and that returns 404 as there is no route nor a static file. But I would expect the 500 status to be returned as the response should be empty.
In console, I can see that within the after filter, the response to /favicon.ico is something like 'Not found' and response to /test really is empty even though there is response returned by the route.
What do I miss?
The response.body is set Sinatra::Base#invoke, which wraps around Sinatra::Base#dispatch!, which in turn calls the filters. However, #invoke sets the response body after dispatch! is done, therefore the body is not yet set. What you want to do is probably better solved with a rack middleware.

Resources