with reference to Rails url options based on model attribute, I have a proposed solution but I am not sure if this is the way to proceed. I am also looking to create routes based on model's attributes for seo purpose. Here it goes.
I have a business models, and the routes generated is /businesses/id
. With friendlyid gem, it becomes businesses/slug
However, I want the route to become /business-type/business-name-and-address
for example.
So with reference to Code Connoisseur's blog
# app/models/dynamic_router.rb
class DynamicRouter
def self.load
Ttt::Application.routes.draw do
Business.all.each do |biz|
get "/#{biz.slug}", :to => "businesses#show", defaults: { id: biz.id, name: biz.name, type: biz.type, address: biz.address }
end
end
end
def self.reload
Ttt::Application.routes_reloader.reload!
end
end
# config/routes.rb
DynamicRouter.load
resources :businesses # still preserve old routes for 301 redirect in businesses#show
# app/controller/businesses_controller.rb
def show
@business = Business.cached_find(params[:id])
if request.path != @business.slug
redirect_to @business.slug, :status => :moved_permanently
else
...
end
end
end
# app/models/business.rb
after_save :reload_routes
def normalize_friendly_id text
# slug is changed here and works in conjunction with FriendlyID
# and keeps record of old routes (/business/xxx-xxx) for 301 redirects
"/#{self.type.parameterize}/sg/#{self.name.parameterize}-#{self.address.parameterize}"
end
def reload_routes
if slug.blank? || name_changed? || address_changed? || type_changed?
DynamicRouter.reload
end
end
# app/helper/business_helper and application_controller.rb
# to still be able to use business_path just like the defauilt url_helper,
# override it in the relevant files
def business_path business, options={}
path = ""
path = "#{business.slug}"
# this part can be and should be more robust
if options[:some_param]
path += "?some_param=#{options[:some_param]}"
end
path += "##{options[:anchor]}" if options[:anchor]
return path
end
I can now access the business#show pages via /businesses/xxx
or the new /type/name-address
routes, with the former being able to be 301 redirected to the latter.
Whenever a new business is created, the routes will reload via after_save callback and a new route to that new business will be created without having to reload the application.
If the business is updated again, and its route changed from /type1/name1-address1
to /type1/name2-address1
, the first route will become a deadlink. To deal with this I use high_voltage gem, and override the invalid_page
method to redirect this route back to businesses#show
# app/controller/pages_controller
class PagesController < ApplicationController
include HighVoltage::StaticPage
def invalid_page
slug = FriendlyId::Slug.find_by_slug("/" + params[:id])
if slug && slug.sluggable_type == "Business"
redirect_to slug.sluggable.slug, :status => :moved_permanently
else
raise ActionController::RoutingError, "No such page: #{params[:id]}"
end
end
end
end
Doing this allows me to achieve what I want but Im not sure if this is the way to go. For one, this method will create as many routes as number of rows in the business table. Would this adversely impact the performance or other aspects of the application if any?
Love to hear from everyone!