Olivier El Mekki

Get rake routes to recognize constraint classes in rails

I’ve been playing recently with the new rails-3 route constraints behaviour. Quite nice, actually, I really enjoy being able to add total custom code to determine if a route match. Of course (?), my first use of that has been subdomain constraints. Here is an example :

  constraints( ClientSubdomain ) do
    resource :organization, :only => %w( show edit update destroy ) do
      get 'close', :on => :member
    end
 
    root :to => 'static_page#get_page', :page => 'home_client'
  end
 
  constraints( AdminSubdomain ) do
    scope :module => 'admin' do
      resources :organizations
    end
 
    root :to => 'admin/organizations#index'
  end

So here, if I’m on the admin subdomain, I can handle multiple organizations, my clients ones. And if I am on a client subdomain, the organization resource is a singleton (client must only handle its own organization, right?).
Ok, let’s see what rake routes outputs.

close_organization GET    /organization/close(.:format)     {:action=>"close", :controller=>"organizations"}
 edit_organization GET    /organization/edit(.:format)      {:action=>"edit", :controller=>"organizations"}
      organization GET    /organization(.:format)           {:action=>"show", :controller=>"organizations"}
                   PUT    /organization(.:format)           {:action=>"update", :controller=>"organizations"}
                   DELETE /organization(.:format)           {:action=>"destroy", :controller=>"organizations"}
              root        /(.:format)                       {:action=>"get_page", :controller=>"static_page"}
     organizations GET    /organizations(.:format)          {:action=>"index", :controller=>"admin/organizations"}
                   POST   /organizations(.:format)          {:action=>"create", :controller=>"admin/organizations"}
  new_organization GET    /organizations/new(.:format)      {:action=>"new", :controller=>"admin/organizations"}
                   GET    /organizations/:id/edit(.:format) {:action=>"edit", :controller=>"admin/organizations"}
                   GET    /organizations/:id(.:format)      {:action=>"show", :controller=>"admin/organizations"}
                   PUT    /organizations/:id(.:format)      {:action=>"update", :controller=>"admin/organizations"}
                   DELETE /organizations/:id(.:format)      {:action=>"destroy", :controller=>"admin/organizations"}
              root        /(.:format)                       {:action=>"index", :controller=>"admin/organizations"}

Erf, not really helpful.

After playing a while with mapper, routeset and routes, I came to a solution.

Monkey patch for Constraints

First, we need to expose Constraints#constraints to the world. Create a lib/route_constraints.rb:

module ActionDispatch
  module Routing
    class Mapper
      class Constraints
        attr_reader :constraints
      end
    end
  end
end

Be sure to require it in config/environment.rb.

Rake task

Then, put this content in lib/tasks/route_with_constraints.rake:
Gist

namespace :routes do
  desc 'Print out all defined routes in match order, with names, per constraint class. Target specific constraint class with CONSTRAINT=x. Target specific controller with CONTROLLER=x.'
  task :constrained => :environment do
    Rails.application.reload_routes!
 
    constraints_routes = Hash.new
 
    Rails.application.routes.routes.each do |route|
      group = (route.app.class == ActionDispatch::Routing::Mapper::Constraints ? route.app.constraints.to_s : 'No constraint class')
      constraints_routes[group] ||= []
      constraints_routes[group] < < route
    end
 
    requested_constraint = ENV['CONSTRAINT']
 
    constraints_routes.each do |group, all_routes|
      if requested_constraint.nil? or group == requested_constraint
        puts "\n\nConstraint class : #{group}\n\n"
 
        if ENV['CONTROLLER']
          all_routes = all_routes.select{ |route| route.defaults[:controller] == ENV['CONTROLLER'] }
        end
 
        routes = all_routes.collect do |route|
 
          reqs = route.requirements.dup
          reqs[:to] = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
          reqs = reqs.empty? ? "" : reqs.inspect
 
          {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
        end
 
        routes.reject! { |r| r[:path] =~ %r{/rails/info/properties} } # Skip the route if it's internal info route
 
        name_width = routes.map{ |r| r[:name].length }.max
        verb_width = routes.map{ |r| r[:verb].length }.max
        path_width = routes.map{ |r| r[:path].length }.max
 
        routes.each do |r|
          puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
        end
      end
    end
  end
end

Ok, now we have a rake routes:constrained task. Let see its output :

Constraint class : No constraint class

Constraint class : AdminSubdomain

   organizations GET    /organizations(.:format)          {:action=>"index", :controller=>"admin/organizations"}
                 POST   /organizations(.:format)          {:action=>"create", :controller=>"admin/organizations"}
new_organization GET    /organizations/new(.:format)      {:action=>"new", :controller=>"admin/organizations"}
                 GET    /organizations/:id/edit(.:format) {:action=>"edit", :controller=>"admin/organizations"}
                 GET    /organizations/:id(.:format)      {:action=>"show", :controller=>"admin/organizations"}
                 PUT    /organizations/:id(.:format)      {:action=>"update", :controller=>"admin/organizations"}
                 DELETE /organizations/:id(.:format)      {:action=>"destroy", :controller=>"admin/organizations"}
            root        /(.:format)                       {:action=>"index", :controller=>"admin/organizations"}

Constraint class : ClientSubdomain

close_organization GET    /organization/close(.:format) {:action=>"close", :controller=>"organizations"}
 edit_organization GET    /organization/edit(.:format)  {:action=>"edit", :controller=>"organizations"}
      organization GET    /organization(.:format)       {:action=>"show", :controller=>"organizations"}
                   PUT    /organization(.:format)       {:action=>"update", :controller=>"organizations"}
                   DELETE /organization(.:format)       {:action=>"destroy", :controller=>"organizations"}
              root        /(.:format)                   {:action=>"get_page", :controller=>"static_page"}

Way better :)

Une Réponse à “Get rake routes to recognize constraint classes in rails”

  1. Twitter Trackbacks for Get rake routes to recognize constraint classes in rails - Olivier El Mekki [olivier-elmekki.com] on Topsy.com dit:

    [...] Get rake routes to recognize constraint classes in rails - Olivier El Mekki blog.olivier-elmekki.com/2011/01/30/get-rake-routes-to-recognize-constraint-classes-in-rails/ – view page – cached Blog français de Olivier El Mekki Tags [...]

Laisser un commentaire