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, #can_visit?, can_visit?, #hash_context_shifted?, #initialize, #quote, #quoted?, #symbolify, #visit, #visit_ActiveRecord_Base, #visit_ActiveRecord_Relation, #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_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

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}

Parameters:

Returns:

  • (Arel::Nodes::Node)

    An Arel predicate node



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)

Returns Whether the given value implies a context change.

Parameters:

  • v

    The value to consider

Returns:

  • (Boolean)

    Whether the given value implies a context change



55
56
57
58
59
60
61
62
63
64
# File 'lib/squeel/visitors/predicate_visitor.rb', line 55

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.

Parameters:

  • o (Hash)

    The Hash to visit

  • parent

    The current parent object in the context

Returns:

  • (Array)

    An array of values for use in a where or having clause



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_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.

Parameters:

  • k

    The hash key

  • v

    The hash value

  • parent

    The current parent object in the context

Returns:

  • An Arel predicate



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/squeel/visitors/predicate_visitor.rb', line 74

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