cancancan-squeel 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e54134fb57f78cfd282b96b03b2bc49cf164468
4
- data.tar.gz: 1c5b7a4a8f1aeea3cca907c7755a8a3fc9f4d13c
3
+ metadata.gz: edbfe73803fc60f8f004514ca962dcb92818cbb4
4
+ data.tar.gz: fc92e4d84a7557edf512293ebcfefd8995496d82
5
5
  SHA512:
6
- metadata.gz: 0b03fa5bfafc638b66de73e42856e3047de0450898f2f6b31b0b4c2f514b4ffbeec719b6b6a35e8d03c09f129351a21c97729d41ecdb1c414e64891ff9901fbb
7
- data.tar.gz: 9b2cee5e00ea453850b5065b826ae5917ebfbd9dd0017bd5870f7770f5802797ece8169f8652af4a33b11bcb219af4af1af4fd925b78dd9175ab52160bdf7c9d
6
+ metadata.gz: ca8e2455b16e3bc007829341fffdce9682cdebfccc1089e1f0874a6edfcdc35433a955388814dd79b929e79a43e55b0b198fcd959587429d40fba178a4d9fe39
7
+ data.tar.gz: 9ea2d0969b402e28030d14b1a3c464aafaf8a34d13385056a9853bea7123b5b1e23e10d7ec602ba7f1894a06fbb6b2948fe1beaea158e841092d65bf726101e7
data/.rubocop.yml CHANGED
@@ -7,7 +7,7 @@ AllCops:
7
7
  - '**/Rakefile'
8
8
  Exclude:
9
9
  - 'vendor/bundle/**/*'
10
- TargetRubyVersion: 2.1
10
+ TargetRubyVersion: 2.3
11
11
 
12
12
  Metrics/LineLength:
13
13
  Max: 100
