Database driven root level routing, like /some_page_name

I have a “page” model, and I want to be able access those pages with a
URL like: http://foo.com/my_page_name

Just wondering if there is a best practice for this. I have avoided it
this far because I didnt want these routes conflicting with the routes
of the rest of the app.

I see 2 ways off the top of my head.

  1. Use a low priority route that will match any single parameter URL.
    This route will call the page controller and show the page. Since its
    low priority, it only triggers if no other routes match. This seems
    like it might make 404’s a bit messy to handle since I’m basically
    creating a catch all that shouldn’t always work.

  2. Ask my Page model for a regex to match the id parameter with. This
    would be a cleaner solution in the routes themselves, but I don’t like
    the idea of hitting the database to check routing.

Any comments or ideas?

Could you accept the slightly-less-beautiful:

http://foo.com/pages/my_page_name?

If so, you could override to_param on your Page class and have it
return the page name. If you do that then the Pages controller can
use Page.find_by_name to show the page.

On May 13, 5:01 pm, Alex W. [email protected]

AndyV wrote:

Could you accept the slightly-less-beautiful:

http://foo.com/pages/my_page_name?

If so, you could override to_param on your Page class and have it
return the page name. If you do that then the Pages controller can
use Page.find_by_name to show the page.

On May 13, 5:01 pm, Alex W. [email protected]

I have that now. The problem is that my client keeps wanting shorter at
the root level URLs for print in ads and what not. Easier to type and
remember.

On May 13, 2008, at 6:17 PM, Alex W. wrote:

I have that now. The problem is that my client keeps wanting
shorter at
the root level URLs for print in ads and what not. Easier to type and
remember.

In that case… you might suggest

http://foo.com/xabc/my_page_name
http://foo.com/xbcd/my_page_name
http://foo.com/xcde/my_page_name

where /x…/ can be routed based on starting with x and the rest maps
to a specific campaign. You get a routing cop-out, but you can also
log the request URLs to provide tracking for response to very
specific sources. Keep the string short, but you can still get as
detailed as making every advertising source a unique string.

Doesn’t answer your question (not much of a routes guru yet), but
maybe there’s an opportunity to do something even more beneficial
than planned…

– gw

Sorry, I’d guessed that might be the case.

One other possibility… sometimes it’s easy to overlook that even
things like the routes.rb file are just specialized ruby scripts. As
such you can still stick code in there and dynamically create the
routes. Something like this totally untested, off-the-top-of-my-head
block:

Page.find(:all).each do |routed_page|
map.connect “/
#{page.slug}”, :controller=>‘pages’, :action=>‘show’, :id=>page.id
end

Interested to know how/if this works but don’t have time to test it.

HTH,
AndyV

On May 13, 9:17 pm, Alex W. [email protected]

AndyV wrote:

Page.find(:all).each do |routed_page|
map.connect “/
#{page.slug}”, :controller=>‘pages’, :action=>‘show’, :id=>page.id
end

Interesting approach!

Although, I am pretty sure that routes.rb only gets evaluated when rails
boots up, so new pages that are added would not have a route.

I did manage to come with a solution after some digging that does seem
to work though. I added this to the bottom of my routes file:

map.page ‘:id’, :controller => ‘pages’, :action => ‘show’

Since its at the bottom it gets referenced last so it doesn’t interfere
with other named routes. And the pages/show action will raise a
ActiveRecord::RecordNotFound exception if the page isn’t in the
database, which rails rescues and displays a 404. So I guess it works
great.

Thanks for the ideas all.

On Tue, May 13, 2008 at 2:01 PM, Alex W.
[email protected] wrote:

I have a “page” model, and I want to be able access those pages with a
URL like: http://foo.com/my_page_name

  1. Use a low priority route that will match any single parameter URL.
    This route will call the page controller and show the page. Since its
    low priority, it only triggers if no other routes match. This seems
    like it might make 404’s a bit messy to handle since I’m basically
    creating a catch all that shouldn’t always work.

I do something a lot like this for my app. After the normal rails
default route, I catch everything else with my show_page route. My
page controllers show action checks the db for a page with the
appropriate path and displays it - or renders my custom “not found”
message with a status code of 404.

routes.rb

Install the default routes as the lowest priority.

map.connect ‘:controller/:action/:id’
map.connect ‘:controller/:action/:id.:format’

Finally let CMS figure out what to do with fall through pages

map.show_page ‘*url_parts’, :controller => “pages”, :action => “show”

pages_controller.rb

def show
@page = Page.find_by_url_parts(params[:url_parts])

respond_to do |format|
  if @page
    store_location
    response.headers['Last-Modified'] = 

@page.updated_at.to_s(:rfc822)
format.html # show the page
format.xml { render :xml => @page }
else
format.html {
title = “Page Not Found”
body = ‘

Please use the search box above


render_404( [“”], title, body)
}
format.xml { head :status => :not_found }
end
end
end

and in my application.rb controller, the render_404 method:

def render_404(base, title, body)
@page = Page.find_by_url_parts( base )
@page.body = body
@page.title = title
render :template => “pages/show”, :status => 404
end

render_404 grabbing a page (the home page) is so that the styling and
navigation stuff gets built and sent to the user.


Cynthia K.