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:
http://jayfields.blogspot.com/2006/09/rails-model-view-controller-presenter.html
http://www.martinfowler.com/eaaDev/PresentationModel.html

- 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