O que é uma abordagem comum para registros de escopo por aqueles que um usuário pode “ler”?
Estou usando Ruby on Rails 3.2.2 e gostaria de saber qual é uma abordagem comum quando deve ser verificado se um usuário possui autorizações adequadas para "ler" registros presentes em uma "lista" de registros. Ou seja, neste momento tenho o seguinte:
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
Ao usar o código acima, posso executar verificações de autorização em umsolteiro objeto do artigo:
@article.readable_by_user?(@current_user)
No entanto, quando eu gostaria de fazer (geralmente, no meu controladorindex
ação) algo como o seguinte, recuperando exatamente 10 objetos
Article.readable_by_user(@current_user).search(...).paginate(..., :per_page => 10)
Ainda preciso realizar verificações de autorização em cada objeto. Assim,o que posso fazer para realizar verificações de autorização nessa "lista" de registros (uma matriz deArticle
objetos)de uma maneira "inteligente" / "de alto desempenho"? Isto é, por exemplo, devo carregarArticle.all
(talvez ordenando aqueles por dados criados, limitando a consulta SQL a 10 registros, ...) e, em seguida, iterar em cada um desses objetos para realizar verificações de autorização? ou devo fazer algo diferente (talvez com algum truque de consulta SQL, alguma instalação Ruby on Rails ou outra coisa)?
Eu tentei recuperar artigoslegível por um usuário "manualmente", por exemplo, usando ofind_each
método:
# 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
Neste momento, o código acima é o mais "compromisso de desempenho" que eu posso pensar, mesmo que tenha alguma armadilha: ele "restringe"a quantidade de objetos recuperados para uma determinada quantidade de registros comid
s (10 registros por padrão no exemplo acima); praticamente falando,"realmente" não recupera todos os objetos legíveis por um usuário desde quando você tenta ampliar ainda mais o relacionadoActiveRecord::Relation
"onde" / "com o qual" oreadable_by_user
método de escopo é usado (por exemplo, quando você também procuraria artigos portitle
adicionando mais uma cláusula de consulta SQL),restringir registros para aqueleswhere("articles.id IN (?)", readable_article_ids)
(isto é, "limita" / "restringe" a quantidade de objetos recuperados e legíveis aos primeiros 10 e todos os outros artigoslegível pelo usuário será ignorado ao pesquisar portitle
). Uma solução para o problema, a fim de tornar oreadable_by_user
método para trabalhar corretamente com outros métodos de escopo poderia ser fazernão break
o bloco de modo a carregar todoslegível artigos, mas não é bom por razões de desempenho quando há muitos registros (talvez, outra solução poderia ser armazenar em algum lugar todo o artigoid
s legível por um usuário, mas eu acho que não é uma solução comum / fácil para resolver o problema).
Assim,existe alguma maneira de realizar o que eu gostaria de fazer de uma forma eficaz e "correta" (talvez mudando a abordagem acima)?