Opensteam_blog_logo4
opensteam.net | rss | search | archive
Results (escape to close):

Namespaced Controller - get all sub-controller

Posted by michael.schaerfer on 19-Nov-08 at 16:52

Let's say we have a pretty big rails application with lots of namespaced controller like:

   1  class AdminController < ApplicationController ; end
   2  class Admin::UsersController < AdminController ; end
   3  class Admin::PostsController < AdminController ; end

...and so on.

Now we want to build a method to get all subcontroller of a given controller, so that we can say:

   1  AdminController.subcontroller
   2  # => [Admin::UsersController, Admin::PostsController]

For every namespaced controller, rails builts a module with the controller-name (without "Controller") for the sub-controller.

So, we add a class method to AdminController:

   1  class << self ;
   2    def subcontroller
   3      self.to_s =~/^(.+)Controller$/
   4      return [] unless $1
   5      mod = $1
   6      smod = $1.demodulize
   7      if( pmod = self.parent ).const_defined?(:"#{smod}")
   8        return ( mod = mod.constantize ).constants.reject { |r| 
   9          !( mod.const_get( r ) < ActionController::Base )
  10        }
  11      end
  12      return []
  13    end
  14  end

Looks complicated, so lets get through this:
First we get the module name of the current controller( "AdminController" => "Admin" ).
Then we check if the constant ("Admin") is defined and, if it is, we return all constants/classes of this module that inherit from ActionController::Base, thus being a controller class.
If the module is not defined, we return an empty array, no subcontroller.

(All this "const_get(..)" stuff is used for more nested controllers, like "Admin::System::Config::PostsController".)

Now all the ruby-geeks out there are screaming "way too complicated! why not use this:"

   1  Object.subclasses_of( AdminController )
   2  # => [Admin::UsersController, Admin::PostsController]

..and this works too. But keep in mind that the Object#subclasses_of method cycles through the whole ObjectSpace, trying to find an inherited class.

A little benchmark test on script/console:

   1  >> n = 5000
   2  => 5000
   3  >> Benchmark.bm do |x|
   4  ?> x.report { n.times do ; AdminController.subcontroller ; end }
   5  >> x.report { n.times do ; Object.subclasses_of( AdminController ) ; end }
   6  >> end
   7        user     system      total        real
   8    0.310000   0.000000   0.310000 (  0.312271)
   9   61.190000   0.280000  61.470000 ( 62.760276)
  10  => true

.. so the subcontroller method, using Module#constants, is a little bit faster..

Comments

There are 0 comments on this post. Post yours →

Post a comment

Required fields in bold.