So check this: you have a nice little Rails app. You have some stuff for a JSON API type system. You've got an STI setup with the Widget model class and the more concrete FancyWidget and WeirdWidget classes. You've got a WidgetsController, and the corresponding FancyWidgetsController and WeirdWidgetsController
Yay. It works. You can deal with both types of Widgets using JSON.
Now you need an HTML interface to it.
Fine, you can just throw in a format.html in the emit method. Oh wait that won't work. The JSON just returns the same thing all the time, since it has that valid method on there, so if a save fails, valid will be false, and then whatever deals with the JSON can handle when valid is false.
So what to do? Well, you could duplicate the code in emit to all the
methods in the
WidgetsController
and it would probably work out. Then you just do some fun things in the
views
to generate the right paths (fancy_widget_path
instead of
widget_path
).
But that's no fun. Then you have all this duplicate code, and it's still a pain to override methods in the Fancy and Weird Widget controllers since you can't really call super, since there is that format.html and render call and they don't exactly replace each other. Pain. And Suffering
Who cares, because we have blocks. Tasty ruby blocks that when mixed with anything result in a tasty meal.
So I threw together respond_glue
A little plugin to fix this problem, with minimal crap.
In its simplest form, it's this
This gets included into ActionController::Base, which lets you do stuff like this:
The respond_glue
lines setup blocks to be used when calling
format.html (or js, xml, whatever. In the example, format.html). The
glue_for
line sets up the glue for the index action. This must be
called after the methods passed in have been defined (so if you want
glue_for(:index,:show,:new)
then you must call it after defining the
index, show and new actions. The superglue_for
call sets up the other
actions so they run the default superclass action, and actually render.
So what happens when the app hits the index action on the FancyWidgetsController? It's pretty straightforward:
-
FancyWidgetsController#index
gets called - …which is actually defined by the
glue_for
line - …so the newly defined method calls the original method
- …and it calls super
- …which calls
WidgetsController#index
- …which sets up some format handlers
- …which returns and continues in
FancyWidgetsController#index
- …which replaces the html handler originally defined in
WidgetsController#index
- …which returns, and then the new method (defined by the
glue_for
) calls therespond_to
block
So. You get nice inheritance where you can reuse code in the parent
class, and still override the respond_to
stuff.
Opinions? Ideas? Did I just do a bunch of work to solve a problem that is better solved another way?