Formar objetos en rieles

El siguiente código de ejemplo es un ejemplo artificial de un intento de un objeto de formulario en el que probablemente sea excesivo utilizar un objeto de formulario. No obstante: muestra el problema que estoy teniendo:

Tengo dos modelos: unUser y unEmail:

# app/models/user.rb
class User < ApplicationRecord
  has_many :emails
end
# app/models/user.rb
class Email < ApplicationRecord
  belongs_to :user
end

Quiero crear unobjeto de forma que crea unuser grabar, y luego crea tres asociadosemail archivos.

Aquí están mis clases de objetos de formulario:

# app/forms/user_form.rb
class UserForm 
  include ActiveModel::Model

  attr_accessor :name, :email_forms

  validates :name, presence: true

  def save
    if valid?
      persist!
      true
    else
      false
    end
  end

  private

  def persist!
    puts "The Form is VALID!"
    puts "I would proceed to create all the necessary objects by hand"

    user = User.create(name: name)
    email_forms.each do |email|
      Email.create(user: user, email_text: email.email_text)
    end
  end
end
# app/forms/email_form.rb
class EmailForm 
  include ActiveModel::Model

  attr_accessor :email_text, :user_id

  validates :email_text, presence: true

  def save
    if valid?
      persist!
      true
    else
      false
    end
  end

  private

  def persist!
    puts "The Form is VALID!"
    # DON'T THINK I WOULD PERSIST DATA HERE
    # INSTEAD DO IT IN THE user_form
  end
end

Aviso: elvalidaciones en los objetos de formulario. UNAuser_form se considera no válido si esname el atributo está en blanco o si elemail_text el atributo se deja en blanco para cualquiera de losemail_form objetos dentro esemail_forms formación.

Por brevedad: solo voy a pasar por elnew ycreate acción de utilizar eluser_form:

# app/controllers/user_controller.rb
class UsersController < ApplicationController

  def new
    @user_form = UserForm.new
    @user_form.email_forms = [EmailForm.new, EmailForm.new, EmailForm.new]
  end

  def create
    @user_form = UserForm.new(user_form_params)

    if @user_form.save
      redirect_to users_path, notice: 'User was successfully created.' 
    else
      render :new
    end 
  end

  private

  def user_form_params
    params.require(:user_form).permit(:name, {email_forms: [:_destroy, :id, :email_text, :user_id]})
  end
end

Por último: la forma misma:

# app/views/users/new.html.erb
<h1>New User</h1>

<%= render 'form', user_form: @user_form %>
<%= link_to 'Back', users_path %>
# app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
  <% if user_form.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(user_form.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% user_form.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>

  # MESSY, but couldn't think of a better way to do this...
  <% unique_index = 0 %>
  <% user_form.email_forms.each do |email_form| %>
    <div class="field">
      <%= label_tag "user_form[email_forms][#{unique_index}][email_text]", "Email Text" %>
      <%= text_field_tag "user_form[email_forms][#{unique_index}][email_text]" %>
    </div>
    <% unique_index += 1 %>
  <% end %>


  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

El formulario representa:

Y aquí está el formulario html:

Voy a enviar el formulario. Aquí está el hash de params:

Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "email_forms"=>{"0"=>{"email_text"=>"test_email_1"}, "1"=>{"email_text"=>"test_email_2"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}

Lo que debería suceder es que el formulario debe volver a representarse y no persistir nada porque form_object no es válido: los tres correos electrónicos asociados NO deben estar en blanco. Sin embargo: form_object cree que es válido y explota en elpersist! método en elUserForm. Destaca elEmail.create(user: user, email_text: email.email_text) línea y dice:

método indefinido `email_text 'para [" 0 ", {" email_text "=>" test_email_1 "}]: matriz

Claramente, están sucediendo un par de cosas: las validaciones anidadas parecen no funcionar, y tengo problemas para reconstruir cada uno de los correos electrónicos del hash de params.

Recursos que ya he examinado:

Este artículo Parecía prometedor pero estaba teniendo problemas para que funcione.He intentado una implementación con la gema virtus y la gema reform-rails. Tengo preguntas pendientes publicadas para ambas implementaciones también:intento de virtus aquí y entoncesintento de reforma-rieles aquí.He intentado enchufarmeaccepts_nested_attributes, pero tenía problemas para descubrir cómo utilizar eso con un objeto de formulario, así como con un objeto de formulario anidado (como en este ejemplo de código). Parte del problema era quehas_many yaccepts_nested_attributes_for no parecen estar incluidos enActiveModel::Model.

¡Cualquier orientación sobre cómo hacer que este objeto de formulario haga lo que se espera sería muy apreciada! ¡Gracias!

Respuestas a la pregunta(1)

Su respuesta a la pregunta