Patches for nested attributes and autosave

There are some small issues with the current nested attributes and
autosave implementation that I would like to address until Rails 3.

1) The first one was already dissused on the mailing list, which is
the use of "_delete" in accepts_nested_attributes_for when both the
DSL (:allow_destroy) and the behavior are destroy. I've created a
patch that just checks for _destroy instead of _delete with backwards
compatibility.

https://rails.lighthouseapp.com/projects/8994/tickets/2889

This is to avoid confusion and open room for more improvements in the
future.

2) How messages are created with nested attributes are hard to
translate and does not work for other languages. For instance:

  model_name + " " + attribute_name + " " + message

Is not grammatically correct in portuguese and japanese. There is a
ticket about it on Lighthouse:

https://rails.lighthouseapp.com/projects/8994/tickets/2210

Although the ticket does not solve the whole problem. It only deals
with attributes at the beginning of the message, but not with the
errors messages. For instance, when you have a project which has many
tasks, it will search for messages at:

+ activerecord.errors.messages.project.tasks.name

And it does not fallback to:

+ activerecord.errors.messages.task.name

So if I customize the error message, I will have to do that in two
places.

3) Another problem that has bitten me frequently when using autosave
is that I don't want error messages to be copied to the parent when
I'm giving both parent and child to error_messages_for. For that, I
usually need to create an after_validate callback that delete the
messages in the parent:

  def after_validate
    self.errors.each do |key, value|
      next unless key.to_s =~ /child_/
      self.errors.instance_variable_get('@errors').delete(key)
    end
  end

Right now, we deal with parent-child errors in two ways:

  + We copy the child errors to the parent (default for autosave);
  + We add an error to the parent saying that the child is invalid
(default for validate => true);

I would like to add a third one:

  + Do nothing with the error messages;

The patch would be simple, the tricky is how the DSL is going to be. A
suggestion (which definitely needs improvement):

  has_many :tasks, :validate => :copy_errors # copy all messages
to the parent
  has_many :account, :validate => true # add one message
that the parent is invalid
  has_one :account, :validate => :skip_errors # validate but skip
the error messages
  has_one :account, :validate => false # does not
validate

I already have a patch for (1), working on another patch for (2) and
would like to have some discussion on the DSL for (3) before working
on it.

What are your thoughts?

I discussed the problem with Eloy and we reached a solution that would
simplify both #2 and #3 cases.

The main problem is that ActiveRecord is copying the messages from the
child to the parent on autosave, while it shouldn't.
error_messages_for should be the one responsible with showing those
messages properly:

  error_messages_for :project, :association => :tasks

It would show errors in the project and in the associated tasks
objects. The I18n part could be dealt as:

  activerecord:
    errors:
      format:
        association: "{{association}} {{attribute}} {{message}}"

Which defaults to:

  Tasks name can't be blank

It's simpler and faster since it does not rely on a fallback mechanism
for I18n.

It's simpler and faster since it does not rely on a fallback mechanism
for I18n.

Sounds good to me, will eloy merge that into his branch?

Koz,

The second step was talking with Sven Fuchs from I18n.
The string concatenation "{{association}} {{attribute}} {{message}}"
is a no-go in I18n and they are trying to eliminate it.

A solution that is still valid would be to nest messages on
error_messages_for. Let's suppose that projects has many tasks, and
project has invalid title when task has an invalid name. When invoked
as:

  error_messages_for :projects, :association => :tasks

It would print something as:

  + Title can't be blank
  + Tasks
     + Name can't be blank

It's the same solution as above, just how we will show the error
messages has changed (which can be easily customized by overwriting
it).

It still sounds good to you? :slight_smile:

Yup, once José is done with it I will merge it in my branch so you can
pull it in before 2.3.4.

Eloy

@koz and @eloy, here is the patch:

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2904-avoid-copying-errors-from-child-to-parent-on-autosave