Module: Squeel::Visitors::PredicateVisitation

Included in:
OrderVisitor, PredicateVisitor, SelectVisitor
Defined in:
lib/squeel/visitors/predicate_visitation.rb

Constant Summary collapse

EXPAND_BELONGS_TO_METHODS =
[:eq, :not_eq]
TRUE_SQL =
Arel.sql('1=1').freeze
FALSE_SQL =
Arel.sql('1=0').freeze

Instance Method Summary collapse

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.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/squeel/visitors/predicate_visitation.rb', line 77

def arel_predicate_for(attribute, value, parent)
  if ActiveRecord::Relation === value && value.select_values.empty?
    value = visit(value.select(value.klass.arel_table[value.klass.primary_key]), parent)
  else
    value = can_visit?(value) ? visit(value, parent) : value
  end

  case value
  when Array
    attribute_in_array(attribute, value)
  when Range, Arel::SelectManager
    attribute.in(value)
  else
    attribute.eq(value)
  end
end

#attribute_in_array(attribute, array) ⇒ Object (private)



94
95
96
97
98
99
100
101
102
103
# File 'lib/squeel/visitors/predicate_visitation.rb', line 94

def attribute_in_array(attribute, array)
  if array.empty?
    FALSE_SQL
  elsif array.include? nil
    array = array.compact
    array.empty? ? attribute.eq(nil) : attribute.in(array).or(attribute.eq nil)
  else
    attribute.in array
  end
end

#attribute_not_in_array(attribute, array) ⇒ Object (private)



105
106
107
108
109
110
111
112
113
114
# File 'lib/squeel/visitors/predicate_visitation.rb', line 105

def attribute_not_in_array(attribute, array)
  if array.empty?
    TRUE_SQL
  elsif array.include? nil
    array = array.compact
    array.empty? ? attribute.not_eq(nil) : attribute.not_in(array).and(attribute.not_eq nil)
  else
    attribute.not_in array
  end
end

#quote_for_node(node, v) ⇒ Object (private)

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



124
125
126
127
128
129
130
131
132
133
# File 'lib/squeel/visitors/predicate_visitation.rb', line 124

def quote_for_node(node, v)
  case node
  when Nodes::Function, Nodes::Literal
    quote(v)
  when Nodes::Predicate
    quote_for_node(node.expr, v)
  else
    v
  end
end

#visit_Squeel_Nodes_Predicate(o, parent) ⇒ Object (private)

Visit a Squeel predicate, converting it into an Arel predicate



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/squeel/visitors/predicate_visitation.rb', line 29

def visit_Squeel_Nodes_Predicate(o, parent)
  value = o.value

  # Short-circuit for stuff like `where{ author.eq User.first }`
  # This filthy hack emulates similar behavior in AR PredicateBuilder
  if ActiveRecord::Base === value &&
    EXPAND_BELONGS_TO_METHODS.include?(o.method_name) &&
    association = classify(parent).reflect_on_association(
      symbolify(o.expr)
    )
    return expand_belongs_to(o, parent, association)
  end

  case value
  when Nodes::KeyPath
    value = can_visit?(value.endpoint) ? visit(value, parent) : contextualize(traverse(value, parent))[value.endpoint.to_s]
  when ActiveRecord::Relation
    value = visit(
      value.select_values.empty? ? value.select(value.klass.arel_table[value.klass.primary_key]) : value,
      parent
    )
  else
    value = visit(value, parent) if can_visit?(value)
  end

  value = quote_for_node(o.expr, value)

  attribute = case o.expr
  when Nodes::Stub, Nodes::Function, Nodes::Literal, Nodes::Grouping
    visit(o.expr, parent)
  else
    contextualize(parent)[o.expr]
  end

  if Array === value && [:in, :not_in].include?(o.method_name)
    o.method_name == :in ? attribute_in_array(attribute, value) : attribute_not_in_array(attribute, value)
  else
    attribute.send(o.method_name, value)
  end
end

#visit_Squeel_Nodes_Sifter(o, parent) ⇒ Object (private)

Visit a Squeel sifter by executing its corresponding constraint block in the parent's class, with its given arguments, then visiting the result.



18
19
20
21
# File 'lib/squeel/visitors/predicate_visitation.rb', line 18

def visit_Squeel_Nodes_Sifter(o, parent)
  klass = classify(parent)
  visit(klass.send("sifter_#{o.name}", *o.args), parent)
end