1

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?

UIS
  • 27
  • 5

2 Answers2

4

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?

max
  • 96,212
  • 14
  • 104
  • 165
  • As someone who came from WP I would encourage you to try to forget everything you think you know about web development and try to approach Rails with fresh eyes. WP and Drupal both have some pretty terrible design decisions (WP especially) and have very little in common with developing MVC applications the right way. – max May 14 '16 at 12:26
  • Actually I'm trying to build plugin functionality where each plugin could have it's own Controller and it should be able to contribute to a common navigation by providing it's set of links/routes. – UIS May 15 '16 at 11:31
  • by the way, the problem you referred to with Drupal and WP design, is it because you think they aren't highly scalable? – UIS May 15 '16 at 11:33
  • WP is a general all around disaster. Its basically built around global everything and is a hodgepodge of crap from the bottom up. But it gets the job done. Most of "scaling" problems with wordpress is that it has a crappy home made ORM and you have SQL queries being fired off from everywhere. Drupal wouldn't have to become totally incompatible every major version if there was good design to start with. – max May 15 '16 at 12:26
0

First you need to eager load all the controllers:

Rails.application.eager_load!

then, to get the list of controllers in your app:

controllers = ApplicationController.descendants

from here you can call their class methods

controllers[0].get_link

Note that the methods should be declared as class methods like this:

def self.get_link
end

This will probably slow down your application somewhat because it eager loads everything. And in general it doesn't seem to me like a good idea, since the controllers in your app are pretty much static. Consider using path helpers instead like contact_path for example. Check out Rails Routing Docs for details.

jetpackpony
  • 1,270
  • 1
  • 12
  • 22
  • Hmm, didn't knew you could get descendants this way. I always thought only child class should know what their parent class is :) I'll check this anyway, thanks! – UIS May 21 '16 at 17:01
  • Yep, this method is a part of Rails though. If you want same thing in vanilla Ruby, check out this answer: http://stackoverflow.com/questions/2393697/look-up-all-descendants-of-a-class-in-ruby Again, it is probably not the best idea to use it unless you absolutely have to :-) – jetpackpony May 29 '16 at 07:54