DRYing error_messages_for

The RAILS generators add code to display error messages. For example:

<%= error_messages_for :model %>

may display:

There were problems with the following fields:

    * field1 must be 4 characters
    * field2 can't be blank

With the intention of keeping things DRY, is it possible to display
all error messages from a central location (e.g. /view/layouts/
application.html.erb)??

With the intention of keeping things DRY, is it possible to display

all error messages from a central location (e.g. /view/layouts/
application.html.erb)??

I don’t think this is really required and sounds like a case of over-engineering. This isn’t a jab at you, I fall victim to this myself from time-to-time.

Not all pages will have the need to show errors: list and show are simple examples. If you are looking to DRY up your views, take a look at a generator I released yesterday. This may help you out some:

http://blog.stonean.com/2008/01/generator-stage.html

Even if you don’t use it, it may give you some ideas.

Hope this helps,
andy

Andrew Stone said the following on 01/02/08 10:38 AM:

If you are looking to DRY up your views, take a look
at a generator I released yesterday. This may help you out some:

http://blog.stonean.com/2008/01/generator-stage.html

Even if you don't use it, it may give you some ideas.

I've been looking to DRY up views and make use of a common partial for
the view-edit-new.

The
     <% form_for(@record) do |f| %>
       <%= render :partial => "data", :locals => {:f => f} %>
       <p> <%= f.submit submit_label %> </p>
     <% end %>

is the key bit, the "_data.html.erb" being of the form

     <!--[form:record]-->
     <p><label for="record_bracket_only>Brackets only</label>
         <%= f.check_box 'brackets_only' %></p>
      ...
     <!--[eoform:record]-->

for umpteen fields of the record.

So I was interested when I saw your view that seemed to operate on
"|f|", and was then disappointed to find that it didn't, that every
field needed routine in the helper. I was disappointed because that
actually means there a lot of code, that its not DRY-by-design, that
all you've done is shifted the code into the helper.

I'm still trying to find something where I can define a layout once and
once only and use it for both show and edit.

It would be useful in this context if

    <% fields_for(@record) do |f| %>

worked outside of a form, but it doesn't.

So I was interested when I saw your view that seemed to operate on

“|f|”, and was then disappointed to find that it didn’t, that every
field needed routine in the helper. I was disappointed because that
actually means there a lot of code, that its not DRY-by-design, that

all you’ve done is shifted the code into the helper.

That’s interesting. I haven’t simply shifted code to the helper, at least that’s not my view. The duplication done in the show, edit and new is now combined into the data partial. This is not DRY how? I am curious.

I’m still trying to find something where I can define a layout once and

once only and use it for both show and edit.

It would be useful in this context if

<% fields_for(@record) do |f| %>

worked outside of a form, but it doesn’t.

It seems that you’re looking to boil it down to an list (index) and a data view. Is this correct? You could do this with some modifications. Create a data partial and in your controller, render the partial instead of the view for the action.

For example:

def new
render :partial => “data”, :locals => {:title => “Creating MyObject”}
end

…you get the idea. That’s one way to go about it and I have done this. It worked well for me.

Yes, it's possible in a number of ways.

The simplest may be to use a 'named' yield in your /view/layouts/
application.html.erb. You'd add something like this:

<div class="errors">
<%= yield :error_list %>
</div>

Then, in your views, you'd add use content_for to render the errors
into the div:

<% content_for :error_list do %>
<% error_messages_for :model %>
<% end %>

If you want more control then the key is to understand the magic
behind error_messages_for. All it's doing (assumption, I haven't read
the code...) is iterating through the errors hash on the model
object. That hash is keyed by the field name and has a string or
array of strings with the associated error messages. If you really
wanted to you could probably create some kind of @errors structure
(hash?) and then iterate through it if it's not empty in your target
error section.

HTH,
AndyV

Andrew Stone said the following on 01/02/08 11:23 AM:

That's interesting. I haven't simply shifted code to the helper, at
least that's not my view. The duplication done in the show, edit and
new is now combined into the data partial. This is not DRY how? I am
curious.