data/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ ## master
2
+
3
+ ### enhancements
4
+
5
+ - Remove unnecessary joins when performing algebraic simplification
6
+ [@lowjoel](https://github.com/lowjoel)
7
+ - Allow querying an unloaded association using a database query. For rules which reference
8
+ associations, and the association is not currently loaded, an SQL query will be executed
9
+ instead to verify the rule instead of loading all rows to memory
10
+ [@lowjoel](https://github.com/lowjoel)
11
+ - Add Frozen String Literal directive [@lowjoel](https://github.com/lowjoel)
12
+
13
+ ## 0.1.1
14
+
15
+ ### enhancements
16
+
17
+ - Added usage instructions. [@lowjoel](https://github.com/lowjoel)
18
+
19
+ ### bug fixes
20
+
21
+ - Follow CanCanCan's default ability rule priority [@lowjoel](https://github.com/lowjoel)
22
+ - Fix generation of comparison operators for array values [@lowjoel](https://github.com/lowjoel)
23
+
24
+ ## 0.1.0
25
+
26
+ Initial release.
data/README.md CHANGED
@@ -11,3 +11,13 @@ uses
11
11
  - `WHERE` fragments, joined lexically using `OR` or `AND` or `NOT`.
12
12
 
13
13
  As a side effect of using `squeel`, this allows self-joins in rule definitions.
14
+
15
+ ## Usage
16
+
17
+ In your `Gemfile`, insert the following line:
18
+
19
+ ```ruby
20
+ gem 'cancancan-squeel'
21
+ ```
22
+
23
+ after you included `cancancan`.
@@ -8,6 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = CanCanCan::Squeel::VERSION
9
9
  spec.authors = ['Joel Low']
10
10
  spec.email = ['joel@joelsplace.sg']
11
+ spec.license = 'MIT'
11
12
 
12
13
  spec.summary = 'Squeel database adapter for CanCanCan.'
13
14
  spec.description = "Implements CanCanCan's rule-based record fetching using Squeel."
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_record'
2
3
  require 'cancancan'
3
4
  require 'squeel'
4
5
 
5
6
  require 'cancancan/squeel/version'
6
7
  require 'cancancan/squeel/active_record_disabler'
8
+ require 'cancancan/squeel/attribute_mapper'
9
+ require 'cancancan/squeel/expression_builder'
10
+ require 'cancancan/squeel/expression_combinator'
7
11
  require 'cancancan/squeel/squeel_adapter'
8
-
@@ -1,7 +1,8 @@
1
- class CanCanCan::Squeel::ActiveRecordDisabler
2
- ::CanCan::ModelAdapters::ActiveRecord4Adapter.class_eval do
3
- def self.for_class?(_)
4
- false
5
- end
6
- end
7
- end
1
+ # frozen_string_literal: true
2
+ class CanCanCan::Squeel::ActiveRecordDisabler
3
+ ::CanCan::ModelAdapters::ActiveRecord4Adapter.class_eval do
4
+ def self.for_class?(_)
5
+ false
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ # Implements mapping attributes, values, and comparators for a given model to appropriate
3
+ # database equivalents.
4
+ #
5
+ # This implements:
6
+ # - comparing values against an array: interpreted as any value for ==, none of the values for !=.
7
+ # - mapping foreign keys to IDs
8
+ module CanCanCan::Squeel::AttributeMapper
9
+ module_function
10
+
11
+ # Picks the appropriate column, comparator, and value to use in the Squeel expression.
12
+ #
13
+ # This checks for association references: this will use the appropriate column name.
14
+ #
15
+ # Array values are interpreted as alternative choices allowed or disallowed.
16
+ #
17
+ # @param [Class] model_class The model class which the key references.
18
+ # @param [Symbol] key The column being compared.
19
+ # @param [Symbol] comparator The comparator to get the appropriate Squeel comparator for.
20
+ # @param value The value to be comparing against.
21
+ # @return [Array<(Symbol, Symbol, Object)>] A triple containing the column to compare with, the
22
+ # comparator to use, and the value to compare with.
23
+ def squeel_comparison_for(model_class, key, comparator, value)
24
+ key, value = map_association(model_class, key, value)
25
+
26
+ comparator = squeel_comparator_for(comparator, value)
27
+ [key, comparator, value]
28
+ end
29
+
30
+ # Picks the table column to compare the value against for the given key.
31
+ #
32
+ # This sets associations to use the proper foreign key column.
33
+ #
34
+ # @param [Class] model_class The model class which the key references.
35
+ # @param [Symbol] key The column being compared.
36
+ # @param value The value to be comparing against.
37
+ # @return [Array<(Symbol, Object)>] A tuple containing the column to compare with and the value
38
+ # to compare with.
39
+ def map_association(model_class, key, value)
40
+ if (association = model_class.reflect_on_association(key))
41
+ key = association.foreign_key
42
+ end
43
+
44
+ [key, value]
45
+ end
46
+
47
+ # Maps the given comparator to a comparator appropriate for the given value.
48
+ #
49
+ # Array values are interpreted as alternative choices allowed or disallowed.
50
+ #
51
+ # @param [Symbol] comparator The comparator to get the appropriate Squeel comparator for.
52
+ # @param value The value to be comparing against.
53
+ # @return [Symbol] The comparator for the desired effect, suitable for the given type.
54
+ def squeel_comparator_for(comparator, value)
55
+ if value.is_a?(Array)
56
+ case comparator
57
+ when :== then :>>
58
+ when :!= then :<<
59
+ end
60
+ else
61
+ comparator
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ # Builds Squeel expressions from the given scope, model class, and a hash of conditions.
3
+ #
4
+ # This is used by building a set of rules for retrieving all accessible records, as well as for
5
+ # building queries instead of loading all records into memory.
6
+ module CanCanCan::Squeel::ExpressionBuilder
7
+ module_function
8
+
9
+ # Builds a new Squeel expression node given a model class, the comparator, and the conditions.
10
+ #
11
+ # @param squeel The Squeel context. This is the value of +self+ within a +where+ DSL block.
12
+ # @param [Class] model_class The model class which the conditions reference.
13
+ # @param [Symbol] comparator The comparator to use when generating the comparison.
14
+ # @param [Hash] conditions The values to compare the given node's attributes against.
15
+ # @return [Array<(Squeel::Nodes::Node, Array<Array<Symbol>>)>] A tuple containing the Squeel
16
+ # expression representing the rule's conditions, as well as an array of joins which the Squeel
17
+ # expression must be joined to.
18
+ def build(squeel, model_class, comparator, conditions)
19
+ build_expression_node(squeel, model_class, comparator, conditions, true)
20
+ end
21
+
22
+ # Builds a new Squeel expression node.
23
+ #
24
+ # @param node The parent node context.
25
+ # @param [Class] model_class The model class which the conditions reference.
26
+ # @param [Symbol] comparator The comparator to use when generating the comparison.
27
+ # @param [Hash] conditions The values to compare the given node's attributes against.
28
+ # @param [Boolean] root True if the node being built is from the root. The root node is special
29
+ # because it does not mutate itself; all other nodes do.
30
+ # @return [Array<(Squeel::Nodes::Node, Array<Array<Symbol>>)>] A tuple containing the Squeel
31
+ # expression representing the rule's conditions, as well as an array of joins which the Squeel
32
+ # expression must be joined to.
33
+ def build_expression_node(node, model_class, comparator, conditions, root = false)
34
+ conditions.reduce([nil, []]) do |(left_expression, joins), (key, value)|
35
+ comparison_node, node_joins = build_comparison_node(root ? node : node.dup, model_class,
36
+ key, comparator, value)
37
+ if left_expression
38
+ [left_expression & comparison_node, joins.concat(node_joins)]
39
+ else
40
+ [comparison_node, node_joins]
41
+ end
42
+ end
43
+ end
44
+
45
+ # Builds a comparison node for the given key and value.
46
+ #
47
+ # @param node The node context to build the comparison.
48
+ # @param [Class] model_class The model class which the conditions reference.
49
+ # @param [Symbol] key The column to compare against.
50
+ # @param [Symbol] comparator The comparator to compare the column against the value.
51
+ # @param value The value to compare the column against.
52
+ def build_comparison_node(node, model_class, key, comparator, value)
53
+ if value.is_a?(Hash)
54
+ reflection_class = model_class.reflect_on_association(key).klass
55
+ expression, joins = build_expression_node(node.__send__(key), reflection_class, comparator,
56
+ value)
57
+ [expression, joins.map { |join| join.unshift(key) }.unshift([key])]
58
+ else
59
+ key, comparator, value = CanCanCan::Squeel::AttributeMapper.
60
+ squeel_comparison_for(model_class, key, comparator, value)
61
+ [node.__send__(key).public_send(comparator, value), []]
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ module CanCanCan::Squeel::ExpressionCombinator
3
+ # This true expression is used to indicate a condition that is always satisfied.
4
+ ALWAYS_TRUE = Squeel::Nodes::Predicate.new(Squeel::Nodes::Literal.new('1'), :eq, 1).freeze
5
+
6
+ # This true expression is used to indicate a condition that is never satisfied.
7
+ ALWAYS_FALSE = Squeel::Nodes::Predicate.new(Squeel::Nodes::Literal.new('1'), :eq, 0).freeze
8
+
9
+ # Combines two Squeel expressions. This is aware of the +ALWAYS_TRUE+ and +ALWAYS_FALSE+
10
+ # constants and performs simplification.
11
+ #
12
+ # @param [Squeel::Nodes::Node] left_expression The left expression.
13
+ # @param [Array] left_expression_joins An array of joins which the Squeel expression must be
14
+ # joined to.
15
+ # @param [Symbol] operator The operator to combine with. This must be either +:&+ or +:|+.
16
+ # @param [Squeel::Nodes::Node] right_expression The right expression.
17
+ # @param [Array] right_expression_joins An array of joins which the Squeel expression must be
18
+ # joined to.
19
+ # @return [Array<(Squeel::Nodes::Node, Array)>] A tuple containing the combination of the given
20
+ # expressions, as well as an array of joins which the Squeel expression must be joined to.
21
+ def combine_squeel_expressions(left_expression, left_expression_joins, operator,
22
+ right_expression, right_expression_joins)
23
+ case operator
24
+ when :& then conjunction_expressions(left_expression, left_expression_joins,
25
+ right_expression, right_expression_joins)
26
+ when :| then disjunction_expressions(left_expression, left_expression_joins,
27
+ right_expression, right_expression_joins)
28
+ else
29
+ raise ArgumentError, "#{operator} must either be :& or :|"
30
+ end
31
+ end
32
+
33
+ # Computes the conjunction of the two Squeel expressions.
34
+ #
35
+ # Boolean simplification is done for the +ALWAYS_TRUE+ and +ALWAYS_FALSE+ values.
36
+ # @param [Squeel::Nodes::Node] left_expression The left expression.
37
+ # @param [Array] left_expression_joins An array of joins which the Squeel expression must be
38
+ # joined to.
39
+ # @param [Squeel::Nodes::Node] right_expression The right expression.
40
+ # @param [Array] right_expression_joins An array of joins which the Squeel expression must be
41
+ # joined to.
42
+ # @return [Array<(Squeel::Nodes::Node, Array)>] A tuple containing the conjunction of the left and
43
+ # right expressions, as well as an array of joins which the Squeel expression must be joined to.
44
+ def conjunction_expressions(left_expression, left_expression_joins, right_expression,
45
+ right_expression_joins)
46
+ if left_expression == ALWAYS_FALSE || right_expression == ALWAYS_FALSE
47
+ [ALWAYS_FALSE, []]
48
+ elsif left_expression == ALWAYS_TRUE
49
+ [right_expression, right_expression_joins]
50
+ elsif right_expression == ALWAYS_TRUE
51
+ [left_expression, left_expression_joins]
52
+ else
53
+ [left_expression & right_expression, left_expression_joins + right_expression_joins]
54
+ end
55
+ end
56
+
57
+ # Computes the disjunction of the two Squeel expressions.
58
+ #
59
+ # Boolean simplification is done for the +ALWAYS_TRUE+ and +ALWAYS_FALSE+ values.
60
+ # @param [Squeel::Nodes::Node] left_expression The left expression.
61
+ # @param [Array] left_expression_joins An array of joins which the Squeel expression must be
62
+ # joined to.
63
+ # @param [Squeel::Nodes::Node] right_expression The right expression.
64
+ # @param [Array] right_expression_joins An array of joins which the Squeel expression must be
65
+ # joined to.
66
+ # @return [Array<(Squeel::Nodes::Node, Array)>] A tuple containing the disjunction of the left and
67
+ # right expressions, as well as an array of joins which the Squeel expression must be joined to.
68
+ def disjunction_expressions(left_expression, left_expression_joins, right_expression,
69
+ right_expression_joins)
70
+ if left_expression == ALWAYS_TRUE || right_expression == ALWAYS_TRUE
71
+ [ALWAYS_TRUE, []]
72
+ elsif left_expression == ALWAYS_FALSE
73
+ [right_expression, right_expression_joins]
74
+ elsif right_expression == ALWAYS_FALSE
75
+ [left_expression, left_expression_joins]
76
+ else
77
+ [left_expression | right_expression, left_expression_joins + right_expression_joins]
78
+ end
79
+ end
80
+ end
@@ -1,15 +1,37 @@
1
+ # frozen_string_literal: true
1
2
  class CanCanCan::Squeel::SqueelAdapter < CanCan::ModelAdapters::AbstractAdapter
3
+ include CanCanCan::Squeel::ExpressionCombinator
4
+
5
+ ALWAYS_TRUE = CanCanCan::Squeel::ExpressionCombinator::ALWAYS_TRUE
6
+ ALWAYS_FALSE = CanCanCan::Squeel::ExpressionCombinator::ALWAYS_FALSE
7
+
2
8
  def self.for_class?(model_class)
3
9
  model_class <= ActiveRecord::Base
4
10
  end
5
11
 
6
12
  def self.override_condition_matching?(subject, name, _)
7
- return false unless subject.class.respond_to?(:defined_enums)
8
-
9
- subject.class.defined_enums.include?(name.to_s)
13
+ match_relation?(subject, name) || match_enum?(subject, name)
10
14
  end
11
15
 
12
16
  def self.matches_condition?(subject, name, value)
17
+ if match_relation?(subject, name)
18
+ matches_relation?(subject, name, value)
19
+ elsif match_enum?(subject, name)
20
+ matches_enum?(subject, name, value)
21
+ else
22
+ false
23
+ end
24
+ end
25
+
26
+ # Overrides condition matching for enums.
27
+ def self.match_enum?(subject, name)
28
+ klass = subject.class
29
+ klass.respond_to?(:defined_enums) && klass.defined_enums.include?(name.to_s)
30
+ end
31
+ private_class_method :match_enum?
32
+
33
+ # Overrides condition matching for enums.
34
+ def self.matches_enum?(subject, name, value)
13
35
  # Get the mapping from enum strings to values.
14
36
  enum = subject.class.public_send(name.to_s.pluralize)
15
37
 
@@ -19,6 +41,24 @@ class CanCanCan::Squeel::SqueelAdapter < CanCan::ModelAdapters::AbstractAdapter
19
41
  # Check to see if the value matches the condition.
20
42
  value.is_a?(Enumerable) ? value.include?(attribute) : attribute == value
21
43
  end
44
+ private_class_method :matches_enum?
45
+
46
+ def self.match_relation?(subject, name)
47
+ subject_attribute = subject.public_send(name)
48
+ subject_attribute.is_a?(ActiveRecord::Relation) && !subject_attribute.loaded
49
+ end
50
+ private_class_method :match_relation?
51
+
52
+ def self.matches_relation?(subject, name, value)
53
+ relation = subject.public_send(name)
54
+ klass = subject.class.reflect_on_association(name).klass
55
+
56
+ relation.where do
57
+ expression, = CanCanCan::Squeel::ExpressionBuilder.build(self, klass, :==, value)
58
+ expression
59
+ end.any?
60
+ end
61
+ private_class_method :matches_relation?
22
62
 
23
63
  def database_records
24
64
  # TODO: Handle overridden scopes.
@@ -29,32 +69,17 @@ class CanCanCan::Squeel::SqueelAdapter < CanCan::ModelAdapters::AbstractAdapter
29
69
 
30
70
  # Builds a relation that expresses the set of provided rules.
31
71
  #
32
- # This first joins all the tables specified in the rules, then builds the corresponding Squeel
33
- # expression for the conditions.
72
+ # The required Squeel expression is built, then the joins which are necessary to satisfy the
73
+ # expressions are added to the query scope.
34
74
  def relation
35
- join_scope = @rules.reduce(@model_class.where(nil)) do |scope, rule|
36
- add_joins_to_scope(scope, build_join_list(rule.conditions))
75
+ adapter = self
76
+ join_list = nil
77
+ scope = @model_class.where(nil).where do
78
+ expression, join_list = adapter.send(:build_accessible_by_expression, self)
79
+ expression
37
80
  end
38
81
 
39
- add_conditions_to_scope(join_scope)
40
- end
41
-
42
- # Builds an array of joins for the given conditions hash.
43
- #
44
- # For example:
45
- #
46
- # a: { b: { c: 3 }, d: { e: 4 }} => [[:a, :b], [:a, :d]]
47
- #
48
- # @param [Hash] conditions The conditions to build the joins.
49
- # @return [Array<Array<Symbol>>] The joins needed to satisfy the given conditions
50
- def build_join_list(conditions)
51
- conditions.flat_map do |key, value|
52
- if value.is_a?(Hash)
53
- [[key]].concat(build_join_list(value).map { |join| Array(join).unshift(key) })
54
- else
55
- []
56
- end
57
- end
82
+ add_joins_to_scope(scope, join_list)
58
83
  end
59
84
 
60
85
  # Builds a relation, outer joined on the provided associations.
@@ -72,24 +97,17 @@ class CanCanCan::Squeel::SqueelAdapter < CanCan::ModelAdapters::AbstractAdapter
72
97
  end
73
98
  end
74
99
 
75
- # Adds the rule conditions to the scope.
76
- #
77
100
  # This builds Squeel expression for each rule, and combines the expression with those to the left
78
101
  # using a fold-left.
79
102
  #
80
- # @param [ActiveRecord::Relation] scope The scope to add the rule conditions to.
81
- def add_conditions_to_scope(scope)
82
- adapter = self
83
- rules = @rules
84
-
85
- # default n
86
- scope.where do
87
- rules.reduce(nil) do |left_expression, rule|
88
- combined_rule = adapter.send(:combine_expression_with_rule, self, left_expression, rule)
89
- break if combined_rule.nil?
90
-
91
- combined_rule
92
- end
103
+ # The rules provided by Cancancan are in reverse order, i.e. the lowest priority rule is first.
104
+ #
105
+ # @param squeel The Squeel scope.
106
+ # @return [Array<(Squeel::Nodes::Node, Array<Array<Symbol>>)>] A tuple containing the Squeel
107
+ # expression, as well as an array of joins which the Squeel expression must be joined to.
108
+ def build_accessible_by_expression(squeel)
109
+ @rules.reverse.reduce([ALWAYS_FALSE, []]) do |(left_expression, joins), rule|
110
+ combine_expression_with_rule(squeel, left_expression, joins, rule)
93
111
  end
94
112
  end
95
113
 
@@ -97,99 +115,32 @@ class CanCanCan::Squeel::SqueelAdapter < CanCan::ModelAdapters::AbstractAdapter
97
115
  #
98
116
  # @param squeel The Squeel scope.
99
117
  # @param left_expression The Squeel expression for all preceding rules.
118
+ # @param [Array<Array<Symbol>>] joins An array of joins which the Squeel expression must be
119
+ # joined to.
100
120
  # @param [CanCan::Rule] rule The rule being added.
101
- # @return [Squeel::Nodes::Node] If the rule has an expression.
102
- # @return [NilClass] If the rule is unconditional.
103
- def combine_expression_with_rule(squeel, left_expression, rule)
104
- right_expression = build_expression_from_rule(squeel, rule)
105
- return right_expression if right_expression.nil? || !left_expression
106
-
107
- if rule.base_behavior
108
- left_expression | right_expression
109
- else
110
- left_expression & right_expression
111
- end
121
+ # @return [Array<(Squeel::Nodes::Node, Array<Array<Symbol>>)>] A tuple containing the Squeel
122
+ # expression, as well as an array of joins which the Squeel expression must be joined to.
123
+ def combine_expression_with_rule(squeel, left_expression, joins, rule)
124
+ right_expression, right_expression_joins = build_expression_from_rule(squeel, rule)
125
+
126
+ operator = rule.base_behavior ? :| : :&
127
+ combine_squeel_expressions(left_expression, joins, operator, right_expression,
128
+ right_expression_joins)
112
129
  end
113
130
 
114
131
  # Builds a Squeel expression representing the rule's conditions.
115
132
  #
116
133
  # @param squeel The Squeel scope.
117
134
  # @param [CanCan::Rule] rule The rule being built.
135
+ # @return [Array<(Squeel::Nodes::Node, Array<Array<Symbol>>)>] A tuple containing the Squeel
136
+ # expression representing the rule's conditions, as well as an array of joins which the Squeel
137
+ # expression must be joined to.
118
138
  def build_expression_from_rule(squeel, rule)
119
- comparator = rule.base_behavior ? :== : :!=
120
- build_expression_node(squeel, @model_class, comparator, rule.conditions, true)
121
- end
122
-
123
- # Builds a new Squeel expression node.
124
- #
125
- # @param node The parent node context.
126
- # @param [Class] model_class The model class which the conditions reference.
127
- # @param [Symbol] comparator The comparator to use when generating the comparison.
128
- # @param [Hash] conditions The values to compare the given node's attributes against.
129
- # @param [Boolean] root True if the node being built is from the root. The root node is special
130
- # because it does not mutate itself; all other nodes do.
131
- def build_expression_node(node, model_class, comparator, conditions, root = false)
132
- conditions.reduce(nil) do |left_expression, (key, value)|
133
- comparison_node = build_comparison_node(root ? node : node.dup, model_class, key,
134
- comparator, value)
135
- if left_expression
136
- left_expression & comparison_node
137
- else
138
- comparison_node
139
- end
140
- end
141
- end
142
-
143
- # Builds a comparison node for the given key and value.
144
- #
145
- # @param node The node context to build the comparison.
146
- # @param [Class] model_class The model class which the conditions reference.
147
- # @param [Symbol] key The column to compare against.
148
- # @param [Symbol] comparator The comparator to compare the column against the value.
149
- # @param value The value to compare the column against.
150
- def build_comparison_node(node, model_class, key, comparator, value)
151
- if value.is_a?(Hash)
152
- reflection = model_class.reflect_on_association(key)
153
- build_expression_node(node.__send__(key), reflection.klass, comparator, value)
139
+ if rule.conditions.empty?
140
+ [rule.base_behavior ? ALWAYS_TRUE : ALWAYS_FALSE, []]
154
141
  else
155
- key, comparator, value = squeel_comparison_for(model_class, key, comparator, value)
156
- node.__send__(key).public_send(comparator, value)
157
- end
158
- end
159
-
160
- # Picks the appropriate column, comparator, and value to use in the Squeel expression.
161
- #
162
- # This checks for association references: this will use the appropriate column name.
163
- #
164
- # Array values are interpreted as alternative choices allowed or disallowed.
165
- #
166
- # @param [Class] model_class The model class which the key references.
167
- # @param [Symbol] key The column being compared.
168
- # @param [Symbol] comparator The comparator to get the appropriate Squeel comparator for.
169
- # @param value The value to be comparing against.
170
- # @return [Array<(Symbol, Symbol, Object)>] A triple containing the column to compare with, the
171
- # comparator to use, and the value to compare with.
172
- def squeel_comparison_for(model_class, key, comparator, value)
173
- if (association = model_class.reflect_on_association(key))
174
- key = association.foreign_key
175
- end
176
-
177
- comparator = squeel_comparator_for(comparator, value)
178
- [key, comparator, value]
179
- end
180
-
181
- # Maps the given comparator to a comparator appropriate for the given value.
182
- #
183
- # Array values are interpreted as alternative choices allowed or disallowed.
184
- #
185
- # @param [Symbol] comparator The comparator to get the appropriate Squeel comparator for.
186
- # @param value The value to be comparing against.
187
- # @return [Symbol] The comparator for the desired effect, suitable for the given type.
188
- def squeel_comparator_for(comparator, value)
189
- case [comparator, value]
190
- when :==, Array then :>>
191
- when :!=, Array then :<<
192
- else comparator
142
+ comparator = rule.base_behavior ? :== : :!=
143
+ CanCanCan::Squeel::ExpressionBuilder.build(squeel, @model_class, comparator, rule.conditions)
193
144
  end
194
145
  end
195
146
  end
@@ -1,4 +1,5 @@
1
- module CanCanCan; end
2
- module CanCanCan::Squeel
3
- VERSION = '0.1.0'.freeze
4
- end
1
+ # frozen_string_literal: true
2
+ module CanCanCan; end
3
+ module CanCanCan::Squeel
4
+ VERSION = '0.1.2'
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cancancan-squeel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Low
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-29 00:00:00.000000000 Z
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -164,16 +164,21 @@ files:
164
164
  - ".rubocop.unhound.yml"
165
165
  - ".rubocop.yml"
166
166
  - ".travis.yml"
167
+ - CHANGELOG.md
167
168
  - Gemfile
168
169
  - README.md
169
170
  - Rakefile
170
171
  - cancancan-squeel.gemspec
171
172
  - lib/cancancan/squeel.rb
172
173
  - lib/cancancan/squeel/active_record_disabler.rb
174
+ - lib/cancancan/squeel/attribute_mapper.rb
175
+ - lib/cancancan/squeel/expression_builder.rb
176
+ - lib/cancancan/squeel/expression_combinator.rb
173
177
  - lib/cancancan/squeel/squeel_adapter.rb
174
178
  - lib/cancancan/squeel/version.rb
175
179
  homepage: https://github.com/lowjoel/cancancan-squeel
176
- licenses: []
180
+ licenses:
181
+ - MIT
177
182
  metadata: {}
178
183
  post_install_message:
179
184
  rdoc_options: []