lib/cancan/model_adapters/active_record_5_adapter.rb
# frozen_string_literal: true
module CanCan
module ModelAdapters
class ActiveRecord5Adapter < ActiveRecord4Adapter
AbstractAdapter.inherited(self)
def self.for_class?(model_class)
version_greater_or_equal?('5.0.0') && model_class <= ActiveRecord::Base
end
# rails 5 is capable of using strings in enum
# but often people use symbols in rules
def self.matches_condition?(subject, name, value)
return super if Array.wrap(value).all? { |x| x.is_a? Integer }
attribute = subject.send(name)
raw_attribute = subject.class.send(name.to_s.pluralize)[attribute]
!(Array(value).map(&:to_s) & [attribute, raw_attribute]).empty?
end
private
def build_joins_relation(relation, *where_conditions)
strategy_class.new(adapter: self, relation: relation, where_conditions: where_conditions).execute!
end
def strategy_class
strategy_class_name = CanCan.accessible_by_strategy.to_s.camelize
CanCan::ModelAdapters::Strategies.const_get(strategy_class_name)
end
def sanitize_sql(conditions)
if conditions.is_a?(Hash)
sanitize_sql_activerecord5(conditions)
else
@model_class.send(:sanitize_sql, conditions)
end
end
def sanitize_sql_activerecord5(conditions)
table = @model_class.send(:arel_table)
table_metadata = ActiveRecord::TableMetadata.new(@model_class, table)
predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
predicate_builder.build_from_hash(conditions.stringify_keys).map { |b| visit_nodes(b) }.join(' AND ')
end
def visit_nodes(node)
# Rails 5.2 adds a BindParam node that prevents the visitor method from properly compiling the SQL query
if self.class.version_greater_or_equal?('5.2.0')
connection = @model_class.send(:connection)
collector = Arel::Collectors::SubstituteBinds.new(connection, Arel::Collectors::SQLString.new)
connection.visitor.accept(node, collector).value
else
@model_class.send(:connection).visitor.compile(node)
end
end
end
end
end