2016-03-24 15 views
1

私は、支払いコントローラをスリム化するために大手リファクタと戦っており、手を使うことができました。ステップ1私は私の工場を修正しようとしています。今のところ、すべての工場が独自に素晴らしい仕事をしていますが、団体を構築しようとすると、FactoryGirl.create(:job, :purchased_with_coupon)は、クーポンではなく支払いが正しく設定されます。これは、支払われた価格は常に1であることを意味します。私はちょうどあなたが他のセクションがコメントアウトされて見ることができることに気づいた。私が肥大化したコントローラに取り組む前に、私はこれをテストのために把握する必要があります。思考?問題は、おそらくロジックがあなたのPaymentprice_paid列を更新している、あなたのモデルの外に存在することである、とのように見えますrails 4 refactor factorygirlが不正確なデータを作成する

工場

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 

モデル

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 

答えて

2

coupon_idも同様に設定します。

コントローラ、サービスクラスなどから余分なロジックを複製して、工場のコールバックafter(:create)に入れることをおすすめします。今

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 

最終的に、そのコードは、容易にテスト(および他の試験に使用)することができるサービスクラスとして抽象化のいくつかの種類に属します。しかし、あなたはリファクタリングを開始し、テストに合格することを望んでいると述べました。私はこれを抽象化する準備が整うまで、これは合理的な妥協であると思います。あなたのスペックで、その後

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 

:最終的に、私はこのような何かをするだろう

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 
関連する問題