mvgijssel/arel_toolkit

View on GitHub
lib/arel/enhance/context_enhancer/arel_table.rb

Summary

Maintainability
A
0 mins
Test Coverage
B
86%
module Arel
  module Enhance
    module ContextEnhancer
      class ArelTable
        # rubocop:disable Metrics/PerceivedComplexity
        # rubocop:disable Metrics/CyclomaticComplexity
        # rubocop:disable Metrics/AbcSize
        def self.call(node)
          context = node.context.merge!(
            range_variable: false, column_reference: false, alias: false,
          )
          parent_object = node.parent.object

          # Using Arel::Table as SELECT ... FROM <table>
          if parent_object.is_a?(Arel::Nodes::JoinSource)
            context[:range_variable] = true

          # NOTE: only applies to ActiveRecord generated Arel
          # which does not use Arel::Table#alias but Arel::TableAlias instead
          # Using Arel::Table as SELECT ... FROM <table> AS alias
          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
            context[:range_variable] = true

          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
            context[:range_variable] = true

          # Using Arel::Table as SELECT ... FROM [<table>]
          elsif parent_object.is_a?(Array) &&
                node.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
            context[:range_variable] = true

          # NOTE: only applies to ActiveRecord generated Arel
          # which does not use Arel::Table#alias but Arel::TableAlias instead
          # Using Arel::Table as SELECT ... FROM [<table> AS alias]
          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.object.is_a?(Array) &&
                node.parent.parent.parent.object.is_a?(Arel::Nodes::JoinSource)
            context[:range_variable] = true

          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.object.is_a?(Arel::Attributes::Attribute)
            context[:column_reference] = true

          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.parent.object.is_a?(Arel::Attributes::Attribute)
            context[:column_reference] = true

          # Using Arel::Table as SELECT ... INNER JOIN <table> ON TRUE
          elsif parent_object.is_a?(Arel::Nodes::Join)
            context[:range_variable] = true

          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.object.is_a?(Arel::Nodes::Join)
            context[:range_variable] = true

          elsif parent_object.is_a?(Arel::Nodes::TableAlias) &&
                node.parent.parent.parent.object.is_a?(Arel::Nodes::Join)
            context[:range_variable] = true

          # Using Arel::Table as an attribute SELECT <table>.id ...
          elsif parent_object.is_a?(Arel::Attributes::Attribute)
            context[:column_reference] = true

          # Using Arel::Table in an INSERT INTO <table>
          elsif parent_object.is_a?(Arel::Nodes::InsertStatement)
            context[:range_variable] = true

          # Using Arel::Table in an UPDATE <table> ...
          elsif parent_object.is_a?(Arel::Nodes::UpdateStatement)
            context[:range_variable] = true

          # Arel::Table in UPDATE ... FROM [<table>]
          elsif parent_object.is_a?(Array) &&
                node.parent.parent.object.is_a?(Arel::Nodes::UpdateStatement)
            context[:range_variable] = true

          # Using Arel::Table in an DELETE FROM <table>
          elsif parent_object.is_a?(Arel::Nodes::DeleteStatement)
            context[:range_variable] = true

          # Arel::Table in DELETE ... USING [<table>]
          elsif parent_object.is_a?(Array) &&
                node.parent.parent.object.is_a?(Arel::Nodes::DeleteStatement)
            context[:range_variable] = true

          # Using Arel::Table as an "alias" for WITH <table> AS (SELECT 1) SELECT 1
          elsif parent_object.is_a?(Arel::Nodes::As) &&
                node.parent.parent.parent.object.is_a?(Arel::Nodes::With)
            context[:alias] = true

          # Resolves errors starting with "WITH" after an upgrade to activerecord 7:
          # WITH RECURSIVE "a" AS [...]
          # WITH "a" AS (SELECT 1) SELECT * FROM (WITH RECURSIVE "c" AS (SELECT 1) SELECT * FROM "c")
          elsif parent_object.is_a?(Arel::Nodes::With)
            context[:alias] = true

          # Using Arel::Table as an "alias" for WITH RECURSIVE <table> AS (SELECT 1) SELECT 1
          elsif parent_object.is_a?(Arel::Nodes::As) &&
                node.parent.parent.parent.object.is_a?(Arel::Nodes::WithRecursive)
            context[:alias] = true

          # Using Arel::Table as an "alias" for SELECT INTO <table> ...
          elsif parent_object.is_a?(Arel::Nodes::Into)
            context[:alias] = true

          else
            # You can "binding.pry" here to debug why this might happen if you
            # run into errors whilst upgrading
            raise "Unknown AST location for table #{node.inspect}, #{node.root_node.to_sql}"
          end
        end
        # rubocop:enable Metrics/PerceivedComplexity
        # rubocop:enable Metrics/CyclomaticComplexity
        # rubocop:enable Metrics/AbcSize
      end
    end
  end
end