0
votes

I have an store application, where I need to make custom routing system where URL stores categories for products. For example, http://example.com/languages/ruby/rails will display category#show named 'rails', that has parent named 'ruby', that has parent named 'languages' and and URL of http://example.com/languages/ruby/rails/store will display product in this category.
Currently I have:
category.rb

belongs_to :parent, class_name: 'Category'
has_many :categories, foreign_key: :parent_id
has_many :products

routes.rb

resources :categories, :path => '', :only => [:index, :show] do
  resources :products, :path => '', :only => [:show]
end
root :to => 'products#index'

but it still stacks up to 2, e.g. URL http://example.com and http://example.com/languages shows list of categories/subcategories, but http://example.com/languages/ruby have params: {"action"=>"show", "controller"=>"products", "category_id"=>"language", "id"=>"ruby"}
Removing products from routes does not help at all - then it just says that No route matches [GET] "/language/ruby", although I assume It might cause need for extra check if current URL point on category or product later on.
Also I tried get '*categories/:id', to: 'category#show' variations + I am using friendly_id gem so that path do not look like http://example.com/2/54/111/6
I just want to find out what is the best ruby on rails solution for this kind of situations, when you need search engine optimizations + endless (e.g. no way to define how deep such recursion can go) nested resources that nest themselves (including fact that category/language/category/ruby/category/rails just looks ugly).

Note: most information I used is taken from Stack Overflow and railscasts.com (including pro/revised episodes), so mentioning a good source with information like this will be great too.

1

1 Answers

0
votes

I solved this myself recently with a CMS I built on Rails recently. I basically construct the routes dynamically at runtime from the database records. I wrote this blog post on the strategy:

http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4

The core of the solution (adapting the blog post above) is simply iterate over the database records and construct the routes needed for each category. This is the main class for doing that:

class DynamicRouter
  def self.load
    Website::Application.routes.draw do

      Category.all.each do |cat|
        get cat.route, 
          to: "categories#show", 
          defaults: { id: cat.id }, 
          as: "#{cat.routeable_name}_#{cat.name}"
      end
    end
  end

  def self.reload
    Website::Application.routes_reloader.reload!
  end
end

For the above, the Category model should implement a "routeable_name" method which simply gives an underscored version of the category name that uniquely names that category's route (its not strictly necessary, but helps when doing "rake routes" to see what you have). and the #route method constructs the full route to the category. Notice the defaults which sets the ID param for the category. This makes the controller action a very simple lookup on the category's ID field like so:

class CategoryController < ApplicationController
  def show
    @category = Category.find(params[:id])
  end
end