Neil Middleton
Neil Middleton

Reputation: 22240

Rails Sub-controllers?

I'm pretty new to Rails and have an issue which I can't quite get my head around as to the architecturally 'correct' way of doing it.

Problem relates to what I kinda call sub-controllers. The scenario is this:

I have a series of pages, on which is a panel of some form containing some information (think the user panel on gitHub top right).

So, in my app, I have controllers that generate the data for the pages and render out the responses which is fine, but when it comes to this panel, it seems to me that you would want some sort of controller action dedicated to generating this panel and it's view.

Question is, how do you go about doing this? How do I render a 'sub controller' from within a view?

Upvotes: 1

Views: 2820

Answers (4)

Omar Qureshi
Omar Qureshi

Reputation: 9103

I dont think a module is what is required here, modules are required for shared behaviour across a small subset of your classes.

What I think is required here is the understanding of the inheritance of ApplicationController and also layouts

so, for example, my layout might look like:

<html>
<head><title>Foo</title></head>
<body>
  <%= render :partial => (current_user ? "/shared/user_widget_bar" : "/shared/login_bar") %>
  <%= yield %>
</body>
</html>

Any code that i want to use for it would go in my ApplicationController since it would be shared across the majority of my app:

 before_filter :generate_user_widget
 def generate_user_widget
   if current_user
     @avatar = ...
     @unread_messages = ...
   end
 end

I understand that it might be cleaner for it to belong in a separate controller BUT honestly, unless the code is huge, it doesn't matter and can even still be put inside a module which is then included by ActionController. However it does need to be inside ApplicationController if you consider the scope of it.

If there are more related pages, say for example, you have a Rails app that manages multiple sites and you want shared behaviour across a particular site, try creating a parent controller which has no actions and only private methods, any controllers that need to have access to those methods can inherit off it. That way you can apply before filters to all controllers which inherit off it, saving you the pain of forgetting to add one in your non-parent controllers.

e.g:

class SiteA::SiteAParentController < ApplicationController
   before_filter :generate_user_widget
   ...
end

class SiteA::ProductController < SiteA::SiteAParentController
  def index
    ...
  end
end

Upvotes: 2

insane.dreamer
insane.dreamer

Reputation: 2072

Like Herman said, if it's logic that you need generated after the controller hands off to the view (ie, the Pages controller generates a page view, but you want a customized panel) then put it in a helper. Or, call a separate method in your Pages controller before handing off to the view. Or, if it's a lot of logic, create a Module and stick it in your /lib folder. So you could have a whole Panel module with methods that generate different parts of your Panel and which are called by your controller. But if you want to call these methods from within the view, then you should use a helper instead.

Upvotes: 2

DanSingerman
DanSingerman

Reputation: 36532

I would put the logic in a helper or a module. (http://api.rubyonrails.org/classes/ActionController/Helpers/ClassMethods.html)

Then render partials where you want these things displayed. (http://api.rubyonrails.org/classes/ActionView/Partials.html)

Upvotes: 3

Maximiliano Guzman
Maximiliano Guzman

Reputation: 2035

well, if you really need to call a controller action from the view, you can use components. They were part of the framework, now they only exist as plugins. One such plugin that seems to be well maintained is here: http://github.com/cainlevy/components/tree/master

from its docs:

== Usage

Note that these examples are very simplistic and would be better implemented using Rails partials.

=== Generator

Running script/generator users details will create a UsersComponent with a "details" view. You might then flesh out the templates like this:

class UsersComponent < Components::Base
  def details(user_or_id)
    @user = user_or_id.is_a?(User) ? user_or_id : User.find(user_or_id)
    render
  end
end

=== From ActionController

class UsersController < ApplicationController
  def show
    return :text => component("users/detail", params[:id])
  end
end

=== From ActionView

<%= component "users/detail", @user %>

Upvotes: 1

Related Questions