2016-03-24 39 views
1

Bir ödeme denetleyicisini incelemek ve bir el kullanabilmek için büyük bir refaktörle mücadele ediyorum. Birinci adım Fabrikalarımı tamir etmeye çalışıyorum. Şu anda tüm fabrikalar kendi başlarına çok iyi çalışıyorlar, ancak dernekler FactoryGirl.create(:job, :purchased_with_coupon)'u kurmaya çalıştığımda, kuponu doğru bir şekilde kupona değil, ödemeye ayarlayacaktır. Bu, ödenen ücretin her zaman her zaman 1 olduğu anlamına gelir. Diğer bölümün görülebildiğini fark ettim. Şişirilmiş kontrolörle uğraşmaya başlamadan önce testlerim için bunu anlamaya ihtiyacım var. Düşünceler?raylar 4 refactor factorygirl hatalı veri yaratıyor

Fabrikalar

FactoryGirl.define do 
    factory :job do 
    category 
    company 
    title { FFaker::Company.position } 
    location { "#{FFaker::Address.city}, #{FFaker::AddressUS.state}" } 
    language_list { [FFaker::Lorem.word] } 
    short_description { FFaker::Lorem.sentence } 
    description { FFaker::HTMLIpsum.body } 
    application_process { "Please email #{FFaker::Internet.email} about the position." } 

    trait :featured do |job| 
     job.is_featured true 
    end 

    trait :reviewed do |job| 
     job.reviewed_at { Time.now } 
    end 

    trait :purchased do |job| 
     job.reviewed_at { Time.now } 
     job.start_at { Time.now } 
     job.end_at { AppConfig.product['settings']['job_active_for_day_num'].day.from_now } 
     job.paid_at { Time.now } 
     payments { |j| [j.association(:payment)] } 
    end 

    trait :purchased_with_coupon do |job| 
     job.reviewed_at { Time.now } 
     job.start_at { Time.now } 
     job.end_at { AppConfig.product['settings']['job_active_for_day_num'].day.from_now } 
     job.paid_at { Time.now } 
     association :coupon, factory: :coupon 
     payments { |j| [j.association(:payment)] } 
    end 

    trait :expired do |job| 
     start_at = (200..500).to_a.sample.days.ago 
     job.reviewed_at { start_at } 
     job.start_at { start_at } 
     job.end_at { |j| j.start_at + AppConfig.product['settings']['job_active_for_day_num'].days } 
     job.paid_at { start_at } 
     payments { |j| [j.association(:payment)] } 
    end 
    end 
end 

FactoryGirl.define do 
    factory :payment do 
    job 
    # price_paid { rand(100..150) } 
    price_paid { 1 } 
    stripe_customer_token { (0...50).map { (65 + rand(26)).chr }.join } 
    end 
end 

FactoryGirl.define do 
    factory :coupon do 
    code { rand(25**10) } 
    percent_discount { rand(100**1) } 
    start_at { 2.days.ago } 
    end_at { 30.day.from_now } 

    trait :executed do |c| 
     association :job, factory: [:job, :purchased] 
     c.executed_at { Time.now } 
    end 
    end 
end 

Modelleri

class Job < ActiveRecord::Base 
    acts_as_paranoid 
    strip_attributes 

    acts_as_taggable 
    acts_as_taggable_on :languages 

    belongs_to :company 
    before_validation :find_company 
    belongs_to :category 
    has_one :coupon 
    has_many :payments 

    before_create :create_slug, :set_price 
    after_create :update_vanity_url 

    accepts_attachments_for :company 
    accepts_nested_attributes_for :company 
    accepts_nested_attributes_for :coupon 
    accepts_nested_attributes_for :payments 

    validates :title, 
      :location, 
      :short_description, 
      presence: true, 
      format: { with: /\A[\w\d .,:[email protected]]+\z/, message: :bad_format } 

    validates :application_process, 
      presence: true, 
      format: { with: %r{\A[\w\d .,:/@&=?-]+\z}, message: :bad_format } 

    validates :title, length: { minimum: 10, maximum: 45 } 
    validates :location, length: { minimum: 10, maximum: 95 } 
    validates :short_description, length: { minimum: 10, maximum: 245 } 
    validates :application_process, length: { minimum: 10, maximum: 95 } 

    validates :description, 
      :category_id, 
      :language_list, 
      presence: true 

    validates :reviewed_at, 
      :start_at, 
      :end_at, 
      :paid_at, 
      date: { allow_blank: true } 

    validates :start_at, date: { before: :end_at, message: :start_at_before_end_at }, if: proc { start_at? } 
    validates :end_at, date: { after: :start_at, message: :end_at_after_start_at }, if: proc { end_at? } 

    scope :active, -> { where.not(reviewed_at: nil, paid_at: nil).where('end_at >= ?', Date.today) } 

    def expired? 
    end_at.present? && end_at < Date.today 
    end 

    def reviewed? 
    reviewed_at.present? 
    end 

    def paid_for? 
    reviewed? && paid_at.present? 
    end 

    def active? 
    reviewed? && paid_at.present? && end_at <= Date.today 
    end 

    private 

    def set_price 
    self.price = AppConfig.product['settings']['job_base_price'] 
    end 

    def create_slug 
    self.slug = title.downcase.parameterize 
    end 

    def update_vanity_url 
    self.vanity_url = '/jobs/' + company.slug + '/' + slug + '/' + id.to_s + '/' 
    save 
    end 

    def find_company 
    existing_company = Company.where(email: company.email) if company 
    self.company = existing_company.first if existing_company.count > 0 
    end 
