Подтвердить что ты не робот

Отличный способ проверки и тестирования Rails 3 ассоциаций (с использованием RSpec/Remarkable)?

Я все еще довольно новичок в тестировании в Rails 3, и я использую RSpec и Remarkable. Я прочитал много сообщений и некоторых книг уже, но я все еще немного застрял в неопределенности, когда использовать имя ассоциации, когда его ID.

class Project < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project
end

Из-за хорошей практики я хочу защитить свои атрибуты от массовых назначений:

class Task < ActiveRecord::Base
  attr_accessible :project  # Or is it :project_id??

  belongs_to :project
end

Прежде всего, я хочу убедиться, что существует проект никогда без действительной задачи:

class Task < ActiveRecord::Base
  validates :project, :presence => true      # Which one is the...
  validates :project_id, :presence => true   # ...right way to go??
end

Я также хочу убедиться, что присвоенный идентификатор проекта или проекта всегда действителен:

class Task < ActiveRecord::Base
  validates :project, :associated => true     # Again, which one is...
  validates :project_id, :associated => true  # ...the right way to go?
end

... и мне нужна проверка: наличие, когда я использую: связанный

Большое спасибо за разъяснение, кажется, что после нескольких часов чтения и попытки проверить материал с помощью RSpec/Shoulda/Remarkable я больше не вижу лес из-за всех деревьев...

4b9b3361

Ответ 1

Кажется, это правильный способ:

attr_accessible :project_id

Вам тоже не нужно ставить :project! В любом случае, можно сделать task.project=(Project.first!)

Затем проверьте существование :project_id, используя следующее (:project_id также устанавливается, когда используется task.project=(...)):

validates :project_id, :presence => true

Теперь убедитесь, что соответствующий проект действителен следующим образом:

validates :project, :associated => true

Итак:

t = Task.new
t.project_id = 1 # Value is accepted, regardless whether there is a Project with ID 1
t.project = Project.first # Any existing valid project is accepted
t.project = Project.new(:name => 'valid value') # A new valid project is accepted
t.project = Project.new(:name => 'invalid value') # A new invalid (or an existing invalid) project is NOT accepted!

Очень жаль, что при назначении идентификатора через t.project_id = он не проверял, действительно ли этот конкретный идентификатор существует. Вы должны проверить это с помощью специальной проверки или с помощью Проверяет наличие GEM.

Чтобы проверить эти ассоциации с помощью RSpec с замечательными совпадениями, сделайте что-то вроде:

describe Task do
  it { should validate_presence_of :project_id }
  it { should validate_associated :project }
end

Ответ 2

validates :project, :associated => true  
validates :project_id, :presence => true

Если вы хотите быть уверенным, что существует ассоциация, вам нужно проверьте, присутствует ли внешний ключ, используемый для сопоставления ассоциации, а не связанный с ним объект. http://guides.rubyonrails.org/active_record_validations_callbacks.html

attr_accessible :project_id

Ответ 3

EDIT: предполагается, что ассоциация не является необязательной...

Единственный способ, с помощью которого я могу его полностью проверить, заключается в следующем:

validates_associated :project
validates_presence_of :project_id, 
    :unless => Proc.new {|o| o.project.try(:new_record?)}
validates_presence_of :project, :if => Proc.new {|o| o.project_id}

Первая строка проверяет, является ли соответствующий проект действительным, если он есть. Вторая строка настаивает на наличии project_id, если только соответствующий проект не существует и не является новым (если это новая запись, у него еще не будет ID). Третья строка гарантирует, что проект присутствует, если присутствует идентификатор, т.е. Если связанный проект уже сохранен.

ActiveRecord назначит project_id задаче, если назначить сохраненный проект на project. Если вы присвойте несохраненный/новый проект project, он оставит project_id пустым. Таким образом, мы хотим убедиться, что project_id присутствует, но только при работе с сохраненным проектом; это выполняется в первой строке выше.

И наоборот, если вы присвоите номер project_id, который представляет реальный проект, ActiveRecord заполнит project соответствующим объектом Project. Но если присвоенный вами идентификатор является фиктивным, он оставит project равным нулю. Таким образом, строка три выше, что гарантирует, что мы имеем project, если project_id заполняется - если вы укажете фиктивный идентификатор, это не удастся.

См. примеры RSpec, которые проверяют эти проверки: https://gist.github.com/kianw/5085085

Ответ 4

Решение Joshua Muheim работает, но я ненавижу, что не могу просто связать проект с задачей с таким идентификатором:

t = Task.new
t.project_id = 123 # Won't verify if it valid or not.

Итак, я придумал это:

class Task < ActiveRecord:Base

    belongs_to :project

    validates :project_id, :presence => true
    validate :project_exists

    private
    def project_exists
        # Validation will pass if the project exists
        valid = Project.exists?(self.project_id)
        self.errors.add(:project, "doesn't exist.") unless valid
    end

end