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
# 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
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
# 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
# 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