end 

class Coupon < ActiveRecord::Base 
    acts_as_paranoid 
    strip_attributes 

    belongs_to :job 

    validates :start_at, date: { before: :end_at } 
    validates :executed_at, date: { allow_blank: true } 

    validates_presence_of :job, if: proc { executed_at? } 
    validates_presence_of :executed_at, if: :job 

    validates :code, 
      presence: true, 
      length: { minimum: 10, maximum: 19 }, 
      uniqueness: { case_sensitive: false }, 
      numericality: { only_integer: true } 

    validates :percent_discount, 
      inclusion: { in: 1..100 }, 
      length: { minimum: 1, maximum: 3 }, 
      numericality: { only_integer: true }, 
      presence: true 

    scope :active, -> { where('start_at < ? AND end_at > ? AND executed_at IS ?', Date.today, Date.today, nil) } 

    def active? 
    start_at < Date.today && end_at > Date.today && executed_at.nil? 
    end 

    def executed? 
    job_id.present? 
    end 
end 


class Payment < ActiveRecord::Base 
    belongs_to :job 
    belongs_to :coupon 

    validates_presence_of :job 
    validate :coupon_must_be_active 

    before_create :net_price 

    Numeric.include CoreExtensions::Numeric::Percentage 

    attr_accessor :coupon_code 
    def coupon_code=(code) 
    @coupon = Coupon.find_by_code(code) 
    end 

    def net_price 
    return job.price unless @coupon 
    job.price = @coupon.percent_discount.percent_of(job.price) 
    self.coupon = @coupon 
    end 

    private 

    def coupon_must_be_active 
    if @coupon 
     errors[:coupon] << I18n.t('flash_messages.coupons.id.inactive') unless @coupon.active? 
    elsif @coupon_code.present? 
     errors[:coupon_code] << I18n.t('flash_messages.coupons.id.not_found') 
    end 
    end 
end 

cevap

2

sorun mantık senin Payment üzerinde price_paid sütun güncelleyen modellerinin dışında olduğu gibi görünüyor ve muhtemelen Üzerinde coupon_id ayarını da yapabilirsiniz.

Bu nedenle, denetleyicilerinizden, hizmet sınıflarınızdan vb. Herhangi bir ek mantığı fabrikada bir after(:create) geri arama içine çoğaltmanızı öneriyorum.

trait :purchased_with_coupon do 
    # ...other attributes... 

    association :coupon 

    after(:create) do |job, evaulator| 
    discount_value = 100 - job.coupon.percent_discount)/100.0 
    calculated_price_paid = job.price * discount_value 
    create(:payment, price_paid: price_paid, job: job, coupon: coupon) 
    end 
end 

Şimdi nihayetinde bu kod böyle kolayca test (ve diğer testlerde kullanılan) edilebilen bir hizmet sınıfı olarak soyutlama, bir çeşit aittir. Ancak, bir refaktöre başladığınızı ve sınavları geçmek istediğinizi belirttiniz. Bunu soyutlamaya kadar makul bir uzlaşma olduğunu düşünüyorum. senin gözlük,

class CreatePaymentWithCoupon 
    attr_reader :job 

    def initialize(job) 
    @job = job 
    end 

    def call 
    job.payments.create(coupon: job.coupon, price_paid: discounted_price) 
    end 

    private 

    def discounted_price 
    discount_value = (100 - job.coupon.percent_discount)/100.0 
    job.price * discount_value 
    end 
end 

Sonra: Sonuçta, böyle bir şey yapacağını

it "calculates discounted price" do 
    coupon = create(:coupon, percent_discount: 25) 
    job = create(:job, :purchased_with_coupon, price: 100) 
    CreatePaymentWithCoupon.new(job).call 

    expect(job.payments.first.price_paid).to eq(75.0) 
end