2016-05-30 14 views
2

Rails 4およびdelayed_job 4.1.2。レビューを破棄した後の総合評価の再計算を遅らせようとしますが、レビューオブジェクトを破棄した後にレビューオブジェクトのIDがないためです。だから、オブジェクトを破壊しようとした後たびに、それが遅延ジョブを作成しようとしますが、このエラーがスローされます。遅延ジョブ - オブジェクトを破棄した後に非永続レコードに対してジョブを作成できません。

ArgumentError (job cannot be created for non-persisted record: 
#<Review id: 44, review: "Bad", rating: 1, reviewable_id: 2, 
reviewable_type: "Spot", user_id: 1, created_at: "2016-05-30 17:13:29", 
updated_at: "2016-05-30 17:13:29">): 
    app/controllers/reviews_controller.rb:40:in `destroy' 

私は、次のコードを持っている:

# reviews_controller.rb 
class ReviewsController < ApplicationController 
    def destroy 
    review.destroy 
    flash[:success] = t("reviews.destroy.success") 
    end 
end 

# review.rb 
class Review < ActiveRecord::Base 
    after_destroy :calculate_overall_rating 

    def calculate_overall_rating 
    if number_of_reviews > 0 
     reviewable.update_attribute(:overall_rating, overall_rating) 
    else 
     reviewable.update_attribute(:overall_rating, 0) 
    end 
    end 
    handle_asynchronously :calculate_overall_rating 
end 

それはcalculate_overall_rating doesnのことに注意することは良いことですReviewオブジェクトが必要です。

handle_asynchronously :calculate_overall_ratingを削除しても、再計算されます。しかし、私はこの仕事を延期しようとしています。

答えて

3

削除された(またはまだ作成されていない)レコードのメソッドを遅延しようとすると、このエラーは実際にraisedによってdelayed_jobになります。このエラーの直接の原因は、handle_asynchronouslyメソッドを呼び出すときにdelayed_job passes self(つまり、レビューされたばかりのレビューオブジェクト)がターゲットオブジェクトとして存在するためです。私はなぜそれがそのように振る舞うのか分からない、私はちょうどそれがActiveJobと同じように動作すると言う宝石の作者の一人からstatementを見つけた。

とにかく、間違った場所に再計算方法を定義している可能性があります。レビューが破棄された後、正しく評価された場合、指定されたreviewable(例:スポット)のすべてのレビューに基づいて平均評価が再計算されます。そのようなコードは、単一のレビューインスタンスのメソッドとして定義されていることは私にとっては不思議なようです。 1つのレビュー(削除されたもの)は、同じ場所の他のレビューについて何も知ってはいけません。

私はメソッドは、パラメータとしてreviewableで、代わりクラスメソッドとして定義されている必要がありますね。もちろんこれは、他の計算方法をoverall_ratingnumber_of_reviewsのクラスメソッドにもしなければならないことを意味します。しかし、これらの方法の領域がの外側にあるので、これは良いことだと思います。以下のような何か:

# review.rb 
class Review < ActiveRecord::Base 
    after_destroy :recalculate_overall_rating 

    def recalculate_overall_rating 
    self.class.calculate_overall_rating(reviewable) 
    end 

    def self.calculate_overall_rating(reviewable) 
    if number_of_reviews(reviewable) > 0 
     reviewable.update_attribute(:overall_rating, overall_rating(reviewable)) 
    else 
     reviewable.update_attribute(:overall_rating, 0) 
    end 
    end 
    handle_asynchronously :calculate_overall_rating 
end 

別のオプション(と私ももう少し、私は推測する、それが好き)、例えば、検討可能クラス内部の再計算方法を置くことであろうPostクラス内にあります。レビュー可能なクラスタイプがさらにある場合は、これらのクラスすべてからモジュールを含めることができます(例: Reviewable(私はRailsアソシエーション名と衝突しないことを望みます)、今回はのインスタンスのメソッドとして再計算メソッドを配置します。どうして? インスタンスレビューのすべてのレビューを再計算し、レビューの削除後もまだ存在するので、非同期で簡単に実行できるためです。次のようなもの:

# reviewable.rb 
module Reviewable 
    def calculate_overall_rating 
    if number_of_reviews > 0 
     update_attribute(:overall_rating, overall_rating) 
    else 
     update_attribute(:overall_rating, 0) 
    end 
    end 
    handle_asynchronously :calculate_overall_rating 

    # overall_rating and number_of_reviews are also defined in this module 
end 

# review.rb 
class Review < ActiveRecord::Base 
    after_destroy :recalculate_overall_rating 

    def recalculate_overall_rating 
    reviewable.calculate_overall_rating 
    end 
end 

# post.rb 
class Post < ActiveRecord::Base 
    include Reviewable 
end 
関連する問題