Object Oriented Model Helpers

I heard in #caboose that you are interested in working on OO model helpers. I have actually been working on this for a few weeks. I am planning on releasing a plugin called SimplyBeautiful that does just that:

example:

def update    foo = Foo.new.helperize!    foo.update_attributes(params[:foo])    render :update do |page|      foo.replace_on(page)      foo.highlight_on(page)    end end

foo.form do |f| ...

foo.render (for partials)

... etc

The plugin (still rough) is attached

simply_beautiful.tgz (3.44 KB)

What we were talking about, was, the idea that models and views shouldn't really interact; rather, we could define an interface between the model and the view, similar to the way Liquid templates use a Drop to limit the fields to which you have access.

The idea being, you could turn around the helpers so you're doing

<%= @user.link %> rather than <%= link_to @user.name, user_path(@user) %> or whatever it is.

court3nay@gmail.com wrote:

What we were talking about, was, the idea that models and views shouldn't really interact; rather, we could define an interface between the model and the view, similar to the way Liquid templates use a Drop to limit the fields to which you have access.

The idea being, you could turn around the helpers so you're doing

<%= @user.link %> rather than <%= link_to @user.name, user_path(@user) %> or whatever it is.

Hmm, I missed the specifics of that conversation, but here are some thoughts:

I like the idea of turning the helpers "around" like you mentioned, but the example you gave seems to go a little too far, i.e. now we're putting view logic in the model, even if it's done with a mixin.

Perhaps a better (?) separation would be to leave link_to as a "view helper" and then #path just becomes a method on AR::Base (or wherever):

<%= link_to @user.name, @user.path %>

I guess the problem is that it's not *that* much less code, but feels ever so slightly cleaner (and DRY) to me, compared to:

<%= link_to @user.name, user_path(@user) %>

Then again I don't know what anyone has planned along these lines.

Tony

-1

Creative idea, but completely messes up the nice separation of concerns we have going on right now in Rails. I think it would detrimental in the long run.

Actually, the model in no way relies on the implementation of the
helper. The reason that you separate the model from the view is that
you don't want your models implementation depending on any view of
the model. In this case, the model is untouched, other than the
forwarding of methods to the helper. In fact, you could use the
library this way:

ActiveRecordHelper.new(the_record).render or ActiveRecordHelper.new (the_record).form ... etc

The helperize! method is just for convenience (so you don't have to
call the_helper.the_model) and is not required.

I'm a bit wary of this idea. My first reaction was, hey, keep the model clean, that stuff doesn't belong there, put it somewhere else. But then I had to realize that this was a reflex borne from experience with less dynamic languages than Ruby. In contrast to Java et al., in Ruby it is possible to attach presentation-specific methods to model objects without conflating concerns. Allen Holub campaigned[*] for UI-aware model objects in Java way back. Without success, and that's for the better, IMHO.

I've had a quick look at the simply beautiful plugin. My first complaint is that the name is as much presumptuous as it is vacuous. Lacking docs and tests, I was at a loss to get the gist of the code; "helper" and "helperize" aren't good names either. The code is non-trivial, in particular in the assumptions it makes on how things are going to be used.

So, I reserve judgment and won't jump from scepticism to condemnation. Nonetheless, I think the most important task for people sporting this approach is to present a convincing case of how it improves on the current state. How about a couple of examples that demonstrate the beauty of doing things your way?

Michael

[*] http://www.javaworld.com/javaworld/jw-07-1999/jw-07-toolbox.html

I kind of pushed it out early as I heard something called presenters
were being considered. It was not really ready for external use, but
I wanted to put it out there to join the impending conversation about
OO helpers. I extracted it from an a real world application that we
are developing. We are planning launch around April (still stealth
at this point).

When I get some time, I will do the canonical blog example to
showcase features of the plugin. The thing I am finding most useful
is partial rendering based on type. In general, the plugin helps to
align your application with CRUD even further.

The name was internal - just didn't come up with anything else before
I posted it.

I am certainly open to advice and criticism. I think we need to move
away from functional style helpers and toward a more OO solution.

Rich

This mindset is what kept me from dismissing the idea altogether. I completely agree that there has to be something more elegant than sticking together helper methods (functions!) in a barely cohesive lump.

Now, the question is whether the model objects are the right place to put helper methods (always assuming, of course, that separation of concerns is maintained). Presenters are another option. I think it largely depends on whether and where polymorphism is needed.

In the past, I have at times wished for a feature in Ruby/Rails for scoping mixins dynamically. Something like this

  <% with_mixin Person => BriefDisplayPersonMixin,        Address => BriefDisplayAddressMixin do -%>     <% models.each do |m| -%>       <%= m.display %>     <% end -%>   <% end -%>

What I'm envisioning here is that the mixed in features are only available during the execution of the block. It might even be useful to confine the mixin to only specific objects instead of the class itself. But that's all pretty baseless speculation on my part.

