lib/recurso/queries/relation.rb in recurso-0.5.3 vs lib/recurso/queries/relation.rb in recurso-0.6.1

- old
+ new

@@ -1,58 +1,70 @@ module Recurso module Queries class Relation - def initialize(identity, resource, relation_name, all_columns: true, action: :view) + def initialize(identity, resource, relation_name, all_columns: true, action: :view, include_actions: []) @identity = identity @resource = resource @relation = resource.send(relation_name) @all_columns = all_columns + @include_actions = (include_actions + Array(action)).uniq @action = action end def resources - @resources ||= join_permissions - .select("#{@relation.table_name}.#{@all_columns ? :* : :id}") - .where(coalesce(@action)) - .distinct + @relation + .select("#{@relation.table_name}.#{@all_columns ? :"*" : :id}") + .select(*@include_actions.map { |action| "included.can_#{action}"}) + .joins(" + INNER JOIN (#{included_permissions}) included + ON #{@relation.table_name}.id = included.id + AND included.can_#{@action} = 1 + ") end private - def join_permissions - @relation.relevant_associations.reduce(@relation) do |result, assoc| - result.joins(through_join_for(assoc)).joins(" - LEFT OUTER JOIN #{permission_class.table_name} #{assoc.name}_permissions - ON #{assoc.name}_permissions.resource_type = '#{assoc.class_name}' - AND #{assoc.name}_permissions.resource_id = #{resource_id_for(assoc)} - AND #{assoc.name}_permissions.#{identity_foreign_key} = #{@identity.id.to_i} - ") - end + def included_permissions + " + SELECT cascaded.id, #{included_permission_select} + FROM (#{cascaded_permissions}) cascaded + GROUP BY cascaded.id + " end + def cascaded_permissions + @relation.relevant_associations.map do |assoc| + @relation + .select(:id, :level) + .joins(through_join_for(assoc)) + .joins(" + INNER join #{permission_class.table_name} + ON #{permission_class.table_name}.resource_type = '#{assoc.class_name}' + AND #{permission_class.table_name}.resource_id = #{resource_id_for(assoc)} + AND #{permission_class.table_name}.#{identity_foreign_key} = #{@identity.id.to_i} + ").to_sql + end.join(" UNION ") + end + + def included_permission_select + @include_actions.map do |action| + level_values = @resource.relevant_levels_for(action).map { |level| permission_class.levels[level] }.join(',') + + "cascaded.level IN (#{level_values}) as can_#{action}" + end.join(", ") + end + def resource_id_for(assoc) "#{(assoc.through_reflection || assoc.active_record).table_name}.#{assoc.foreign_key}" end def through_join_for(assoc) return unless through = assoc.through_reflection " LEFT OUTER JOIN #{through.table_name} ON #{through.table_name}.#{through.association_primary_key} = #{resource_id_for(through)} " - end - - def coalesce(action) - "coalesce(#{level_columns}) IN (#{level_values(action)})" - end - - def level_columns - @relation.relevant_associations.map { |assoc| "#{assoc.name}_permissions.level" }.join(',') - end - - def level_values(action) - @resource.relevant_levels_for(action).map { |level| permission_class.levels[level] }.join(',') end def permission_class @permission_class ||= Recurso::Config.instance.permission_class_name_for(@identity.class).constantize end