私は Ruby on Rails 3.2.2 を使用しています。ユーザーがレコードの「リスト」に存在するレコードを「読み取る」ための適切な権限を持っているかどうかを確認する必要がある場合の一般的なアプローチを知りたいです。つまり、現時点では次のようなものがあります。
class Article < ActiveRecord::Base
def readable_by_user?(user)
# Implementation of multiple authorization checks that are not easy to
# translate into an SQL query (at database level, it executes a bunch of
# "separate" / "different" SQL queries).
... # return 'true' or 'false'
end
end
上記のコードを使用して、単一の記事オブジェクトに対して承認チェックを実行できます。
@article.readable_by_user?(@current_user)
ただし、正確に10個のオブジェクトを取得して、(通常、コントローラーindex
アクションで)次のようなものを作成したい場合
Article.readable_by_user(@current_user).search(...).paginate(..., :per_page => 10)
各オブジェクトに対して承認チェックを実行する必要があります。では、レコードの「リスト」(オブジェクトの配列)に対して「スマート」/「パフォーマンス」な方法で承認チェックを実行するにはどうすればよいでしょうか? つまり、たとえば、ロードして (作成されたデータごとに並べ替えたり、SQL クエリを 10 レコードに制限したりするなど)、それらの各オブジェクトを反復処理して、承認チェックを実行する必要がありますか? または、何か違うものを作る必要がありますか (おそらく、SQL クエリのトリック、Ruby on Rails 機能など)。Article
Article.all
@Matziの回答後に更新
たとえば、次の方法を使用して、ユーザーが「手動で」読み取り可能な記事を取得しようとしました。find_each
# Note: This method is intended to be used as a "scope" method
#
# Article.readable_by_user(@current_user).search(...).paginate(..., :per_page => 10)
#
def self.readable_by_user(user, n = 10)
readable_article_ids = []
Article.find_each(:batch_size => 1000) do |article|
readable_article_ids << article.id if article.readable_by_user?(user)
# Breaks the block when 10 articles have passed the readable authorization
# check.
break if readable_article_ids.size == n
end
where("articles.id IN (?)", readable_article_ids)
end
現時点では、上記のコードは、落とし穴があるとしても、私が考えることができる最も「パフォーマンスの良い妥協案」です。取得するオブジェクトの量を、指定されたs (デフォルトでは 10 レコード) の指定された量のレコードに「制限id
」します。上記の例では); 実際には、ユーザーが読み取り可能なすべてのオブジェクトを「実際に」取得するわけではありません。これは、関連するActiveRecord::Relation
「どこで」/「どの方法で」をさらにスコープしようとすると、readable_by_user
スコープ メソッドが使用されるためです (たとえば、次の方法でも記事を検索する場合)。title
さらに SQL クエリ句を追加する)、レコードをそれらに制限where("articles.id IN (?)", readable_article_ids)
します(つまり、「制限」/「制限」します)。による検索では無視されますtitle
)。readable_by_user
メソッドが他のスコープ メソッドで適切に動作するようにするための問題の解決策は、読み取り可能なすべての記事を読み込むようにブロックしない ことですが、多くのレコードがある場合 (おそらく、別の解決策は、すべての記事をユーザーが読み取れる場所に保存することですが、問題を解決するための一般的/簡単な解決策ではないと思います)。break
id
それで、私がパフォーマントで「本当に」正しい方法で作りたいことを達成する方法はありますか(おそらく、上記のアプローチをまったく変更することによって)?