views:

487

answers:

3

If you have a layout that has in a menu which gets its menu items from a database. Where is the recommended place in a Rails application to place that call and assigns it to the instance variable that the layout uses?

1. @menuitems # exists in application.html.erb
2. @menuitems = MenuItem.find(:all) # code exists somewhere (where should this exist?)

@womble - Yes a before_filter would be helpful, but I would have to include it in all controllers using this layout, or is this something I can put in the application_controller.rb, would the child controllers and accompanying views be able to see that instance variable?

A: 

Martin Fowler has described direct calls to active record function within template code one of the weaknesses of Rails' MVC structure. For best practices, you should create a member function within a model class and call that function from your template (including the layout template).

  • The function you want should defined somewhere in app/models
Joe Soul-bringer
A: 

That sort of call should exist in the Controller layer of the system. Since it's something that looks like it needs to happen globally, I'd probably put it in a before_filter, and hate myself for doing it. Getting data that's needed for a layout is a bit fugly in Rails, no matter which way you do it.

womble
+6  A: 

Yes, you can define and set this as a before_filter in your application controller. All your controllers will call that before filter. It's not all that uncommon to set global instance variables this way.

An alternative is to use a memoization method in application controller and set is as a helper method, in a similar way to the popular current_user methods. The instance variable would then be nicely wrapped by your memoization function. For example:

def menu_items
  @menu_items ||= MenuItem.all
end
helper_method :menu_items

Now just call menu_items from your layout instead of using @menu_items directly.


I suppose its worth mentioning that putting this in application controller is somewhat unnecessary. If you're only going to use this in your view / layout, you could put it directly in a helper module, such as LayoutHelper. You will need to use helper :all (or at least helper :layout) in your application controller.

This is somewhat of personal preference - you may or may not like having ActiveRecord calls in your helpers. But, if there's a time to do so, this would be it.

wuputah