Static content in Rails 3
To auto-generate static pages in Rails 3, other than by simply adding html files to the ‘public’ directory, is very easy.
The most simple way is to just; add a controller such as ‘PagesController’, redirect all unmatched routes there, create a view and it will be displayed if the html/haml view exists – with any others raising a template not found error (display 500 to the production user).
However, I usually want to capture whats going on behind the scenes, or share variables across a group of pages.
Disclaimer – this isn’t the ideal or the best way, but it is my convenient way, let me know if you have any tips ’n tricks to improve it further!
My usual setup can be handy, if you want custom static pages for both scoped areas of those logged in and those not, or even subdomains, anything is possible really with the Rails 3 routing setup. Simply add the following:
Create a pages controller class, under any submodule or folder structure you like.
This will simply check if the view template exists (even haml) and render that, otherwise render out your custom 404 page, the only params being sent are ‘base_page’ which will be set by the routes.
app/controllers/pages_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class PagesController < ApplicationController def base_page_processor view_prefix = "pages" if params[:base_page].present? && template_exists?(params[:base_page], view_prefix) render "#{view_prefix}/#{params[:base_page]}" else #TODO : Notify missing url via email error or error notification service render '/public/404.html', :status => 404 end end end |
To ensure all routes are matched, that aren’t specifically set in the config/routes.rb file, add this line right at the very end of the file (or at the end of a specific scope, such as a logged in section) :
config/routes.rb
1 |
match ':base_page', :controller => 'pages', :action => 'base_page_processor', :as => :page_processor |
Some specs to help you along the way (using RSpec), and ensure you don’t break the setup as time goes on:
spec/controllers/pages_controller_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
require 'spec_helper' describe PagesController, 'automatic paths' do before(:all) do @routes = Rails.application.routes end it "should route all traffic and render if template exists" do ["/some_random_path", "/something_else", "/doesnt_exist"].each do |dynamic_path| @routes.recognize_path(dynamic_path).should == {:controller => 'pages', :action => 'base_page_processor', :base_page => dynamic_path.gsub(/^\//,'')} end end it 'should not route any traffic with sub paths' do ["/some_random_path/with_sub/lines", "/something_else/123/something.csv", "/doesnt_exist/nope"].each do |dynamic_path| lambda { @routes.recognize_path(dynamic_path) }.should raise_error(ActionController::RoutingError) end end describe 'with templates existing' do it 'should render the template' do controller.stub!(:template_exists?).and_return(true) controller.stub!(:render) controller.should_receive(:render).with( 'pages/something_that_exists' ) get 'base_page_processor', :base_page => 'something_that_exists' response.status.should == 200 end end describe 'when template doesnt exist' do it 'should render the 404 page' do get 'base_page_processor', :base_page => 'something_that_doesnt_exist' response.status.should == 404 response.should render_template('public/404') end end end |
Now simply add any view template in your set page prefix, in the above example this would be ‘/app/views/pages/any_template_file’, this will now immediately load in your app. Easy.