<commit_before>require_relative 'relation/distinct_on'
require_relative 'relation/auxiliary_statement'
require_relative 'relation/inheritance'

require_relative 'relation/merger'

module Torque
  module PostgreSQL
    module Relation
      extend ActiveSupport::Concern

      include DistinctOn
      include AuxiliaryStatement
      include Inheritance

      SINGLE_VALUE_METHODS = [:itself_only]
      MULTI_VALUE_METHODS = [:distinct_on, :auxiliary_statements, :cast_records]
      VALUE_METHODS = SINGLE_VALUE_METHODS + MULTI_VALUE_METHODS

      # Resolve column name when calculating models, allowing the column name to
      # be more complex while keeping the query selection quality
      def calculate(operation, column_name)
        column_name = resolve_column(column_name).first if column_name.is_a?(Hash)
        super(operation, column_name)
      end

      # Resolve column definition up to second value.
      # For example, based on Post model:
      #
      #   resolve_column(['name', :title])
      #   # Returns ['name', '"posts"."title"']
      #
      #   resolve_column([:title, {authors: :name}])
      #   # Returns ['"posts"."title"', '"authors"."name"']
      #
      #   resolve_column([{authors: [:name, :age]}])
      #   # Returns ['"authors"."name"', '"authors"."age"']
      def resolve_column(list, base = false)
        base = resolve_base_table(base)

        Array.wrap(list).map do |item|
          case item
          when String
            ::Arel::Nodes::SqlLiteral.new(klass.send(:sanitize_sql, item.to_s))
          when Symbol
            base ? base.arel_attribute(item) : klass.arel_attribute(item)
          when Array
            resolve_column(item, base)
          when Hash
            raise ArgumentError, "Unsupported Hash for attributes on third level" if base
            item.map do |key, other_list|
              other_list = [other_list] unless other_list.kind_of? Enumerable
              resolve_column(other_list, key)
            end
          else
            raise ArgumentError, "Unsupported argument type: #{value} (#{value.class})"
          end
        end.flatten
      end

      # Get the TableMetadata from a relation
      def resolve_base_table(relation)
        return unless relation

        table = predicate_builder.send(:table)
        if table.associated_with?(relation)
          table.associated_table(relation).send(:klass)
        else
          raise ArgumentError, "Relation for #{relation} not found on #{klass}"
        end
      end

      private

        def dynamic_selection
          @dynamic_selection ||= []
        end

        def build_arel(*)
          arel = super
          arel.project(*dynamic_selection) if select_values.blank? && dynamic_selection.any?
          arel
        end

        # Compatibility method with 5.0
        unless ActiveRecord::Relation.method_defined?(:get_value)
          def get_value(name)
            @values[name] || ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
          end<commit_msg>Turn dynamic selection accessible<commit_after>require_relative 'relation/distinct_on'
require_relative 'relation/auxiliary_statement'
require_relative 'relation/inheritance'

require_relative 'relation/merger'

module Torque
  module PostgreSQL
    module Relation
      extend ActiveSupport::Concern

      include DistinctOn
      include AuxiliaryStatement
      include Inheritance

      SINGLE_VALUE_METHODS = [:itself_only]
      MULTI_VALUE_METHODS = [:distinct_on, :auxiliary_statements, :cast_records]
      VALUE_METHODS = SINGLE_VALUE_METHODS + MULTI_VALUE_METHODS

      # Resolve column name when calculating models, allowing the column name to
      # be more complex while keeping the query selection quality
      def calculate(operation, column_name)
        column_name = resolve_column(column_name).first if column_name.is_a?(Hash)
        super(operation, column_name)
      end

      # Resolve column definition up to second value.
      # For example, based on Post model:
      #
      #   resolve_column(['name', :title])
      #   # Returns ['name', '"posts"."title"']
      #
      #   resolve_column([:title, {authors: :name}])
      #   # Returns ['"posts"."title"', '"authors"."name"']
      #
      #   resolve_column([{authors: [:name, :age]}])
      #   # Returns ['"authors"."name"', '"authors"."age"']
      def resolve_column(list, base = false)
        base = resolve_base_table(base)

        Array.wrap(list).map do |item|
          case item
          when String
            ::Arel::Nodes::SqlLiteral.new(klass.send(:sanitize_sql, item.to_s))
          when Symbol
            base ? base.arel_attribute(item) : klass.arel_attribute(item)
          when Array
            resolve_column(item, base)
          when Hash
            raise ArgumentError, "Unsupported Hash for attributes on third level" if base
            item.map do |key, other_list|
              other_list = [other_list] unless other_list.kind_of? Enumerable
              resolve_column(other_list, key)
            end
          else
            raise ArgumentError, "Unsupported argument type: #{value} (#{value.class})"
          end
        end.flatten
      end

      # Get the TableMetadata from a relation
      def resolve_base_table(relation)
        return unless relation

        table = predicate_builder.send(:table)
        if table.associated_with?(relation)
          table.associated_table(relation).send(:klass)
        else
          raise ArgumentError, "Relation for #{relation} not found on #{klass}"
        end
      end

      protected

        def dynamic_selection
          @dynamic_selection ||= []
        end

      private

        def build_arel(*)
          arel = super
          arel.project(*dynamic_selection) if select_values.blank? && dynamic_selection.any?
          arel
        end

        # Compatibility method with 5.0
        unless ActiveRecord::Relation.method_defined?(:get_value)
          def get_value(name)
            @values[name] || ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
          end
{'fraction_non_alphanumeric': 0.09083456304312182, 'fraction_numerical': 0.0006558452205279554, 'mean_word_length': 2.4777651083238315, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2470501', 'n_tokens_mistral': 1714, 'n_tokens_neox': 1660, 'n_words': 413}