74

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?

jwadsack
  • 5,708
  • 2
  • 40
  • 50
Satchel
  • 16,414
  • 23
  • 106
  • 192

8 Answers8

179

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'
Roland Studer
  • 4,315
  • 3
  • 27
  • 43
  • 4
    +1 Awesome, just what I needed. This should get more votes up as it's for the latest Rails. – spong Aug 19 '11 at 01:45
  • 14
    If anyone else is wondering, the ":action" here is magic; it won't work if you call it ":id" or ":title" or whatever. – mahemoff Dec 26 '11 at 21:41
  • 1
    In Rails4, I get an alert in development mode saying to replace "match" with "get". – joseph Mar 13 '13 at 20:34
  • @joseph why don't you check how it works in rails4 and post it as a new answer, don't have the time right now to update my answer. – Roland Studer Mar 14 '13 at 08:37
  • 1
    @RolandStuder Sorry I wasn't clear - all you have to do is replace "match" with "get" and it works perfectly. :) – joseph Mar 14 '13 at 16:44
  • @joseph Tested it now, works nicely with get, I updated my answer to reflect that. Thanks a lot for your input! – Roland Studer Mar 27 '13 at 11:07
  • You can also also use ':to', generate named routes with ':as', and add constraints with regular expressions: "get ':action', to: 'static#:action', as: static, action: /(eat|drink|sleep)/". – Jan Hettich Aug 31 '14 at 07:12
  • 2
    How to avoid exceptions since this is a catch-all? Ideally you want to give a 404, not trigger an exception. I was trying to find how this is documented, it is not triggering a missing method, it just looks for the view on disk. – Amala Oct 23 '14 at 18:08
  • See https://stackoverflow.com/a/59903043/2037537 for a extended, but just as convenient solution that handles 404s – Roland Studer Jul 10 '20 at 14:51
  • This is super eloquent. Thanks very much! – Rich May 31 '21 at 19:26
  • How would I handle routing in this case? Can I use the rails lin_to helper with this method? – Rich Jun 06 '21 at 19:02
  • 1
    @Rich No, as this works as a wild card, there are no link_to helpers. You point to the raw paths like `/hello` – Roland Studer Jun 08 '21 at 07:09
31

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

Zabba
  • 64,285
  • 47
  • 179
  • 207
Gabe Martin-Dempesy
  • 7,687
  • 4
  • 33
  • 24
22

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

Omar Qureshi
  • 8,963
  • 3
  • 33
  • 35
13

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.

Tate Johnson
  • 3,910
  • 1
  • 23
  • 21
3

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.

eriese
  • 175
  • 7
  • Awesome stuff! I just came back to my answer and wanted to write up how to handle 404s then I saw you already did that, and now I link to your solution instead. In a way it would still be good to update my answer for visibility. Or maybe you want to make an edit to my answer, so you get some attribution. Much deserved upvote. I hope that the best answer, will not stay with the least votes for too long. – Roland Studer Jul 10 '20 at 14:55
  • This is great. I was able to rip out high voltage and replace it with 8 lines of code. Thanks very much, this is super helpful. – Rich May 31 '21 at 19:25
2

Considering if u have 1 Home Controller with couple method like show, aboutus, privacy :

class HomesController < ApplicationController
  def show
  end
  def privacy
  end
  def aboutus
  end
end

And map the show method to your root, and map the other to some named routes like

map.root      :controller => "homes", :action => "show"
map.aboutus "/aboutus", :controller => "homes", :action => "aboutus"
map.privacy "/privacy", :controller => "homes", :action => "privacy"

And with view for each

app/views/homes/aboutus.html.erb --> you get http://localhost:3000/aboutus
app/views/homes/show.html.erb --> you get http://localhost:3000 (root)
app/views/homes/privacy.html.erb --> you get http://localhost:3000/privacy

All using the same layout at app/views/layout/application.html.erb

gkrdvl
  • 960
  • 9
  • 20
2

Lindsaar solution is one of the best I ever seen. He build a caching static pages that expired when git revision changed.

<%= cache "site-page-#{@page_name}-#{App.git_revision}" do %>
  <%= render :partial => @page_name %>
<% end %>
ecleel
  • 11,748
  • 15
  • 48
  • 48
1

Create a PagesController for your static pages (e.g contact) and insert

def contact_page
end

in config/routes.rb insert

get 'contact' => 'pages#contact_page'

which will display the content from views/pages/contact_page.html.erb

GoodViber
  • 791
  • 5
  • 2
  • Why the downvote? I appreciate my answer relates to Rails 4, but will answer someones question nonetheless. – GoodViber Jul 23 '15 at 10:50