Wie überprüfe ich die Eindeutigkeit eines ActiveModel-Objekts?

In Bryan Helmkamps ausgezeichnetem Blogpost mit dem Titel "7 Muster zur Refaktorisierung von Fat ActiveRecord-Modellen", erwähnt er mitForm Objects um mehrschichtige Formulare zu abstrahieren und die Verwendung zu beendenaccepts_nested_attributes_for.

Bearbeiten: sieheunten für eine Lösung.

Ich habe sein Codebeispiel fast genau dupliziert, da ich das gleiche Problem zu lösen hatte:

class Signup
  include Virtus

  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  attr_reader :user
  attr_reader :account

  attribute :name, String
  attribute :account_name, String
  attribute :email, String

  validates :email, presence: true
  validates :account_name,
    uniqueness: { case_sensitive: false },
    length: 3..40,
    format: { with: /^([a-z0-9\-]+)$/i }

  # Forms are never themselves persisted
  def persisted?
    false
  end

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

private

  def persist!
    @account = Account.create!(name: account_name)
    @user = @account.users.create!(name: name, email: email)
  end
end

Eines der Dinge, die sich in meinem Code unterscheiden, ist, dass ich das validieren mussEinzigartigkeit des Kontonamens (und der Benutzer-E-Mail). Jedoch,ActiveModel::Validations hat keineuniqueness validator, da es sich um eine nicht datenbankgestützte Variante von handeln sollActiveRecord.

Ich dachte mir, dass es drei Möglichkeiten gibt, damit umzugehen:

Schreiben Sie meine eigene Methode, um dies zu überprüfen (fühlt sich überflüssig an)Include ActiveRecord :: Validations :: UniquenessValidator (habe es versucht, habe es nicht zum Laufen gebracht)Oder fügen Sie die Einschränkung in die Datenspeicherebene ein

Ich würde lieber den letzten verwenden. Aber dann wundere ich mich immer wiederWie Ich würde das umsetzen.

Ich könnte sowas machen(Metaprogrammierung, ich müsste einige andere Bereiche ändern):

  def persist!
    @account = Account.create!(name: account_name)
    @user = @account.users.create!(name: name, email: email)
  rescue ActiveRecord::RecordNotUnique
    errors.add(:name, "not unique" )
    false
  end

Aber jetzt habe ich zwei Schecks in meiner Klasse, die ich zuerst benutzevalid? und dann benutze ich arescue Anweisung für die Datenspeicherungsbeschränkungen.

Kennt jemand einen guten Weg, um mit diesem Problem umzugehen? Wäre es besser, vielleicht meinen eigenen Validator dafür zu schreiben (aber dann hätte ich zwei Abfragen an die Datenbank, wo im Idealfall eine genug wäre).

Antworten auf die Frage(2)

Ihre Antwort auf die Frage