2015-05-17 3 views
2

Я использую объект формы на основе ActiveModel для обработки регистрации (регистрации) для приложения. Класс signup абстрагирует информацию для account и user (основной пользователь для учетной записи).Rails 4: Проверка связанных моделей ActiveRecord при использовании объекта формы

Однако, я нахожу, что я дублирую логику проверки для account и user внутри класса signup. Когда я писал свои спецификации (используя rspec), я понял, что это дублирование, вероятно, указывает на проблему с тем, как я обрабатываю это.

Есть ли способ пройти проверку в классе signup на модели account и user без дублирования? Таким образом, валидация остается в этих моделях, и я могу ссылаться/называть ее в классе signup.

Ниже signup класс у меня есть это работает, но, кажется, дублирования кода ...

class Signup 
    include ActiveModel::Model 

    # Scopes 
    #---------------------------------------------------------------------- 

    # NOOP 

    # Macros 
    #---------------------------------------------------------------------- 

    attr_accessor :slug, :email, :password, :password_confirmation 

    # Associations 
    #---------------------------------------------------------------------- 

    # NOOP 

    # Validations 
    #---------------------------------------------------------------------- 

    validate :verify_unique_email 
    validate :verify_unique_slug 
    validates :email, presence: true, format: { with: /@/, message: "is invalid" } 
    validates :password, presence: true, length: { minimum: 8 }, confirmation: true 
    validates :password_confirmation, presence: true 
    validates :slug, 
    presence: true, 
    format: { with: /\A[\w-]+\z/, message: "is invalid" }, 
    exclusion: { in: %w[signup signups login] } 


    # Methods 
    #---------------------------------------------------------------------- 

    def account 
    @account ||= Account.new 
    end 

    def user 
    @user ||= account.build_primary_user 
    end 

    def save 
    account.active = true 
    account.slug = slug 

    user.email = email 
    user.password = password 
    user.password_confirmation = password_confirmation 

    if valid? 
     ActiveRecord::Base.transaction do 
     account.save! 
     user.save! 
     end 

     true 
    else 
     false 
    end 
    end 

    def save! 
    save 
    end 

    private 

    def verify_unique_email 
    if User.exists?(email: email) 
     errors.add :email, "is invalid" 
    end 
    end 

    def verify_unique_slug 
    if Account.exists?(slug: slug) 
     errors.add :slug, "has already been taken" 
    end 
    end 
end 

Вот account модель, обратите внимание на дублирование в валидаций:

class Account < ActiveRecord::Base 
    has_one :primary_user, -> { where(primary: true) }, class_name: User 
    has_many :users, dependent: :destroy 

    validates :slug, 
    uniqueness: true, 
    presence: true, 
    format: { with: /\A[\w-]+\z/, message: "is invalid" }, 
    exclusion: { in: %w[signup signups login] } 
end 

ответ

1

Мне нравится, что вы делаете, используя form object. validates_associated :user, :account может помочь, но сообщения об ошибках могут быть нечетными. Скорее всего, я мог бы использовать mixins for common validations:

class Account < ActiveRecord::Base 
    module Validations 
    extend ActiveSupport::Concern 
    included do 
     validates :slug, presence: true 
    end 
    end 
    include Validations 
end 

class Signup 
    include ActiveModel::Model 
    include Account::Validations 
    extend Forwardable 
    def_delegators :account, :slug 
end 
+0

К сожалению, я склонен согласиться с тем, что включенный модуль может быть самым чистым DRY-решением для моей конкретной проблемы. Я надеялся, что есть способ сказать что-то эквивалентное: 'signup.errors = account.errors + user.errors', но, похоже, нет чистого способа реализовать это без возможности исправления Rails, которые у меня есть довольно строгая политика против. Я проверю ваше решение и приму его, если это сработает. –

+0

В моем конкретном случае я решил придерживаться дублирования валидаций, поскольку позже я могу иметь отдельные проверки в классе 'signup', но извлечение длинных повторных проверок в отдельный модуль является лучшим решением, которое я видел до сих пор. Я рассмотрел делегирование валидации связанным объектам, копирование фактического массива 'object.errors' и т. Д., И ни одно из них не является« чистым »решением. –

+0

Вы пробовали validates_associated, возможно, с помощью пользовательских сообщений? – aceofspades

0

Самый простой что будет работать:

def valid? 
    user.valid? && account.valid? && super 
end 

Затем вы можете избавиться от дублированных валидаций.

super необходим только в том случае, если вам необходимы дополнительные валидации, кроме тех, что указаны на User и Account. Это также добавит ошибки к объектам User и Account, что может быть более полезным, чем для класса Signup.

+0

в моем конкретном случае использования, я на самом деле хочу ошибки, чтобы показать на 'signup' сам объект, как это абстракция' 'account' и user' модели. –

Смежные вопросы