Class: CanCan::ModelAdapters::ActiveRecordAdapter
- Inherits:
-
AbstractAdapter
- Object
- AbstractAdapter
- CanCan::ModelAdapters::ActiveRecordAdapter
- Defined in:
- lib/cancan/model_adapters/active_record_adapter.rb
Direct Known Subclasses
Instance Attribute Summary collapse
-
#compressed_rules ⇒ Object
readonly
Returns the value of attribute compressed_rules.
Attributes inherited from AbstractAdapter
Class Method Summary collapse
- .child_association_to_parent(parent, child) ⇒ Object
- .matching_parent_child_polymorphic_association(parent_assoc, child_class) ⇒ Object
-
.nested_subject_matches_conditions?(parent, child, all_conditions) ⇒ Boolean
parent_id condition can be an array of integer or one integer, we check the parent against this.
-
.override_nested_subject_conditions_matching?(parent, child, all_conditions) ⇒ Boolean
When belongs_to parent_id is a condition for a model, we want to check the parent when testing ability for a hash => model.
- .parent_child_conditions(parent, child, all_conditions) ⇒ Object
- .parent_condition_name(parent, child) ⇒ Object
- .version_greater_or_equal?(version) ⇒ Boolean
- .version_lower?(version) ⇒ Boolean
Instance Method Summary collapse
- #build_relation(*where_conditions) ⇒ Object
-
#conditions ⇒ Object
Returns conditions intended to be used inside a database query.
- #database_records ⇒ Object
- #extract_multiple_conditions(conditions_extractor, rules) ⇒ Object
-
#initialize(model_class, rules) ⇒ ActiveRecordAdapter
constructor
A new instance of ActiveRecordAdapter.
-
#joins ⇒ Object
Returns the associations used in conditions for the :joins option of a search.
Methods inherited from AbstractAdapter
adapter_class, find, for_class?, inherited, matches_condition?, matches_conditions_hash?, override_condition_matching?, override_conditions_hash_matching?
Constructor Details
#initialize(model_class, rules) ⇒ ActiveRecordAdapter
Returns a new instance of ActiveRecordAdapter.
19 20 21 22 23 24 25 26 27 28 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 19 def initialize(model_class, rules) super @compressed_rules = if CanCan.rules_compressor_enabled RulesCompressor.new(@rules.reverse).rules_collapsed.reverse else @rules end StiNormalizer.normalize(@compressed_rules) ConditionsNormalizer.normalize(model_class, @compressed_rules) end |
Instance Attribute Details
#compressed_rules ⇒ Object (readonly)
Returns the value of attribute compressed_rules.
17 18 19 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 17 def compressed_rules @compressed_rules end |
Class Method Details
.child_association_to_parent(parent, child) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 74 def child_association_to_parent(parent, child) child_class = child.is_a?(Class) ? child : child.class parent_class = parent.is_a?(Class) ? parent : parent.class association = child_class.reflect_on_all_associations(:belongs_to).find do |belongs_to_assoc| # Do not match on polymorphic associations or it will throw an error (klass cannot be determined) !belongs_to_assoc.polymorphic? && belongs_to_assoc.klass == parent.class end return association if association parent_class.reflect_on_all_associations(:has_many).each do |has_many_assoc| association ||= matching_parent_child_polymorphic_association(has_many_assoc, child_class) end association end |
.matching_parent_child_polymorphic_association(parent_assoc, child_class) ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 64 def matching_parent_child_polymorphic_association(parent_assoc, child_class) return nil unless parent_assoc.klass == child_class return nil if parent_assoc&.[:as].nil? child_class.reflect_on_all_associations(:belongs_to).find do |child_assoc| # Only match this way for polymorphic associations child_assoc.polymorphic? && child_assoc.name == parent_assoc.[:as] end end |
.nested_subject_matches_conditions?(parent, child, all_conditions) ⇒ Boolean
parent_id condition can be an array of integer or one integer, we check the parent against this
38 39 40 41 42 43 44 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 38 def nested_subject_matches_conditions?(parent, child, all_conditions) id_condition = parent_child_conditions(parent, child, all_conditions) return id_condition.include?(parent.id) if id_condition.is_a? Array return id_condition == parent.id if id_condition.is_a? Integer false end |
.override_nested_subject_conditions_matching?(parent, child, all_conditions) ⇒ Boolean
When belongs_to parent_id is a condition for a model, we want to check the parent when testing ability for a hash => model
33 34 35 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 33 def override_nested_subject_conditions_matching?(parent, child, all_conditions) parent_child_conditions(parent, child, all_conditions).present? end |
.parent_child_conditions(parent, child, all_conditions) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 46 def parent_child_conditions(parent, child, all_conditions) child_class = child.is_a?(Class) ? child : child.class parent_class = parent.is_a?(Class) ? parent : parent.class foreign_key = child_class.reflect_on_all_associations(:belongs_to).find do |association| # Do not match on polymorphic associations or it will throw an error (klass cannot be determined) !association.polymorphic? && association.klass == parent.class end&.foreign_key&.to_sym # Search again in case of polymorphic associations, this time matching on the :has_many side # via the :as option, as well as klass foreign_key ||= parent_class.reflect_on_all_associations(:has_many).find do |has_many_assoc| matching_parent_child_polymorphic_association(has_many_assoc, child_class) end&.foreign_key&.to_sym foreign_key.nil? ? nil : all_conditions[foreign_key] end |
.parent_condition_name(parent, child) ⇒ Object
92 93 94 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 92 def parent_condition_name(parent, child) child_association_to_parent(parent, child)&.name || parent.class.name.downcase.to_sym end |
.version_greater_or_equal?(version) ⇒ Boolean
9 10 11 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 9 def self.version_greater_or_equal?(version) Gem::Version.new(ActiveRecord.version).release >= Gem::Version.new(version) end |
.version_lower?(version) ⇒ Boolean
13 14 15 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 13 def self.version_lower?(version) Gem::Version.new(ActiveRecord.version).release < Gem::Version.new(version) end |
Instance Method Details
#build_relation(*where_conditions) ⇒ Object
138 139 140 141 142 143 144 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 138 def build_relation(*where_conditions) relation = @model_class.where(*where_conditions) return relation unless joins.present? # subclasses must implement `build_joins_relation` build_joins_relation(relation, *where_conditions) end |
#conditions ⇒ Object
Returns conditions intended to be used inside a database query. Normally you will not call this method directly, but instead go through ModelAdditions#accessible_by.
If there is only one “can” definition, a hash of conditions will be returned matching the one defined.
can :manage, User, :id => 1
query(:manage, User).conditions # => { :id => 1 }
If there are multiple “can” definitions, a SQL string will be returned to handle complex cases.
can :manage, User, :id => 1
can :manage, User, :manager_id => 1
cannot :manage, User, :self_managed => true
query(:manage, User).conditions # => "not (self_managed = 't') AND ((manager_id = 1) OR (id = 1))"
112 113 114 115 116 117 118 119 120 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 112 def conditions conditions_extractor = ConditionsExtractor.new(@model_class) if @compressed_rules.size == 1 && @compressed_rules.first.base_behavior # Return the conditions directly if there's just one definition conditions_extractor.tableize_conditions(@compressed_rules.first.conditions).dup else extract_multiple_conditions(conditions_extractor, @compressed_rules) end end |
#database_records ⇒ Object
128 129 130 131 132 133 134 135 136 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 128 def database_records if override_scope @model_class.where(nil).merge(override_scope) elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins) build_relation(conditions) else @model_class.all(conditions: conditions, joins: joins) end end |
#extract_multiple_conditions(conditions_extractor, rules) ⇒ Object
122 123 124 125 126 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 122 def extract_multiple_conditions(conditions_extractor, rules) rules.reverse.inject(false_sql) do |sql, rule| merge_conditions(sql, conditions_extractor.tableize_conditions(rule.conditions).dup, rule.base_behavior) end end |
#joins ⇒ Object
Returns the associations used in conditions for the :joins option of a search. See ModelAdditions#accessible_by
148 149 150 151 152 153 154 |
# File 'lib/cancan/model_adapters/active_record_adapter.rb', line 148 def joins joins_hash = {} @compressed_rules.reverse_each do |rule| deep_merge(joins_hash, rule.associations_hash) end deep_clean(joins_hash) unless joins_hash.empty? end |