Hi folks --
I've been a big fan of Erector for a long time. I now have a chance to
champion its use with a commercial, large-scale Rails app (among the
top handful of largest Rails sites worldwide). We're looking at
integrating it into our existing codebase, and I've run into a couple
interesting pieces of engineering work that I wanted to share.
First: controller instance variables. ERb passes them through to the
direct view and any partials, all the way down; it also silently
ignores any extras you set. You effectively have a pool of
"globals" (views can even modify them, ick!), and if your view set is
large enough it becomes very hard to maintain. ("This action sets
@foobar, but it looks like the view doesn't use it. I want to remove
it. But maybe some partial nested six levels down uses it...maybe only
certain conditions! Uh-oh. I better leave it in place, or else spend
painful hours with grep.")
So we really want to use 'needs' to avoid this. However, our app
infrastructure, over time, has come to set many controller instance
variables across all actions -- before_filters, ApplicationController
calls, modules included into ApplicationController, and so on. (I
suspect this is prototypical of large Rails apps that have evolved
with traditional ERb views.) In our app, an "empty" action sees
several dozen controller variables. As a result, 'needs' either is
counterproductive (list several dozen in every widget...and now
several dozen are *required* everywhere) or has to be omitted,
negating one of Erector's great strengths.
What I've done is added a class method:
Erector::Widget.ignore_extra_controller_assigns=. Set this to true,
and then iff you declare a 'needs' clause in that widget, any
controller instance variables that aren't in that clause are silently
ignored, rather than causing errors. (Crucially, they aren't available
to the widget, either.) It's false by default, and inherits; the idea
is that if you need it, you set it on a Widget subclass that you use
on all your app's widgets, and it just works.
This only affects Rails widgets, nothing else, and only comes into
play when Rails renders your widget (implicit render at the end of a
controller action, or "render :template => ..", etc.) -- instantiating
the widget directly will still fail if you pass unneeded variables, as
will Erector's 'widget' helper and anything else. You can see the
diffs here:
http://github.com/ageweke/erector/commit/dd7676fbe1da4260dc35cb9a76ee97c657aef058
If folks have a different/better idea, I'm certainly all ears. But I
wanted to share this to get discussion flowing, and, if this seems
like a good idea, offer the patch for merging back upstream. I feel
like it works well and solves an existing, significant problem for
legacy Rails apps in a minimally-intrusive way -- I'm a huge Erector
fan and want to see it be able to be used everywhere. :) Let me know
what you think, and I'll work to make this the best possible!
Cheers,
Andrew