How to do static content in Rails?

Looking at different options:

One is to just put the static pages in the public/ folder, but I do want the header from layout/application to be consistent.

I tried this, but I got an error:

# in routes.rb:
map.connect '*path', :controller => 'content', :action => 'show'

# in content_controller.rb:
def show
  render :action => params[:path].join('/')
end

All I want is an easy way to put together things like my faq, contact, tos, privacy, and other non-application type pages somewhere easy by just creating an .rhtml. who has done this?


Solution 1:

For Rails6, Rails5 and Rails4 you can do the following:

Put the line below at the end of your routes.rb

  get ':action' => 'static#:action'

Then requests to root/welcome, will render the /app/views/static/welcome.html.erb.

Don't forget to create a 'static' controller, even though you don't have to put anything in there.

Limitation: If somebody tries to access a page that does not exist, it will throw an application error. See this solution below that can handle 404s

For Rails3 you have to use 'match' instead of 'get'

  match ':action' => 'static#:action'

Solution 2:

thoughtbot has a plugin called high_voltage for displaying static content: https://github.com/thoughtbot/high_voltage

Solution 3:

depends on the url structure, if you want the paths to come off of / (e.g. /about_us), then:

map.connect ':action', :controller => "static"

This should go at the very end of your routes file, Throw your .html.erb files into app/views/static and you are done.

e.g: throwing in about_us.html.erb, will give you a page at /about_us.

The item that you have in your question is great for a catch all route where you can analyze the array given to you at params[:path]. A bit more information on that at http://railscasts.com/episodes/46-catch-all-route

Solution 4:

Rendering an action doesn't make sense. You'll want to render a template (or a file) with a layout.

# Path relative to app/views with controller's layout
render :template => params[:path]

# ... OR

# Absolute path. You need to be explicit about rendering with a layout
render :file => params[:path], :layout => true

You could serve a variety of different templates from a single action with page caching.

# app/controllers/static_controller.rb
class StaticController < ApplicationController
  layout 'static'

  caches_page :show

  def show
    valid = %w(static1 static2 static3)
    if valid.include?(params[:path])
      render :template => File.join('static', params[:path])
    else
      render :file   => File.join(Rails.root, 'public', '404.html'), 
             :status => 404
    end
  end
end

Lastly, we'll need to define a route.

# config/routes.rb
map.connect 'static/:path', :controller => 'static', :action => 'show'

Try accessing these static pages. If the path doesn't include a valid template, we'll render the 404 file and return a 404 status.

  • http://localhost:3000/static/static1
  • http://localhost:3000/static/static3
  • http://localhost:3000/static/static2

If you take a look in app/public you'll notice a static/ directory with static1.html, static2.html and static3.html. After accessing the page for the first time, any subsequent requests will be entirely static thanks to page caching.

Solution 5:

I used the idea of a generalized controller from the answers given, but I wanted to catch 404s, so I put an action in it to handle either case:

# app/controllers/static_controller.rb
class StaticController < ApplicationController
    def static_or_404
        render params[:static]
    rescue ActionView::MissingTemplate
        render :not_found
    end
end

and then at the very bottom in my routing:

# config/routes.rb
get '*static', to: 'static#static_or_404'

It serves the view from app/views/static of the same name as the path, and if there isn't such a view, it serves app/views/static/not_found.html.erb. One could also replace render :not_found with redirect_to root_path or anything else one wanted to have happen.