Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic CMS like routes in ruby on rails

I want to create a CMS like site where the user starts off with a some generic pages, i.e.

  • homepage
  • about
  • contact
  • etc

and from there can add child pages dynamically, for example

  • homepage
    • articles
      • article1
        • something
          • something-else
      • article2
  • about
  • contact
  • etc

To achieve this I'm planning on using some kind of self-referential association like

class Page < ActiveRecord::Base
  belongs_to :parent, :class_name => 'Page'
  has_many :children, :class_name => 'Page'
end

The one thing I'm struggling with is the route generation. Because pages can be added on the fly I need to dynamically generate routes for these pages and there is no way of knowing how many levels deep a page may be nested

So if I start off with the homepage: /

and then start adding pages i.e.

/articles/article1/something/something-else/another-thing

How can something like that be achieved with the rails routing model?

like image 818
KJF Avatar asked Apr 22 '09 08:04

KJF


People also ask

How do routes work in Ruby on Rails?

Rails routing is a two-way piece of machinery – rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings.

What is routing in Ruby on Rails?

The routing module provides URL rewriting in native Ruby. It's a way to redirect incoming requests to controllers and actions. It replaces the mod_rewrite rules. Best of all, Rails' Routing works with any web server.

What are rails resource routes?

Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. A single call to resources can declare all of the necessary routes for your index , show , new , edit , create , update , and destroy actions.


3 Answers

Once you have some way to generate the URL string for your Page records (and I'll leave that part up to you), you can just map every page in config/routes.rb:

Page.all.each do |page|
  map.connect page.url, :controller => 'pages', :action => 'show', :id => page
end

And have an observer hook the page model to reload routes when something changes:

class PageObserver < ActiveRecord::Observer
  def reload_routes(page)
    ActionController::Routing::Routes.reload!
  end
  alias_method :after_save,    :reload_routes
  alias_method :after_destroy, :reload_routes
end

Don't forget to edit config/environment.rb to load the observer:

# Activate observers that should always be running
config.active_record.observers = :page_observer
like image 189
kch Avatar answered Nov 10 '22 19:11

kch


One solution to this prob is to dynamically load routes from hooks on your models. From example, a snippet from the Slug model on my site:

class Slug < ActiveRecord::Base

  belongs_to :navigable

  validates_presence_of :name, :navigable_id
  validates_uniqueness_of :name

  after_save :update_route

  def add_route
    new_route = ActionController::Routing::Routes.builder.build(name, route_options)
    ActionController::Routing::Routes.routes.insert(0, new_route)
  end

  def remove_route
    ActionController::Routing::Routes.routes.reject! { |r| r.instance_variable_get(:@requirements)[:slug_id] == id }
  end

  def update_route
    remove_route
    add_route
  end

  def route_options
    @route_options ||= { :controller     => navigable.controller, 
                         :action         => navigable.action, 
                         :navigable_id   => navigable_id,
                         :slug_id        => id }
  end

end

This inserts the route at top priority (0 in the routing array in memory) after it has been saved.

Also, it sounds like you should be using a tree management plugin and like awesome nested set or better nested set to manage the tree for your site.

like image 30
Ben Crouse Avatar answered Nov 10 '22 17:11

Ben Crouse


You have to parse the route yourself

map.connect '*url', :controller => 'pages', :action => 'show'

Now you should have a params[:url] available in your action that is the request path as an array separated by the slashes. Once you have those strings its a simple matter to find the models you need from there.

That was from memory, and it's been a long while. Hope it works for you.

like image 22
Alex Wayne Avatar answered Nov 10 '22 17:11

Alex Wayne