Class: Squeel::Visitors::PredicateVisitor

Inherits:
Visitor
  • Object
show all
Includes:
PredicateVisitation
Defined in:
lib/squeel/visitors/predicate_visitor.rb

Direct Known Subclasses

HavingVisitor, WhereVisitor

Constant Summary

Constants included from PredicateVisitation

Squeel::Visitors::PredicateVisitation::EXPAND_BELONGS_TO_METHODS, Squeel::Visitors::PredicateVisitation::FALSE_SQL, Squeel::Visitors::PredicateVisitation::TRUE_SQL

Constants inherited from Visitor

Visitor::DISPATCH

Instance Attribute Summary

Attributes inherited from Visitor

#context

Instance Method Summary collapse

Methods included from PredicateVisitation

#arel_predicate_for, #attribute_in_array, #attribute_not_in_array, #quote_for_node, #visit_Squeel_Nodes_Predicate, #visit_Squeel_Nodes_Sifter

Methods inherited from Visitor

#accept, #accept!, #can_visit?, can_visit?, #hash_context_shifted?, #initialize, #quote, #quoted?, #symbolify, #visit, #visit!, #visit_ActiveRecord_Base, #visit_ActiveRecord_Relation, #visit_Arel_Nodes_Node, #visit_Array, #visit_Array!, #visit_Squeel_Nodes_And, #visit_Squeel_Nodes_As, #visit_Squeel_Nodes_Function, #visit_Squeel_Nodes_Grouping, #visit_Squeel_Nodes_KeyPath, #visit_Squeel_Nodes_KeyPath!, #visit_Squeel_Nodes_Literal, #visit_Squeel_Nodes_Not, #visit_Squeel_Nodes_Operation, #visit_Squeel_Nodes_Or, #visit_Squeel_Nodes_Stub, #visit_Symbol, #visit_passthrough, #visit_with_hash_context_shift, #visit_with_hash_context_shift!

Constructor Details

This class inherits a constructor from Squeel::Visitors::Visitor

Instance Method Details

#expand_belongs_to(o, parent, association) ⇒ Arel::Nodes::Node (private)

Expand a belongs_to association that has an AR::Base value. This allows for queries like:

Post.where(:author => User.first)
Post.where{author.eq User.first}


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/squeel/visitors/predicate_visitor.rb', line 20

def expand_belongs_to(o, parent, association)
  context = contextualize(parent)
  ar_base = o.value
  conditions = [
    context[association.foreign_key.to_s].send(o.method_name, ar_base.id)
  ]
  if association.options[:polymorphic]
    conditions << [
      context[association.foreign_type].send(
        o.method_name, ar_base.class.base_class.name
      )
    ]
  end
  conditions.inject(o.method_name == :not_eq ? :or : :and)
end

#implies_hash_context_shift?(v) ⇒ Boolean (private)



65
66
67
68
69
70
71
72
73
74
# File 'lib/squeel/visitors/predicate_visitor.rb', line 65

def implies_hash_context_shift?(v)
  case v
  when Hash, Nodes::Predicate, Nodes::Unary, Nodes::Binary, Nodes::Nary, Nodes::Sifter
    true
  when Nodes::KeyPath
    can_visit?(v.endpoint) && !(Nodes::Stub === v.endpoint)
  else
    false
  end
end

#visit_Hash(o, parent) ⇒ Array (private)

Visit a Hash. This entails iterating through each key and value and visiting each value in turn.



42
43
44
45
46
47
48
49
50
# File 'lib/squeel/visitors/predicate_visitor.rb', line 42

def visit_Hash(o, parent)
  predicates = super

  if predicates.size > 1
    Arel::Nodes::Grouping.new(Arel::Nodes::And.new predicates)
  else
    predicates.first
  end
end

#visit_Hash!(o, parent) ⇒ Object (private)



52
53
54
55
56
57
58
59
60
# File 'lib/squeel/visitors/predicate_visitor.rb', line 52

def visit_Hash!(o, parent)
  predicates = super

  if predicates.size > 1
    Arel::Nodes::Grouping.new(Arel::Nodes::And.new predicates)
  else
    predicates.first
  end
end

#visit_without_hash_context_shift(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.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/squeel/visitors/predicate_visitor.rb', line 84

def visit_without_hash_context_shift(k, v, parent)
  # Short-circuit for stuff like `where(:author => User.first)`
  # This filthy hack emulates similar behavior in AR PredicateBuilder

  if ActiveRecord::Base === v &&
    association = classify(parent).reflect_on_association(k.to_sym)
    return expand_belongs_to(Nodes::Predicate.new(k, :eq, v), parent, association)
  end

  case v
  when Nodes::Stub, Symbol
    v = contextualize(parent)[v.to_s]
  when Nodes::KeyPath # If we could visit the endpoint, we wouldn't be here
    v = contextualize(traverse(v, parent))[v.endpoint.to_s]
  end

  case k
  when Nodes::Predicate
    visit(k % quote_for_node(k.expr, v), parent)
  when Nodes::Function, Nodes::Literal
    arel_predicate_for(visit(k, parent), quote(v), parent)
  when Nodes::KeyPath
    visit(k % quote_for_node(k.endpoint, v), parent)
  else
    attr_name = k.to_s
    attribute = if !hash_context_shifted? && attr_name.include?('.')
        table_name, attr_name = attr_name.split(/\./, 2)
        Arel::Table.new(table_name.to_s, :engine => engine)[attr_name.to_s]
      else
        contextualize(parent)[attr_name]
      end
    arel_predicate_for(attribute, v, parent)
  end
end