(Statically scoping mixins would be useful in its own right to keep independent extensions of core or rails classes from treading on each others feet.)

Michael

FWIW, I didn't say to put extra methods in the model. Hell no.

I was suggesting a layer between the model (or models) and the view which packages up certain methods, much like Liquid's Drops.

Thats essentially what the presenter pattern is, I believe. So I think you guys are talking about the same thing, its just a matter of implementation. I just ask to make it very easily testable w/i the core framework -- helper_test_case is still somewhat of a hack. I think each layer should be testable out of the box, and right now helpers are lacking in that respect.

related:

- rob

Thanks for the links. I think it would be fair to characterize the
objects created by my plugin as "Presenters". They generate data
(html markup) related to the presentation of the models without
explicit instructions regarding the layout (css).

I see that people are going to be hung up on the fact that the
forwarding code is on the model instead of on the presenter. I don't
think it would be terribly hard to reverse this, so that messages not
handled by the presenter are forwarded to the model. Perhaps that
would assuage some of the concerns raised here. There is always the
option to expose the model through an accessor as well (I would get
tired of chaining the messages but I'm lazy :/).

Rich

Thanks for the links. I think it would be fair to characterize the objects created by my plugin as "Presenters".

I don't think so. Presenters and Presentation Models correspond to a single view from which they extract as much "intelligence" as possible. Whereas your plugin adds very generic presentation functionality to model objects/classes. (Yes, I know that the methods are not technically added to the model, but forwarding amounts to the same.)

I see that people are going to be hung up on the fact that the forwarding code is on the model instead of on the presenter.

Not me. I think it is worthwhile to explore extending model objects with view-specific code, *if* at the same time the different concerns of presentation and business logic can be kept separate.

In the meantime I had a closer look at the code included with your original message. There are two layers: A lower layer consisting of Helpable, Helper, and supporting extensions to the functionality of ActionView and ActionController. Basically, this layer allows to add methods to each individual(!) model object assigned in a template. Above that, there is another layer that adds the same set of helper methods to all AR instances indiscriminately and another set of methods to arrays.

What sticks out is an imbalance between the flexibility provided by the lower layer and what little of it the higher layer uses. If indeed no more flexibility is needed, then it would be a lot simpler to mix-in the helper methods into ActiveRecord::Base and Array and be done with it. OTOH, if there is a need to potentially have different implementations of helper methods, then the existing flexibility could be put to use.

Michael

I don't think so. Presenters and Presentation Models correspond to a single view from which they extract as much "intelligence" as
possible.

I often use sub-classes of ActiveRecordHelper for specific model
classes.

Above that, there is another layer that adds the same set of helper methods to all AR instances indiscriminately and another set of
methods to arrays.

Not quite indiscriminately, only to instance variables exposed to the
views as well as locals exposed to partials.

If indeed no more flexibility is needed, then it would be a lot simpler to mix-in the helper methods into ActiveRecord::Base and Array and be done with it. OTOH, if there is a need to potentially have different implementations of helper methods, then the existing flexibility could be put to use.

I suppose I am more hung up on keeping the model clean than I need
be. I wanted to change AR Objects as little as possible to reduce
the possibility of conflicts (protected and private methods on
presenters). It would be simpler to just use a module that gets
mixed into the model's class, and would probably be safe in most cases.

I am interested to see how Liquid Drops work to see if that would be
a more appropriate alternative.

Rich

> I don't think so. Presenters and Presentation Models correspond to > a single view from which they extract as much "intelligence" as > possible.

I often use sub-classes of ActiveRecordHelper for specific model classes.

> Above that, there is another layer that adds the same set of helper > methods to all AR instances indiscriminately and another set of > methods > to arrays.

Not quite indiscriminately, only to instance variables exposed to the views as well as locals exposed to partials.

Yes. I was under the impression that you used the same ActiveRecordHelper class for all "helperized" model objects, that's what the "indiscriminately" was meant to refer to. With the more specific subclasses, my criticism is dispeled.

I suppose I am more hung up on keeping the model clean than I need be. I wanted to change AR Objects as little as possible to reduce the possibility of conflicts (protected and private methods on presenters). It would be simpler to just use a module that gets mixed into the model's class, and would probably be safe in most cases.

You're using delegation to recreate what singleton classes do already. I haven't measured it, but I imagine that it is pretty expensive to add a singleton class to each "helperized" model object instance, include Forwardable and configure delegation. You could get the same effect, by dispensing with delegation and directly including an ActiveRecordHelper *module* in the singleton class. This still uses singleton classes that are specific to individual model instances. I'm wondering whether that's really necessary. Wouldn't it be sufficient to have helper mixins per class?

Anyway, the above are all implementation considerations. I'd really like to see what improvements to view code can be achieved with OO helpers.

Michael