class Mobility::Backends::ActiveRecord::Table::Visitor

Internal class used to visit all nodes in a predicate clause and return a single join type required for the predicate, or nil if no join is required. (Similar to the KeyValue Visitor class.)

Example:

class Post < ApplicationRecord
  extend Mobility
  translates :title, :content, backend: :table
end

backend_class = Post.mobility_backend_class(:title)
visitor = Mobility::Backends::ActiveRecord::Table::Visitor.new(backend_class, :en)

title   = backend_class.build_node("title", :en)   # arel node for title
content = backend_class.build_node("content", :en) # arel node for content

visitor.accept(title.eq(nil).and(content.eq(nil)))
#=> Arel::Nodes::OuterJoin

visitor.accept(title.eq("foo").and(content.eq(nil)))
#=> Arel::Nodes::InnerJoin

In the first case, both attributes are matched against nil values, so we need an OUTER JOIN. In the second case, one attribute is matched against a non-nil value, so we can use an INNER JOIN.

Private Instance Methods

visit_Arel_Nodes_Equality(object) click to toggle source
# File lib/mobility/backends/active_record/table.rb, line 195
def visit_Arel_Nodes_Equality(object)
  nils, nodes = [object.left, object.right].partition(&:nil?)
  if nodes.any?(&method(:visit))
    nils.empty? ? INNER_JOIN : OUTER_JOIN
  end
end
visit_Arel_Nodes_Or(object) click to toggle source

If either left or right is an OUTER JOIN (predicate with a NULL argument) OR we are combining this with anything other than a column on the same translation table, we need to OUTER JOIN here. The only case where we can use an INNER JOIN is when we have predicates like this:

table.attribute1 = 'something' OR table.attribute2 = 'somethingelse'

Here, both columns are on the same table, and both are non-nil, so we can safely INNER JOIN. This is pretty subtle, think about it.

# File lib/mobility/backends/active_record/table.rb, line 220
def visit_Arel_Nodes_Or(object)
  visited = [object.left, object.right].map(&method(:visit))
  if visited.all? { |v| INNER_JOIN == v }
    INNER_JOIN
  elsif visited.any?
    OUTER_JOIN
  end
end
visit_Array(objects)
Alias for: visit_collection
visit_Mobility_Plugins_Arel_Attribute(object) click to toggle source
# File lib/mobility/backends/active_record/table.rb, line 229
def visit_Mobility_Plugins_Arel_Attribute(object)
  # We compare table names here to ensure that attributes defined on
  # different backends but the same table will correctly get an OUTER
  # join when required. Use options[:table_name] here since we don't
  # know if the other backend has a +table_name+ option accessor.
  (backend_class.table_name == object.backend_class.options[:table_name]) &&
    (locale == object.locale) && OUTER_JOIN || nil
end
visit_collection(objects) click to toggle source
# File lib/mobility/backends/active_record/table.rb, line 202
def visit_collection(objects)
  objects.map { |obj|
    visit(obj).tap { |visited| return visited if visited == INNER_JOIN }
  }.compact.first
end
Also aliased as: visit_Array