Class: Squeel::Visitors::PredicateVisitor
- Defined in:
- lib/squeel/visitors/predicate_visitor.rb
Constant Summary collapse
- TRUE_SQL =
Arel.sql('1=1').freeze
- FALSE_SQL =
Arel.sql('1=0').freeze
Constants inherited from Base
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#arel_predicate_for(attribute, value, parent) ⇒ Arel::Nodes::Node
private
Determine whether to use IN or equality testing for a predicate, based on its value class, then return the appropriate predicate.
-
#implies_context_change?(v) ⇒ Boolean
private
Whether the given value implies a context change.
-
#quote_for_node(node, v) ⇒ Object
private
Function nodes require us to do the quoting before the ARel visitor gets a chance to try, because we want to avoid having our values quoted as a type of the last visited column.
-
#visit_ActiveRecord_Base(o, parent) ⇒ Fixnum
private
Visit ActiveRecord::Base objects.
-
#visit_ActiveRecord_Relation(o, parent) ⇒ Arel::SelectManager
private
Visit an ActiveRecord Relation, returning an Arel::SelectManager.
-
#visit_Array(o, parent) ⇒ Array
private
Visit an array, which involves accepting any values we know how to accept, and skipping the rest.
-
#visit_Hash(o, parent) ⇒ Array
private
Visit a Hash.
-
#visit_Squeel_Nodes_And(o, parent) ⇒ Arel::Nodes::Grouping
private
Visit a Squeel And node, returning an ARel Grouping containing an ARel And node.
-
#visit_Squeel_Nodes_Function(o, parent) ⇒ Arel::Nodes::NamedFunction
private
Visit a Squeel function, returning an ARel NamedFunction node.
-
#visit_Squeel_Nodes_KeyPath(o, parent) ⇒ Object
private
Visit a KeyPath by traversing the path and then visiting the endpoint.
- #visit_Squeel_Nodes_Not(o, parent) ⇒ Object private
-
#visit_Squeel_Nodes_Operation(o, parent) ⇒ Arel::Nodes::InfixOperation
private
Visit a Squeel operation node, convering it to an ARel InfixOperation (or subclass, as appropriate).
-
#visit_Squeel_Nodes_Or(o, parent) ⇒ Arel::Nodes::Or
private
Visit a Squeel Or node, returning an ARel Or node.
-
#visit_Squeel_Nodes_Predicate(o, parent) ⇒ Object
private
Visit a Squeel predicate, converting it into an ARel predicate.
-
#visit_Squeel_Nodes_Stub(o, parent) ⇒ Arel::Attribute
private
Visit a Stub by converting it to an ARel attribute.
-
#visit_with_context_change(k, v, parent) ⇒ Object
private
Change context (by setting the new parent to the result of a #find or #traverse on the key), then accept the given value.
-
#visit_without_context_change(k, v, parent) ⇒ Object
private
Create a predicate for a given key/value pair.
Methods inherited from Base
#accept, #can_accept?, can_accept?, #initialize, #quote, #quoted?, #visit
Constructor Details
This class inherits a constructor from Squeel::Visitors::Base
Instance Method Details
#arel_predicate_for(attribute, value, parent) ⇒ Arel::Nodes::Node (private)
Determine whether to use IN or equality testing for a predicate, based on its value class, then return the appropriate predicate.
285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 285 def arel_predicate_for(attribute, value, parent) value = can_accept?(value) ? accept(value, parent) : value if [Array, Range, Arel::SelectManager].include?(value.class) if Array === value && value.empty? FALSE_SQL else attribute.in(value) end else attribute.eq(value) end end |
#implies_context_change?(v) ⇒ Boolean (private)
Returns Whether the given value implies a context change.
203 204 205 206 207 208 209 210 211 212 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 203 def implies_context_change?(v) case v when Hash, Nodes::Predicate, Nodes::Unary, Nodes::Binary, Nodes::Nary true when Nodes::KeyPath can_accept?(v.endpoint) && !(Nodes::Stub === v.endpoint) else false end end |
#quote_for_node(node, v) ⇒ Object (private)
Function nodes require us to do the quoting before the ARel visitor gets a chance to try, because we want to avoid having our values quoted as a type of the last visited column. Otherwise, we can end up with annoyances like having “joe” quoted to 0, if the last visited column was of an integer type.
306 307 308 309 310 311 312 313 314 315 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 306 def quote_for_node(node, v) case node when Nodes::Function quote(v) when Nodes::Predicate Nodes::Function === node.expr ? quote(v) : v else v end end |
#visit_ActiveRecord_Base(o, parent) ⇒ Fixnum (private)
Visit ActiveRecord::Base objects. These should be converted to their id before being used in a comparison.
52 53 54 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 52 def visit_ActiveRecord_Base(o, parent) o.id end |
#visit_ActiveRecord_Relation(o, parent) ⇒ Arel::SelectManager (private)
Visit an ActiveRecord Relation, returning an Arel::SelectManager
136 137 138 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 136 def visit_ActiveRecord_Relation(o, parent) o.arel end |
#visit_Array(o, parent) ⇒ Array (private)
Visit an array, which involves accepting any values we know how to accept, and skipping the rest.
42 43 44 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 42 def visit_Array(o, parent) o.map { |v| can_accept?(v) ? accept(v, parent) : v }.flatten end |
#visit_Hash(o, parent) ⇒ Array (private)
Visit a Hash. This entails iterating through each key and value and visiting each value in turn.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 18 def visit_Hash(o, parent) predicates = o.map do |k, v| if implies_context_change?(v) visit_with_context_change(k, v, parent) else visit_without_context_change(k, v, parent) end end predicates.flatten! if predicates.size > 1 Arel::Nodes::Grouping.new(Arel::Nodes::And.new predicates) else predicates.first end end |
#visit_Squeel_Nodes_And(o, parent) ⇒ Arel::Nodes::Grouping (private)
Visit a Squeel And node, returning an ARel Grouping containing an ARel And node.
184 185 186 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 184 def visit_Squeel_Nodes_And(o, parent) Arel::Nodes::Grouping.new(Arel::Nodes::And.new(accept(o.children, parent))) end |
#visit_Squeel_Nodes_Function(o, parent) ⇒ Arel::Nodes::NamedFunction (private)
Visit a Squeel function, returning an ARel NamedFunction node.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 113 def visit_Squeel_Nodes_Function(o, parent) args = o.args.map do |arg| case arg when Nodes::Function accept(arg, parent) when ActiveRecord::Relation arg.arel.ast when Nodes::KeyPath can_accept?(arg.endpoint) ? accept(arg, parent) : contextualize(traverse(arg, parent))[arg.endpoint.to_sym] when Symbol, Nodes::Stub Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_sym]) else quote arg end end Arel::Nodes::NamedFunction.new(o.name, args, o.alias) end |
#visit_Squeel_Nodes_KeyPath(o, parent) ⇒ Object (private)
Visit a KeyPath by traversing the path and then visiting the endpoint.
61 62 63 64 65 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 61 def visit_Squeel_Nodes_KeyPath(o, parent) parent = traverse(o, parent) accept(o.endpoint, parent) end |
#visit_Squeel_Nodes_Not(o, parent) ⇒ Object (private)
197 198 199 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 197 def visit_Squeel_Nodes_Not(o, parent) accept(o.expr, parent).not end |
#visit_Squeel_Nodes_Operation(o, parent) ⇒ Arel::Nodes::InfixOperation (private)
Visit a Squeel operation node, convering it to an ARel InfixOperation (or subclass, as appropriate)
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 147 def visit_Squeel_Nodes_Operation(o, parent) args = o.args.map do |arg| case arg when Nodes::Function accept(arg, parent) when Nodes::KeyPath can_accept?(arg.endpoint) ? accept(arg, parent) : contextualize(traverse(arg, parent))[arg.endpoint.to_sym] when Symbol, Nodes::Stub Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_sym]) else quote arg end end op = case o.operator when :+ Arel::Nodes::Addition.new(args[0], args[1]) when :- Arel::Nodes::Subtraction.new(args[0], args[1]) when :* Arel::Nodes::Multiplication.new(args[0], args[1]) when :/ Arel::Nodes::Division.new(args[0], args[1]) else Arel::Nodes::InfixOperation(o.operator, args[0], args[1]) end o.alias ? op.as(o.alias) : op end |
#visit_Squeel_Nodes_Or(o, parent) ⇒ Arel::Nodes::Or (private)
Visit a Squeel Or node, returning an ARel Or node.
193 194 195 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 193 def visit_Squeel_Nodes_Or(o, parent) accept(o.left, parent).or(accept(o.right, parent)) end |
#visit_Squeel_Nodes_Predicate(o, parent) ⇒ Object (private)
Visit a Squeel predicate, converting it into an ARel predicate
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 83 def visit_Squeel_Nodes_Predicate(o, parent) value = o.value if Array === value && value.empty? && [:in, :not_in].include?(o.method_name) # Special case, in/not_in with empty arrays should be false/true respectively return o.method_name == :in ? FALSE_SQL : TRUE_SQL end if Nodes::KeyPath === value value = can_accept?(value.endpoint) ? accept(value, parent) : contextualize(traverse(value, parent))[value.endpoint.to_sym] else value = accept(value, parent) if can_accept?(value) end case o.expr when Nodes::Stub accept(o.expr, parent).send(o.method_name, value) when Nodes::Function accept(o.expr, parent).send(o.method_name, quote(value)) else contextualize(parent)[o.expr].send(o.method_name, value) end end |
#visit_Squeel_Nodes_Stub(o, parent) ⇒ Arel::Attribute (private)
Visit a Stub by converting it to an ARel attribute
73 74 75 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 73 def visit_Squeel_Nodes_Stub(o, parent) contextualize(parent)[o.symbol] end |
#visit_with_context_change(k, v, parent) ⇒ Object (private)
Change context (by setting the new parent to the result of a #find or #traverse on the key), then accept the given value.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 221 def visit_with_context_change(k, v, parent) parent = case k when Nodes::KeyPath traverse(k, parent, true) else find(k, parent) end case v when Hash, Nodes::KeyPath, Nodes::Predicate, Nodes::Unary, Nodes::Binary, Nodes::Nary accept(v, parent || k) when Array v.map {|val| accept(val, parent || k)} else raise ArgumentError, <<-END Hashes, Predicates, and arrays of visitables as values imply that their corresponding keys are a parent. This didn't work out so well in the case of key = #{k} and value = #{v}" END end end |
#visit_without_context_change(k, v, parent) ⇒ Object (private)
Create a predicate for a given key/value pair. If the value is a Symbol, Stub, or KeyPath, it’s converted to a table.column for the predicate value.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/squeel/visitors/predicate_visitor.rb', line 251 def visit_without_context_change(k, v, parent) case v when Nodes::Stub, Symbol v = contextualize(parent)[v.to_sym] when Nodes::KeyPath # If we could accept the endpoint, we wouldn't be here v = contextualize(traverse(v, parent))[v.endpoint.to_sym] end case k when Nodes::Predicate accept(k % quote_for_node(k.expr, v), parent) when Nodes::Function arel_predicate_for(accept(k, parent), quote(v), parent) when Nodes::KeyPath accept(k % quote_for_node(k.endpoint, v), parent) else attr_name = k.to_s attribute = if attr_name.include?('.') table_name, attr_name = attr_name.split(/\./, 2) Arel::Table.new(table_name.to_sym, :engine => engine)[attr_name.to_sym] else contextualize(parent)[k.to_sym] end arel_predicate_for(attribute, v, parent) end end |