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