Что я хочу сделать, это определить список допустимых значений, а затем проверить его по этому списку значений при добавлении новой роли в связанную таблицу.
Позвольте мне привести конкретный пример:
Допустим, у меня есть таблица "Занятость" со следующими полями:
user_id (tied to a user table)
employer_id (tied to an employer table)
position_id (tied to a position table)
details
efbegdt
efenddt
Когда пользователь добавляет новую строку в эту таблицу, я хочу убедиться, что Employer_id и position_id уже существуют в других таблицах, и не разрешать сохранение, если это не так в любом случае.
Решения, которые я видел до сих пор, принимают форму:
class Employment < ActiveRecord::Base
EMPLOYERS = ['Google', 'Yahoo', 'Microsoft']
POSITIONS = ['Web Developer', 'Database Admin', 'QA']
validates_inclusion_of :employer_id, :in => EMPLOYERS
validates_inclusion_of :position_id, :in => POSITIONS
end
Но этот подход недостаточно гибок, чтобы вместить потенциально тысячи работодателей и должностей, и при этом он не обеспечивает простого способа позволить пользователям добавлять новые действительные записи, если их работодатель в настоящее время не существует.
Я также видел такой подход:
class Employment < ActiveRecord::Base
validate :employer_exists
protected
def employer_exists
ids = Employer.all.map(&:id)
if !employer_id.blank? && !ids.member?(employer_id)
errors.add(:employer_id, "invalid employer")
end
end
end
Это ближе к тому, что я хочу, но когда я проверяю это с помощью rspec, проверка правильности новой строки в таблице работодателей завершается неудачно:
Failure/Error: it { should be_valid }
expected valid? to return true, got false
Есть ли решение для этой проблемы?
UPDATE
Просто добавьте еще один пример со всеми подробностями настройки. В этом примере пользователи могут иметь несколько адресов электронной почты, хранящихся в таблице электронной почты, но существует ограничение в один адрес для каждого типа (личный, рабочий, учебный и т. Д.). Другая таблица, email_dfn, определяет все допустимые типы:
перенос файлов
class CreateEmailDfns < ActiveRecord::Migration
def change
create_table :email_dfns do |t|
t.string :short_description
t.string :long_description
t.timestamps
end
end
end
и
class CreateEmails < ActiveRecord::Migration
def change
create_table :emails do |t|
t.integer :user_id
t.integer :email_dfn_id
t.string :value
t.text :notes
t.timestamps
end
add_index :emails, [:user_id, :email_dfn_id]
end
end
Модель
class Email < ActiveRecord::Base
attr_accessible :value, :notes, :email_dfn_id
belongs_to :user
belongs_to :email_dfn
validates_associated :email_dfn
valid_email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :value, presence: true,
length: { maximum: 256 },
format: { with: valid_email_regex },
uniqueness: { case_sensitive: false }
validates :user_id, presence: true
validates :email_dfn_id, presence: true
end
и
class EmailDfn < ActiveRecord::Base
attr_accessible :short_description,
:long_description,
validates_uniqueness_of :short_description,
:long_description
has_many :emails
end
Тесты
require 'spec_helper'
describe Email do
let(:user) { FactoryGirl.create(:user) }
before { @email = user.emails.build(email_dfn_id: 1,
value: "personal_email@test.com",
notes: "My personal email address") }
subject { @email }
it { should respond_to(:value) }
it { should respond_to(:notes) }
it { should respond_to(:email_dfn_id) }
it { should respond_to(:user_id) }
it { should respond_to(:user) }
its(:user) { should == user }
it { should be_valid }
describe "when user id is not present" do
before { @email.user_id = nil }
it { should_not be_valid }
end
describe "when email id is invalid" do
before { @email.email_dfn_id = 999 }
it { should_not be_valid }
end
end
В этой текущей настройке последний тест (настройка email_dfn_id = 999, неверный код) завершается неудачей.