2016-03-04 3 views
1

У меня есть модель купона, которая может иметь несколько валют с одинаковым кодом купона.Как сделать toa-matchers использовать определенный набор значений для поля?

Так я проверить это так:

validates :code, presence: true, uniqueness: { case_sensitive: false, scope: :currency }, length: { in: 1..40 } 

и проверить его так:

it { should validate_uniqueness_of(:code).case_insensitive.scoped_to(:currency) } 

Но также купон переписывает атрибут валюты принимать как символ валюты ($), и это код ISO (ДОЛЛАР США). Он переписывает символ в соответствующий код ISO:

# Convert currency symbols to ISO code 
def currency=(value) 
    return write_attribute(:currency, nil) if value.blank? 
    write_attribute(:currency, currency_to_iso(value.strip)) 
end 

def currency_to_iso(symbol_or_iso) 
    return unless symbol_or_iso 
    (Money::Currency.find(symbol_or_iso) || Money::Currency.find_by_symbol(symbol_or_iso)).try(:iso_code) 
end 

Когда валюта код неверен, он преобразуется в nil.

Так Shoulda-matchers выдает ошибку:

Coupon did not properly validate that :code is case-insensitively unique 
    within the scope of :currency. 
    After taking the given Coupon, whose :code is ‹"t8u"›, and saving it 
    as the existing record, then making a new Coupon and setting its :code 
    to ‹"t8u"› as well and its :currency to a different value, ‹"dummy 
    value"› (read back as ‹nil›), the matcher expected the new Coupon to 
    be valid, but it was invalid instead, producing these validation 
    errors: 

    * code: ["has already been taken"] 

Как сделать Shoulda-спичка использовать только действительные коды валют, как FFaker::Currency или предопределенный список?

ответ

1

Я погрузился в код musta и обнаружил, что он использует предопределенный набор значений для validate_uniqueness_of и нет возможности его изменить (кроме хакера Shoulda::Matchers::Util класс).

def self.dummy_value_for(column_type, array: false) 
    if array 
    [dummy_value_for(column_type, array: false)] 
    else 
    case column_type 
    when :integer 
     0 
    when :date 
     Date.new(2100, 1, 1) 
    when :datetime, :timestamp 
     DateTime.new(2100, 1, 1) 
    when :time 
     Time.new(2100, 1, 1) 
    when :uuid 
     SecureRandom.uuid 
    when :boolean 
     true 
    else 
     'dummy value' 
    end 
    end 
end 

Так я построил следующий обходной путь:

context 'coupon code should be unique for the same currency' do 
    before { create(:coupon_amount, currency: 'USD', code: 'aaa') } 
    it 'allows to create a coupon with the same code and another currency' do 
    create(:coupon_amount, currency: 'EUR', code: 'aaa') 
    end 
    it 'does not allow to create a coupon with the same code and currency' do 
    expect { create(:coupon_amount, currency: 'USD', code: 'aaa') }.to(
     raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Code has already been taken') 
    ) 
    end 
end 

Он проверяет уникальность с правильными значениями и работает как шарм.