0

ActiveRecord::Baseを拡張するいくつかのモデルクラスを持つRailsアプリに取り組んでいます。データベースの内容を更新するために使用できるこれらのモデルクラスのいくつかにさまざまなメソッドを実装しましたが、これらのメソッドはすべて、希望どおりに機能しています。たとえば、TeamStatsさまざまな統計値をデータベースに格納するクラスがあり、ゲームの結果を適用してこれらの属性を更新できるメソッドを定義しました。

class TeamStats < ActiveRecord::Base

  belongs_to :team

  def << game_result
     # Lots of stuff here to update persisted attributes for 
     # wins, losses, total points scored, etc
  end

end

class Team < ActiveRecord::Base
    has_one :stats, :class_name => TeamStats, :dependent => destroy
end

今、私はそのロジックを再利用したいと思っていますが、データベースにコミットしたくない一時的なデータがいくつかあります。たとえば、ゲームのすべてではなくサブセットを使用して、チームの統計を再計算したいと思います。だから私はこのようなことをするコードを持っています:

# relevant_game_results is an array containing game results I want considered
teams.each do |team|
    new_stats = TeamStats.new(:team => team)
    relevent_game_results.each do |results|
        new_stats << results
    end
end

# Do stuff to choose a team based on these un-persisted stats that have been 
# assigned to the team

# After I'm done, through all the team_stats I created and make sure all of the
# related team model objects still reference their original team stats values

これに対する私の最初の計画は、モデルインスタンスを変更し、reload完了したらそれらのインスタンスを呼び出すことでした。例えば:

teams.each do |team|
    team.reload
end

それでうまくいくと思いますが、たくさんのオブジェクトに対してやらなければならないので、できれば1回の操作でやりたいと思います。

私が本当に必要としているのは、コミットされるのではなく、常にロールバックされるトランザクションのようです。これを行うための最も適切な「レールの方法」は何ですか?ブロック内でこれを行い、transactionブロックの最後でを上げることになっていActiveRecord::Rollbackますか?言い換えれば、このようなものですか?

Team.transaction do

  teams.each do |team|
      new_stats = TeamStats.new(:team => team)
      relevent_game_results.each do |results|
          new_stats << results
      end
  end

  # Do stuff to choose a team based on these un-persisted stats that have been 
  # assigned to the team

  raise ActiveRecord::Rollback
end

それは私には少し「汚い」ように思えますが、それは私のJavaのバックグラウンドが通過しているだけです;)Railsの方法にもっと一致するよりクリーンなアプローチはありますか?

更新:これをトランザクション内でラップしてロールバックするのは見苦しいだけでなく、正しく機能させるのは非常に難しいようです。このコードを実行するメソッド自体が別のトランザクションにある可能性があり、ActiveRecordリレーションシップオブジェクトへの変更が自動保存される傾向があるため、それを機能させるために多くのフープを飛び越えなければなりませんでした。

受け入れられた回答の提案に基づいて、新しいTeamStatsオブジェクトを完全に作成し、保存しないというアプローチを採用しました。私にとってはずっとうまくいくようです。

4

2 に答える 2

1

ActiveAttr ( https://github.com/cgriego/active_attr ) や Virtus https://github.com/solnic/virtusなどを確認する必要があります。完全にメモリ内にあるオブジェクトを作成します。これにも良いレールキャストがあります: http://railscasts.com/episodes/326-activeattr

于 2013-03-01T02:52:00.790 に答える
1

ロールバックする代わりに、コピーを作成して保存しないでください。例えば:

# in TeamStats

def hypothetically_add(games)
  copy = dup
  games.each {|game| copy << game }
  copy
end

また、すべてのゲームの一致を照会することに基づいて統計を生成している場合、一度に 1 つずつゲームを Stats オブジェクトに追加することは適切な方法ではありません。ゲームのコレクションで操作する方が良いかもしれません。

# in Team

def stats_from_games_where(*conditions)
  games.where(*conditions).reduce( Hash.new(0) ) do |stats, game|
    stats[:wins] += 1 if # we won
    ... 
  end
end
于 2013-03-01T06:57:07.230 に答える