2013-03-11 3 views
1

이것은 견과류입니다. 내가 초기화와 같은 수준의 아이들의 유효성을 검사 죽은 간단한 콜백 함수가 : 지금까지레일 ActiveRecord 콜백 엉망

a = A.new 
>>[#<A>] 
a.bs 
>> [#<T>] 
a.save 
>> true 
a.id 
>> 15 

을 그것은 모두가 좋은 것, 그러나이다 :

class A < ActiveRecord::Base 
    has_many :bs 
    after_initialize :add_t_instance 
    validate :has_only_one_t 

    protected 

    def add_t_instance 
     bs << B.new(:a => self, :type => "T") unless bs.map(&:type).count("T") > 0 
    end 

    def has_only_one_t 
    unless bs.map(&:type).count("T") < 2 
     errors.add(:bs, 'has too many Ts") 
    end 
    end 

end 

지금을, 여기 런타임에 마법을 제공 :

s = A.find(15) 
s.bs 
>>[#<T>,#<T>] 
s.bs.count 
>> 2 
s.valid? 
>> false 
s.errors.full_messages 
>> "Too many Ts" 

여기에 실종 된 것은 무엇입니까?!?! 세계에서 두 번째 #T를 추가 할 수있는 것은 무엇입니까?

+0

이 작업을 다시 시도했지만 하위 클래스 'T'가 정의되지 않았기 때문에 'A'개체를 다시로드하려고하면 오류가 발생합니다. 레일스는 컬럼 이름 'type'때문에 규칙에 따라 서브 클래스를 감지하려고합니다. 'T'에 대한 클래스 정의가 있습니까? – PinnyM

+0

예. 아무 문제 없어. – muichkine

답변

1

새로운 인스턴스를 만든 후에뿐만 아니라 데이터베이스에서 기존 레코드를로드 한 후에 활성 레코드 개체가 인스턴스화 될 때마다 혼란스럽게도 (적어도 나에게) after_initialize이 호출됩니다. 따라서 A.find(15)을 실행할 때 두 번째 B를 만듭니다.

콜백의 새 레코드를 처리하고 있는지 확인하여 문제를 해결할 수 있습니다 (예 :

def add_t_instance 
    if new_record? 
    bs << B.new(:a => self, :type => "T") unless bs.map(&:type).count("T") > 0 
    end 
end 

하거나 before_initialize 선언 자체에 조건을 배치, 또는 아마도 before_create 콜백을 사용하여 시도 할 수 있습니다.

+0

bs.map (& : type) .count ("T")> 0이 두 번째 B가 인스턴스화되지 않는 이유는 무엇입니까? – PinnyM

+0

네, 왜 그런 질문입니까? – muichkine

+0

Btw, new_record에서 테스트를 원하십니까? 문제를 해결합니다. 나는 무슨 일이 일어나고 있는지 이해하지 못한다. – muichkine