Search code examples
ruby-on-railspluginshookmetaprogrammingactivesupport

Rails, hooking into controllers


OK, here's my problem:

I have a HomeController with an action, say index, that would render a landing page for my site. The site also has a bunch of other Controllers that implement actions for various other functionalities.

I want a dynamic navigation bar on my home page and what I'm looking for is that HomeController#index goes to every other Controller in my app and calls a particular method that would return and object with link details, for example, HomeController#index would go to ContactController and call a method get_link which would return something like:

{label: "Contact", url: "/contact"}

Similarly HomeController#index would try calling get_link on every other controller until it has collected an Array of link objects which could then be passed to the View to render navigation.

I tried doing this through metaprogramming in Modules and using included hook method (getting my references from a Sitepoint tutorial). But that didn't work. Then I read somewhere that I may have to use ActiveSupport::Concern for ths kind of functionality, but I'm not sure how to go about it. I've worked on PHP CMS like Drupal and WordPress any they implement this using hooks, which is more-or-less what I'm looking for here. Any ideas?


Solution

  • In Rails flavor MVC controllers are not the dumping ground for a bunch of miscellaneous junk. In fact the only public methods of a controller are the the actions which correspond to a HTTP request:

    class ThingsController
    
      before_action :set_thing, except: [:new, :index]
    
      # THIS IS THE "PUBLIC API" OF THE CONTROLLER:
    
      # GET /things
      def index
        @things = Thing.all
      end
    
      # GET /things/:id
      def show
      end
    
      # EVERYTHING ELSE IS PRIVATE!
      private
    
        def set_thing
          @thing = Thing.find(params[:id])
        end
    end
    

    In fact controllers should only be instantiated by the Rails router or your controller tests. While you can create class methods and call them on your controllers this is also a huge anti-pattern.

    According to the single responsibility pattern the controllers job is to just to respond to HTTP requests when called by the routing layer.

    So do you solve such a case:

    • view partials
    • helper methods
    • initializers / config

    How exactly to solve the issue depends on the use case - is the content static or are you building something CMS like?