For every field in the record you have to have an explicit routine in
the helper. Yes, at a reductionist level you're not repeating
yourself. But look at the structure of those helper routines - and for
some records you're going to need a LOT of them - that are all the same
except for the field names.

    I'm still trying to find something where I can define a layout once and
    once only and use it for both show and edit.

    It would be useful in this context if

       <% fields_for(@record) do |f| %>

    worked outside of a form, but it doesn't.

It seems that you're looking to boil it down to an list (index) and a
data view. Is this correct?

No, its not.

What I'm trying to do is to use the same 'form' definition for both the
edit/new and the view, but with the minimum of code - make Rails do the
work.

You could do this with some
modifications. Create a data partial and in your controller, render the
partial instead of the view for the action.

For example:

def new
  render :partial => "data", :locals => {:title => "Creating MyObject"}
end

....you get the idea. That's one way to go about it and I have done
this. It worked well for me.

Obviously I'm not explaining this enough.

The 'new' has to have a 'form_for' in the partial "_data.html.erb"

         <% form_for(@record) do |f| %>

and the fields then

        f.first-name

and so on. With layout and labels and CSS tags.
Lets put that in a separate file, so that it is written 'once and once
only' and can be used by all of ['new', 'edit', 'show']. The 'index'
doesn't come into this.

Now, lets use that same file for 'show'.
Which means there has to be

   <% something_something_something_for(@record) do |f| %>

to match up the

        f.first-name
and all the rest of the f.whatever_other_fields_there_are in the
partial. You get round this by using an almost identical function for
each field. I see that as a maintenance problem. I don't see it as DRY.

What is the "something_something_something_for' ?

I've tried 'fields_for'. It generates a form, still.Look at the code
for 'field_for' and you'll see why its not suitable for 'show'.

Andrew Stone said the following on 01/02/08 11:23 AM:

That’s interesting. I haven’t simply shifted code to the helper, at
least that’s not my view. The duplication done in the show, edit and

new is now combined into the data partial. This is not DRY how? I am
curious.

For every field in the record you have to have an explicit routine in
the helper. Yes, at a reductionist level you’re not repeating

yourself. But look at the structure of those helper routines - and for
some records you’re going to need a LOT of them - that are all the same
except for the field names.

I do see your concern and I actually did think of this in the design. It just appears we have different goals in mind, or at minimum, a different approach (that’s obvious). :slight_smile:

I wanted to create a way to hand off my view to a designer and let them do their magic while I do mine. So that’s why I went to the “data” partial design. One file to control the view.

Now, what I noticed is that I was having to add additional code to test what to display for form mode or show mode. This littered the “data” partial with Ruby code. I didn’t like that. So I then thought of using the helper as “presenters” of the data.

To your point of repeated “helper routines”, yes there may be a lot depending on your form. I thought of doing something to generalize it, one method to present data regardless of the field, but the processing to do so seemed counter to the benefit. By having a method for each value, it’s clean and simple and easy for someone using this plugin to understand what they need to do to customize it.

I’ve tried ‘fields_for’. It generates a form, still.Look at the code

for ‘field_for’ and you’ll see why its not suitable for ‘show’.

I took a look and agree. It seems your approach is similar to why I didn’t go with something like HAML. I don’t want more code dictating my view. It’s cool and slick and all that, but I want to use HTML as much as possible. It has to be faster than adding another layer of interpretation before HTML is rendered.

Good luck in your search. It sounds like you have a good idea what you want. That is a better start than most people have.

-andy

Andrew Stone said the following on 01/02/08 02:04 PM:

On Feb 1, 2008 11:52 AM, Anton J Aylward <aja@si.on.ca

    Andrew Stone said the following on 01/02/08 11:23 AM:
    >
    > That's interesting. I haven't simply shifted code to the helper, at
    > least that's not my view. The duplication done in the show, edit and
    > new is now combined into the data partial. This is not DRY how? I am
    > curious.

    For every field in the record you have to have an explicit routine in
    the helper. Yes, at a reductionist level you're not repeating
    yourself. But look at the structure of those helper routines - and for
    some records you're going to need a LOT of them - that are all the same
    except for the field names.

