Область применения Rails со сложным оператором SQL, включая OR
Я сейчас работаю над небольшим Rails-проектом. Я использую Pundit для авторизации действий контроллера и прочего. Работа сейчас состоит в том, чтобы использовать Pundit для действия индекса и использовать Pundit's policy_scope
в моем контроллере, чтобы получить проекты, которые пользователь может видеть.
Есть 3 способа, которыми пользователь должен иметь возможность видеть проект: - Он - владелец - Он был приглашен - Проект является публичным
Я попробовал несколько решений прямо сейчас, но в итоге сделал следующее:
scope :public_or_involved, -> (user) {
joins('LEFT JOIN invitations ON invitations.project_id = projects.id').
where('projects.is_public = TRUE OR projects.owner_id = ? OR invitations.reciever_id = ?', user.id, user.id)
}
Это единственный способ получить что-то вроде public_or_involved для работы со следующей областью pundit в project_policy.rb:
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.public_or_involved(user)
end
end
Итак, мой вопрос, наконец:
Есть ли элегантный способ делать то, что я делаю сейчас? Это кажется уродливым и не похожим на рельсы!
2 ответа
Предостережение, я не знаю pundit (я предпочитаю более простой подход DSL в acl9), так что я действительно только конвертирую ваш запрос для вас, вы обычно можете заставить Rails дать вам LEFT JOIN
с includes()
, и вы можете использовать этот маленький трюк, чтобы получить аккуратнее OR
:
scope :public_or_involved,
-> (user) { includes(:invitations).where(
where( is_public: true, owner_id: user.id, invitations: { receiver_id: user.id } ).where_values.reduce(&:or)
)}
По моему опыту, решение, которое у вас есть сейчас, является лучшим. Вы можете использовать что-то вроде Arel, Squeel или MetaWhere, чтобы Ruby-фу ваши строки SQL, но они приходят с дополнительной сложностью.
Если это соответствует вашим требованиям, я бы не стал слишком беспокоиться о том, чтобы сделать его "более Rails-y".