Rails’ method of sharing data between controllers and views via instance variables bothers many developers. For the uninitiated, a Rails controller method like this one…
class EventsController < ApplicationController def index @events = Event.scoped end end
… makes the
@events instance variable accessible to the rendered view.
This bugs me, too. My beefs with it are:
- It is counterintuitive. Why would an instance variable be how you make something available outside the instance?
- It couples the view and the controller more than necessary, shedding Ruby’s encapsulation strength of having method calls and ivar accessors identical.
- Views and partials have varying access to the instance variable, which is confusing.
Better would be explicitly exposing the events collection to views via a reader and helper method, like this:
class EventsController < ApplicationController def index @events = Event.scoped end attr_reader :events helper_method :events end
Now all views can just call the
events method and not worry about the ivar.
This becomes cumbersome when you do it across many controllers and actions, so I use this handy method to “automate” the process to expose certain ivars to views:
class ApplicationController < ActionController::Base # short hand for exposing a list of ivars to view objects def self.exposes(*ivars) ivars.each do |ivar| attr_reader ivar.to_sym helper_method ivar.to_sym end end end
The method takes an arbitrary list of ivar names. Using it, the example controller class looks like this:
class EventsController < ApplicationController exposes :events def index @events = Event.scoped end end
Unfortunately, the view can still access
@events, but it’s a step in the right direction.
I’m sure there are ways to stop direct access of instance variables from views, but I haven’t gotten that drastic yet.
I’ve gotten a lot of mileage out of this method. Perhaps you will too.