I do see your concern and I actually did think of this in the design.
It just appears we have different goals in mind, or at minimum, a
different approach (that's obvious). :slight_smile:

I wanted to create a way to hand off my view to a designer and let them
do their magic while I do mine. So that's why I went to the "data"
partial design. One file to control the view.

LotR?
Indeed, and that's what I found interesting.
But it wasn't _quite_ it. Those helpers were the kicker.

Now, what I noticed is that I was having to add additional code to test
what to display for form mode or show mode. This littered the "data"
partial with Ruby code. I didn't like that. So I then thought of using
the helper as "presenters" of the data.

I know ahead of time I'm going to have to litter it with
    can_do(action, @current_user)
sometime in the future, so I too don't want it littered here and now.

To your point of repeated "helper routines", yes there may be a lot
depending on your form.

Oh, there will be! Not least of all because some forms are for more
than one record! What I'm striving for is consistency of the UI/layout
and pushing that back to the CSS. "Skinning". Yes, I could use
in-place editing with javascript, but I'd rather not for a variety of
reasons that have been discussed here in the past. Having the same
partial for new-edit-show means that I'll never have to worry about
re-editing the source in the development cycle to keep the
UI/look-and-feel in sync.

The real killer are the 'super-parent' records since they contain the
context and configuration information for their descendents. I've
thought of doing some sort of HABM but it always boils down to a STI and
trying to fight that is more code, more controllers...

On top of that, as the model changes I would have to update BOTH the
data and the helpers.

(I just know someone is going to ask why I don't use active_scaffold.
I tried and found it confusing and limiting.
)

I thought of doing something to generalize it,
one method to present data regardless of the field, but the processing
to do so seemed counter to the benefit. By having a method for each
value, it's clean and simple and easy for someone using this plugin to
understand what they need to do to customize it.

    I've tried 'fields_for'. It generates a form, still.Look at the code
    for 'field_for' and you'll see why its not suitable for 'show'.

I took a look and agree. It seems your approach is similar to why I
didn't go with something like HAML. I don't want more code dictating my
view. It's cool and slick and all that, but I want to use HTML as much
as possible. It has to be faster than adding another layer of
interpretation before HTML is rendered.

I very much agree. There are places for 'code generation' and
interpretation, and a lot of the Ruby DSL and widgets get it right, but
obsessing about it costs.

Where I differ from you is that I want the explicit form definition once
and once only. I think it can be done with partials without the helpers
you use. If anything, I want to reserve that all for the 'next layer'
which will be fine grained access control at the field level :slight_smile:

Good luck in your search. It sounds like you have a good idea what you
want. That is a better start than most people have.

Indeed. many of my peers complain I'm too concerned with 'requirements'
and 'getting it right the first time' :-/ However that 'good idea what
I want' can also be frustrating.

@Anton

Andrew has the right idea. new/edit can be combined into a common
data partial that can be rendered by both (and if you don't mind input
elements in your show you can combine that, too). The extra use of
helpers doesn't make sense to me, though.

The main place I'd differ is in what's passed to the partial. As
constructed it is passing the result of a fields_for call (there's a
'hidden' one in the form_for call). I'd recommend just passing the
object in question instead. For example:

_data.html.erb
<% fields_for :person, person do |f| %>
<b>First Name<b>
<%= f.text_field :first_name %><br/>
<b>Middle Name<b>
<%= f.text_field :middle_name %><br/>
<b>Last Name<b>
<%= f.text_field :last_name %><br/>
<% end %>

The following assume that you're setting @person in the controller...

new.html.erb
<% form_for :person, :url=>people_path do |f| %>
<%= render :partial => 'data', :locals=>{:person=>@person} %>
<% end %>

edit.html.erb
<% form_for :person, :url=>person_path(@person), :method=>:put do |f|
%>
<%= render :partial => 'data', :locals=>{:person=>@person} %>
<b>Special For Existing People</b>
<% f.check_box :special_person %>
<% end %>

The last field in edit is simply to suggest that you can mix in fields
that are available only in certain contexts. The broader point is
that you've written one file that captures all the common input
elements and no longer have to worry about keeping the common set in
sync among the templates that use them.

If you *really* want to let Ruby/Rails do the work, you can iterate
over the attributes of the ActiveRecord object so that you don't even
have to create the data partial. This is, in a sense, what the
generators do.

_data.html.erb
<% fields_for :person, person do |f| %>
  <% person.attributes.each do |attr, val| %>
    <b><%= attr.to_s.humanize %></b>
    <%= f.text_field attr if val.is_a?(String) %>
    <%= f.check_box attr if val.is_a?(Boolean) %>
  <% end %>
<% end %>

I would not recommend the latter since you're losing all control over
the rendering order, etc. but it's fun to play with...

HTH,
AndyV

AndyV said the following on 01/02/08 04:24 PM:

@Anton

Andrew has the right idea. new/edit can be combined into a common
data partial that can be rendered by both (and if you don't mind input
elements in your show you can combine that, too). The extra use of
helpers doesn't make sense to me, though.

Agreed. And they aren't DRY.

The main place I'd differ is in what's passed to the partial.

Agreed. See below.

As
constructed it is passing the result of a fields_for call (there's a
'hidden' one in the form_for call). I'd recommend just passing the
object in question instead.

Agreed.
You've just described what I had a result of running the 'scaffold_form'
generator.

I had the edit/new in one partial

_form.html.erb:
     <!--[form:person]-->
     <p><label for="person_name">Name</label>
     <%= f.text_field 'name' %></p>
     .....
     <!--[endofform:person]-->

and

edit.html.erb

     <% form_for(@person) do |f| %>
       <%= render :partial => 'form' , :locals=>{:f => f}%>
       <%= submit_tag 'Save' %>
     <% end %>

new.html.erb

     <% form_for(@person) do |f| %>
       <%= render :partial => 'form' , :locals=>{:f => f}%>
       <%= submit_tag "Add" %>
     <% end %>

But 'scaffold_form' didn't generate the details for the view.html.erb

I've tried 'fields_for':

     <% fields_for(@web) do |f| %>
       <% render :partial => 'form' , :locals=>{:f => f}%>
     <% end %>

Please review the source for 'field_for'.
It produces a FORM. That is the fields are editable, there just isn't a
"submit" button.

The following assume that you're setting @person in the controller...

new.html.erb
<% form_for :person, :url=>people_path do |f| %>
<%= render :partial => 'data', :locals=>{:person=>@person} %>
<% end %>

See above.

edit.html.erb
[...]

[...] The broader point is
that you've written one file that captures all the common input
elements and no longer have to worry about keeping the common set in
sync among the templates that use them.

Yes, I've understood and sought that from the beginning.

My point is trying to make that "one file that captures" all the fields,
regardless of whether they are being used in and input form or as a
display, in one file.

One file for layout for ALL of ['new', 'edit', 'show' ]

Please Andy, we're just going over and over the same stuff. I know that
all this works for ['edit', 'new']; I'm trying to find out how to make
it work for 'show' as well.

Andrew Stone had a way, but I didn't like it

I am confused because it seems like you could easily take Andrew
approach and make it dry.

Assuming the proper conventions you could easily turn:

module AuthorsHelper
def first_name_value(f = nil)
if f
   f.text_field :first_name
else
   h @author.first_name
end
end

def last_name_value(f = nil)
if f
   f.text_field :last_name
else
   h @author.last_name
end
end
end

into a dynamically generated system in a number of a ways. For instance
iterate through all attributes in a model and dynamically define these
methods. The following is pseudo-code-ish and is not valid

@model_class.attributes.each do |attr_name|
  define_method(attr_name) do |f|
    if f
      f.text_field attr_name.intern
    else
      h @model_class.send(attr_name)
  end
end

as a quick and sloppy example would define all the helpers for the model
object automatically effectively DRY'ing up the code (at the obvious
expense of customizability). There are other, more elegant ways of using
metaprogramming to achieve your goal.

If you can, if you cannot come to an existing solution, consider writing
your own plugin so the world can benefit. You can even take andrew's as
a starting point and move from there.

Hi Andrew.

Your statement about over-engineering is entirely fair. I think I'll
take another look at "what am I trying to achieve?" to see if that is
the case.

Thank you for your feedback.

BTW - I look forward to reviewing your source code when I have a free
moment.

@AndyV:

Thank you for your post. That is exactly what I was looking for. Now
it's just a matter of "Is this the right solution for my project?",
but I digress.

Cheers