active_record_extended 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/lib/active_record_extended/active_record.rb +1 -10
- data/lib/active_record_extended/active_record/relation_patch.rb +16 -1
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +22 -21
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +13 -16
- data/lib/active_record_extended/query_methods/select.rb +11 -10
- data/lib/active_record_extended/query_methods/unionize.rb +10 -4
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +4 -3
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- data/lib/active_record_extended/utilities/order_by.rb +9 -28
- data/lib/active_record_extended/utilities/support.rb +8 -15
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- data/spec/query_methods/json_spec.rb +5 -5
- data/spec/query_methods/select_spec.rb +13 -13
- data/spec/query_methods/unionize_spec.rb +5 -5
- data/spec/query_methods/with_cte_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
- data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
- data/spec/sql_inspections/either_sql_spec.rb +3 -3
- data/spec/sql_inspections/json_sql_spec.rb +0 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +12 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- metadata +18 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
| @@ -54,10 +54,10 @@ module ActiveRecordExtended | |
| 54 54 | 
             
                      elsif column.try(:array)
         | 
| 55 55 | 
             
                        Arel::Nodes::ContainsArray.new(arel.left, arel.right)
         | 
| 56 56 | 
             
                      else
         | 
| 57 | 
            -
                        raise ArgumentError | 
| 57 | 
            +
                        raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
         | 
| 58 58 | 
             
                      end
         | 
| 59 59 | 
             
                    else
         | 
| 60 | 
            -
                      raise ArgumentError | 
| 60 | 
            +
                      raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
         | 
| 61 61 | 
             
                    end
         | 
| 62 62 | 
             
                  end
         | 
| 63 63 | 
             
                end
         | 
| @@ -88,7 +88,7 @@ module ActiveRecordExtended | |
| 88 88 | 
             
                    when Arel::Nodes::Equality
         | 
| 89 89 | 
             
                      Arel::Nodes::Equality.new(arel.right, Arel::Nodes::NamedFunction.new(function_name, [arel.left]))
         | 
| 90 90 | 
             
                    else
         | 
| 91 | 
            -
                      raise ArgumentError | 
| 91 | 
            +
                      raise ArgumentError.new("Invalid argument for .where.#{function_name.downcase}(), got #{arel.class}")
         | 
| 92 92 | 
             
                    end
         | 
| 93 93 | 
             
                  end
         | 
| 94 94 | 
             
                end
         | 
| @@ -99,10 +99,18 @@ module ActiveRecordExtended | |
| 99 99 | 
             
                    when Arel::Nodes::In, Arel::Nodes::Equality
         | 
| 100 100 | 
             
                      arel_node_class.new(arel.left, arel.right)
         | 
| 101 101 | 
             
                    else
         | 
| 102 | 
            -
                      raise ArgumentError | 
| 102 | 
            +
                      raise ArgumentError.new("Invalid argument for .where.#{method}(), got #{arel.class}")
         | 
| 103 103 | 
             
                    end
         | 
| 104 104 | 
             
                  end
         | 
| 105 105 | 
             
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def build_where_clause_for(scope, opts, rest)
         | 
| 108 | 
            +
                  if ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR == 1
         | 
| 109 | 
            +
                    scope.send(:build_where_clause, opts, rest)
         | 
| 110 | 
            +
                  else
         | 
| 111 | 
            +
                    scope.send(:where_clause_factory).build(opts, rest)
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 106 114 | 
             
              end
         | 
| 107 115 | 
             
            end
         | 
| 108 116 |  | 
| @@ -112,9 +120,9 @@ module ActiveRecord | |
| 112 120 | 
             
                  prepend ActiveRecordExtended::WhereChain
         | 
| 113 121 |  | 
| 114 122 | 
             
                  def build_where_chain(opts, rest, &block)
         | 
| 115 | 
            -
                    where_clause = @scope | 
| 123 | 
            +
                    where_clause = build_where_clause_for(@scope, opts, rest)
         | 
| 116 124 | 
             
                    @scope.tap do |scope|
         | 
| 117 | 
            -
                      scope.references!(PredicateBuilder.references(opts)) if opts.is_a?(Hash)
         | 
| 125 | 
            +
                      scope.references!(PredicateBuilder.references(opts.stringify_keys)) if opts.is_a?(Hash)
         | 
| 118 126 | 
             
                      scope.where_clause += where_clause.modified_predicates(&block)
         | 
| 119 127 | 
             
                    end
         | 
| 120 128 | 
             
                  end
         | 
| @@ -16,7 +16,7 @@ module ActiveRecordExtended | |
| 16 16 | 
             
                      @scope.window_values! << {
         | 
| 17 17 | 
             
                        window_name:  to_arel_sql(@window_name),
         | 
| 18 18 | 
             
                        partition_by: flatten_to_sql(partitions),
         | 
| 19 | 
            -
                        order_by:     order_by_expression(order_by) | 
| 19 | 
            +
                        order_by:     order_by_expression(order_by)
         | 
| 20 20 | 
             
                      }
         | 
| 21 21 |  | 
| 22 22 | 
             
                      @scope
         | 
| @@ -81,8 +81,9 @@ module ActiveRecordExtended | |
| 81 81 |  | 
| 82 82 | 
             
                  def build_windows(arel)
         | 
| 83 83 | 
             
                    window_values.each do |window_value|
         | 
| 84 | 
            -
                      window = arel.window(window_value[:window_name]) | 
| 85 | 
            -
                      window. | 
| 84 | 
            +
                      window = arel.window(window_value[:window_name])
         | 
| 85 | 
            +
                      window = window.partition(window_value[:partition_by]) if window_value[:partition_by].present?
         | 
| 86 | 
            +
                      window.order(window_value[:order_by])                  if window_value[:order_by]
         | 
| 86 87 | 
             
                    end
         | 
| 87 88 | 
             
                  end
         | 
| 88 89 | 
             
                end
         | 
| @@ -3,78 +3,145 @@ | |
| 3 3 | 
             
            module ActiveRecordExtended
         | 
| 4 4 | 
             
              module QueryMethods
         | 
| 5 5 | 
             
                module WithCTE
         | 
| 6 | 
            -
                  class  | 
| 6 | 
            +
                  class WithCTE
         | 
| 7 | 
            +
                    include ::ActiveRecordExtended::Utilities::Support
         | 
| 8 | 
            +
                    include Enumerable
         | 
| 9 | 
            +
                    extend  Forwardable
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def_delegators :@with_values, :empty?, :blank?, :present?
         | 
| 12 | 
            +
                    attr_reader :with_values, :with_keys
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    # @param [ActiveRecord::Relation] scope
         | 
| 7 15 | 
             
                    def initialize(scope)
         | 
| 8 16 | 
             
                      @scope = scope
         | 
| 17 | 
            +
                      reset!
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    # @return [Enumerable] Returns the order for which CTE's were imported as.
         | 
| 21 | 
            +
                    def each
         | 
| 22 | 
            +
                      return to_enum(:each) unless block_given?
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      with_keys.each do |key|
         | 
| 25 | 
            +
                        yield(key, with_values[key])
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                    alias each_pair each
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    # @param [Hash, WithCTE] value
         | 
| 31 | 
            +
                    def with_values=(value)
         | 
| 32 | 
            +
                      reset!
         | 
| 33 | 
            +
                      pipe_cte_with!(value)
         | 
| 9 34 | 
             
                    end
         | 
| 10 35 |  | 
| 11 | 
            -
                     | 
| 36 | 
            +
                    # @param [Hash, WithCTE] value
         | 
| 37 | 
            +
                    def pipe_cte_with!(value)
         | 
| 38 | 
            +
                      return if value.nil? || value.empty?
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                      value.each_pair do |name, expression|
         | 
| 41 | 
            +
                        sym_name = name.to_sym
         | 
| 42 | 
            +
                        next if with_values.key?(sym_name)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                        # Ensure we follow FIFO pattern.
         | 
| 45 | 
            +
                        # If the parent has similar CTE alias keys, we want to favor the parent's expressions over its children's.
         | 
| 46 | 
            +
                        if expression.is_a?(ActiveRecord::Relation) && expression.with_values?
         | 
| 47 | 
            +
                          pipe_cte_with!(expression.cte)
         | 
| 48 | 
            +
                          expression.cte.reset!
         | 
| 49 | 
            +
                        end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                        @with_keys            |= [sym_name]
         | 
| 52 | 
            +
                        @with_values[sym_name] = expression
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      value.reset! if value.is_a?(WithCTE)
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    def reset!
         | 
| 59 | 
            +
                      @with_keys   = []
         | 
| 60 | 
            +
                      @with_values = {}
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  class WithChain
         | 
| 65 | 
            +
                    # @param [ActiveRecord::Relation] scope
         | 
| 66 | 
            +
                    def initialize(scope)
         | 
| 67 | 
            +
                      @scope       = scope
         | 
| 68 | 
            +
                      @scope.cte ||= WithCTE.new(scope)
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    # @param [Hash, WithCTE] args
         | 
| 72 | 
            +
                    def recursive(args)
         | 
| 12 73 | 
             
                      @scope.tap do |scope|
         | 
| 13 | 
            -
                        scope.with_values    += args
         | 
| 14 74 | 
             
                        scope.recursive_value = true
         | 
| 75 | 
            +
                        scope.cte.pipe_cte_with!(args)
         | 
| 15 76 | 
             
                      end
         | 
| 16 77 | 
             
                    end
         | 
| 17 78 | 
             
                  end
         | 
| 18 79 |  | 
| 19 | 
            -
                   | 
| 20 | 
            -
             | 
| 80 | 
            +
                  # @return [WithCTE]
         | 
| 81 | 
            +
                  def cte
         | 
| 82 | 
            +
                    @values[:cte]
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  # @param [WithCTE] cte
         | 
| 86 | 
            +
                  def cte=(cte)
         | 
| 87 | 
            +
                    raise TypeError.new("Must be a WithCTE class type") unless cte.is_a?(WithCTE)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    @values[:cte] = cte
         | 
| 21 90 | 
             
                  end
         | 
| 22 91 |  | 
| 92 | 
            +
                  # @return [Boolean]
         | 
| 23 93 | 
             
                  def with_values?
         | 
| 24 | 
            -
                    !( | 
| 94 | 
            +
                    !(cte.nil? || cte.empty?)
         | 
| 25 95 | 
             
                  end
         | 
| 26 96 |  | 
| 97 | 
            +
                  # @param [Hash, WithCTE] values
         | 
| 27 98 | 
             
                  def with_values=(values)
         | 
| 28 | 
            -
                     | 
| 99 | 
            +
                    cte.with_values = values
         | 
| 29 100 | 
             
                  end
         | 
| 30 101 |  | 
| 102 | 
            +
                  # @param [Boolean] value
         | 
| 31 103 | 
             
                  def recursive_value=(value)
         | 
| 32 104 | 
             
                    raise ImmutableRelation if @loaded
         | 
| 105 | 
            +
             | 
| 33 106 | 
             
                    @values[:recursive] = value
         | 
| 34 107 | 
             
                  end
         | 
| 35 108 |  | 
| 36 | 
            -
                   | 
| 37 | 
            -
             | 
| 109 | 
            +
                  # @return [Boolean]
         | 
| 110 | 
            +
                  def recursive_value?
         | 
| 111 | 
            +
                    !(!@values[:recursive])
         | 
| 38 112 | 
             
                  end
         | 
| 39 | 
            -
                  alias recursive_value? recursive_value
         | 
| 40 113 |  | 
| 114 | 
            +
                  # @param [Hash, WithCTE] opts
         | 
| 41 115 | 
             
                  def with(opts = :chain, *rest)
         | 
| 42 116 | 
             
                    return WithChain.new(spawn) if opts == :chain
         | 
| 117 | 
            +
             | 
| 43 118 | 
             
                    opts.blank? ? self : spawn.with!(opts, *rest)
         | 
| 44 119 | 
             
                  end
         | 
| 45 120 |  | 
| 46 | 
            -
                   | 
| 121 | 
            +
                  # @param [Hash, WithCTE] opts
         | 
| 122 | 
            +
                  def with!(opts = :chain, *_rest)
         | 
| 47 123 | 
             
                    return WithChain.new(self) if opts == :chain
         | 
| 48 | 
            -
                    self.with_values += [opts] + rest
         | 
| 49 | 
            -
                    self
         | 
| 50 | 
            -
                  end
         | 
| 51 124 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
                       | 
| 55 | 
            -
                        case expression
         | 
| 56 | 
            -
                        when String
         | 
| 57 | 
            -
                          Arel.sql("(#{expression})")
         | 
| 58 | 
            -
                        when ActiveRecord::Relation, Arel::SelectManager
         | 
| 59 | 
            -
                          Arel.sql("(#{expression.to_sql})")
         | 
| 60 | 
            -
                        end
         | 
| 61 | 
            -
                      next if select.nil?
         | 
| 62 | 
            -
                      Arel::Nodes::As.new(Arel.sql(PG::Connection.quote_ident(name.to_s)), select)
         | 
| 125 | 
            +
                    tap do |scope|
         | 
| 126 | 
            +
                      scope.cte ||= WithCTE.new(self)
         | 
| 127 | 
            +
                      scope.cte.pipe_cte_with!(opts)
         | 
| 63 128 | 
             
                    end
         | 
| 64 129 | 
             
                  end
         | 
| 65 130 |  | 
| 66 131 | 
             
                  def build_with(arel)
         | 
| 67 | 
            -
                     | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
                       | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                    end.compact
         | 
| 132 | 
            +
                    return unless with_values?
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    cte_statements = cte.map do |name, expression|
         | 
| 135 | 
            +
                      grouped_expression = cte.generate_grouping(expression)
         | 
| 136 | 
            +
                      cte_name           = cte.to_arel_sql(cte.double_quote(name.to_s))
         | 
| 137 | 
            +
                      Arel::Nodes::As.new(cte_name, grouped_expression)
         | 
| 138 | 
            +
                    end
         | 
| 75 139 |  | 
| 76 | 
            -
                     | 
| 77 | 
            -
             | 
| 140 | 
            +
                    if recursive_value?
         | 
| 141 | 
            +
                      arel.with(:recursive, cte_statements)
         | 
| 142 | 
            +
                    else
         | 
| 143 | 
            +
                      arel.with(cte_statements)
         | 
| 144 | 
            +
                    end
         | 
| 78 145 | 
             
                  end
         | 
| 79 146 | 
             
                end
         | 
| 80 147 | 
             
              end
         | 
| @@ -60,34 +60,15 @@ module ActiveRecordExtended | |
| 60 60 | 
             
                    end
         | 
| 61 61 | 
             
                  end
         | 
| 62 62 |  | 
| 63 | 
            -
                   | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
                      ordering_args.flatten!
         | 
| 73 | 
            -
                      ordering_args.compact!
         | 
| 74 | 
            -
                      ordering_args.map! do |arg|
         | 
| 75 | 
            -
                        next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
         | 
| 76 | 
            -
                        arg.each_with_object([]) do |(field, dir), ordering_object|
         | 
| 77 | 
            -
                          ordering_object << to_arel_sql(field).send(dir.to_s.downcase)
         | 
| 78 | 
            -
                        end
         | 
| 79 | 
            -
                      end.flatten!
         | 
| 80 | 
            -
                    end
         | 
| 81 | 
            -
                  else
         | 
| 82 | 
            -
                    def process_ordering_arguments!(ordering_args)
         | 
| 83 | 
            -
                      ordering_args.flatten!
         | 
| 84 | 
            -
                      ordering_args.compact!
         | 
| 85 | 
            -
                      ordering_args.map! do |arg|
         | 
| 86 | 
            -
                        next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
         | 
| 87 | 
            -
                        arg.each_with_object({}) do |(field, dir), ordering_obj|
         | 
| 88 | 
            -
                          # ActiveRecord will not reflect if the Hash keys are a `Arel::Nodes::SqlLiteral` klass
         | 
| 89 | 
            -
                          ordering_obj[to_arel_sql(field)] = dir.to_s.downcase
         | 
| 90 | 
            -
                        end
         | 
| 63 | 
            +
                  def process_ordering_arguments!(ordering_args)
         | 
| 64 | 
            +
                    ordering_args.flatten!
         | 
| 65 | 
            +
                    ordering_args.compact!
         | 
| 66 | 
            +
                    ordering_args.map! do |arg|
         | 
| 67 | 
            +
                      next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      arg.each_with_object({}) do |(field, dir), ordering_obj|
         | 
| 70 | 
            +
                        # ActiveRecord will not reflect if the Hash keys are a `Arel::Nodes::SqlLiteral` klass
         | 
| 71 | 
            +
                        ordering_obj[to_arel_sql(field)] = dir.to_s.downcase
         | 
| 91 72 | 
             
                      end
         | 
| 92 73 | 
             
                    end
         | 
| 93 74 | 
             
                  end
         | 
| @@ -20,7 +20,7 @@ module ActiveRecordExtended | |
| 20 20 |  | 
| 21 21 | 
             
                  def flatten_safely(values, &block)
         | 
| 22 22 | 
             
                    unless values.is_a?(Array)
         | 
| 23 | 
            -
                      values = yield values if  | 
| 23 | 
            +
                      values = yield values if block
         | 
| 24 24 | 
             
                      return [values]
         | 
| 25 25 | 
             
                    end
         | 
| 26 26 |  | 
| @@ -91,20 +91,12 @@ module ActiveRecordExtended | |
| 91 91 | 
             
                  def pipe_cte_with!(subquery)
         | 
| 92 92 | 
             
                    return self unless subquery.try(:with_values?)
         | 
| 93 93 |  | 
| 94 | 
            -
                     | 
| 95 | 
            -
                    subquery.with_values = nil # Remove nested queries with values
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                    # Add subquery's CTE's to the parents query stack. (READ THE SPECIAL NOTE ABOVE!)
         | 
| 94 | 
            +
                    # Add subquery CTE's to the parents query stack. (READ THE SPECIAL NOTE ABOVE!)
         | 
| 98 95 | 
             
                    if @scope.with_values?
         | 
| 99 | 
            -
                       | 
| 100 | 
            -
                      with_hash = cte_ary.each_with_object(@scope.with_values.first) do |from_cte, hash|
         | 
| 101 | 
            -
                        hash.reverse_merge!(from_cte)
         | 
| 102 | 
            -
                      end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                      @scope.with_values = [with_hash]
         | 
| 96 | 
            +
                      @scope.cte.pipe_cte_with!(subquery.cte)
         | 
| 105 97 | 
             
                    else
         | 
| 106 98 | 
             
                      # Top level has no with values
         | 
| 107 | 
            -
                      @scope.with!( | 
| 99 | 
            +
                      @scope.with!(subquery.cte)
         | 
| 108 100 | 
             
                    end
         | 
| 109 101 |  | 
| 110 102 | 
             
                    self
         | 
| @@ -143,13 +135,13 @@ module ActiveRecordExtended | |
| 143 135 | 
             
                  # Converts a potential subquery into a compatible Arel SQL node.
         | 
| 144 136 | 
             
                  #
         | 
| 145 137 | 
             
                  # Note:
         | 
| 146 | 
            -
                  # We convert relations to SQL to maintain compatibility with Rails 5. | 
| 138 | 
            +
                  # We convert relations to SQL to maintain compatibility with Rails 5.1.
         | 
| 147 139 | 
             
                  # Only Rails 5.2+ maintains bound attributes in Arel, so its better to be safe then sorry.
         | 
| 148 | 
            -
                  # When we drop support for Rails 5. | 
| 140 | 
            +
                  # When we drop support for Rails 5.1, we then can then drop the '.to_sql' conversation
         | 
| 149 141 |  | 
| 150 142 | 
             
                  def to_arel_sql(value)
         | 
| 151 143 | 
             
                    case value
         | 
| 152 | 
            -
                    when Arel::Node, Arel::Nodes::SqlLiteral, nil
         | 
| 144 | 
            +
                    when Arel::Nodes::Node, Arel::Nodes::SqlLiteral, nil
         | 
| 153 145 | 
             
                      value
         | 
| 154 146 | 
             
                    when ActiveRecord::Relation
         | 
| 155 147 | 
             
                      Arel.sql(value.spawn.to_sql)
         | 
| @@ -160,6 +152,7 @@ module ActiveRecordExtended | |
| 160 152 |  | 
| 161 153 | 
             
                  def group_when_needed(arel_or_rel_query)
         | 
| 162 154 | 
             
                    return arel_or_rel_query unless needs_to_be_grouped?(arel_or_rel_query)
         | 
| 155 | 
            +
             | 
| 163 156 | 
             
                    generate_grouping(arel_or_rel_query)
         | 
| 164 157 | 
             
                  end
         | 
| 165 158 |  | 
| @@ -61,7 +61,7 @@ RSpec.describe "Active Record Any / None of Methods" do | |
| 61 61 | 
             
                  it "Return matched records of a joined table on the parent level" do
         | 
| 62 62 | 
             
                    query = Tag.joins(:user).where.any_of(
         | 
| 63 63 | 
             
                      { users: { personal_id: 1 } },
         | 
| 64 | 
            -
                      { users: { personal_id: 3 } } | 
| 64 | 
            +
                      { users: { personal_id: 3 } }
         | 
| 65 65 | 
             
                    )
         | 
| 66 66 |  | 
| 67 67 | 
             
                    expect(query).to include(tag_one, tag_three)
         | 
| @@ -120,7 +120,7 @@ RSpec.describe "Active Record Any / None of Methods" do | |
| 120 120 | 
             
                  it "Return matched records of a joined table on the parent level" do
         | 
| 121 121 | 
             
                    query = Tag.joins(:user).where.none_of(
         | 
| 122 122 | 
             
                      { users: { personal_id: 1 } },
         | 
| 123 | 
            -
                      { users: { personal_id: 3 } } | 
| 123 | 
            +
                      { users: { personal_id: 3 } }
         | 
| 124 124 | 
             
                    )
         | 
| 125 125 |  | 
| 126 126 | 
             
                    expect(query).to include(tag_two)
         | 
| @@ -47,7 +47,7 @@ RSpec.describe "Active Record JSON methods" do | |
| 47 47 | 
             
                end
         | 
| 48 48 |  | 
| 49 49 | 
             
                it "allows for casting results in an aggregate-able Array function" do
         | 
| 50 | 
            -
                  query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results,  | 
| 50 | 
            +
                  query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results, cast_with: :array)
         | 
| 51 51 | 
             
                  expect(query.take.results).to be_a(Array).and(be_present)
         | 
| 52 52 | 
             
                  expect(query.take.results.first).to be_a(Hash)
         | 
| 53 53 | 
             
                end
         | 
| @@ -61,14 +61,14 @@ RSpec.describe "Active Record JSON methods" do | |
| 61 61 |  | 
| 62 62 | 
             
              describe ".json_build_object" do
         | 
| 63 63 | 
             
                let(:sub_query) do
         | 
| 64 | 
            -
                  User.select_row_to_json(from: User.select(:id),  | 
| 64 | 
            +
                  User.select_row_to_json(from: User.select(:id), cast_with: :array, as: :ids).where(id: user_one.id)
         | 
| 65 65 | 
             
                end
         | 
| 66 66 |  | 
| 67 67 | 
             
                it "defaults the column alias if one is not provided" do
         | 
| 68 68 | 
             
                  query = User.json_build_object(:personal, sub_query)
         | 
| 69 69 | 
             
                  expect(query.size).to eq(1)
         | 
| 70 70 | 
             
                  expect(query.take.results).to match(
         | 
| 71 | 
            -
                    "personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }])) | 
| 71 | 
            +
                    "personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }]))
         | 
| 72 72 | 
             
                  )
         | 
| 73 73 | 
             
                end
         | 
| 74 74 |  | 
| @@ -98,7 +98,7 @@ RSpec.describe "Active Record JSON methods" do | |
| 98 98 | 
             
                    :personal,
         | 
| 99 99 | 
             
                    sub_query.where.not(id: user_one),
         | 
| 100 100 | 
             
                    value: "COALESCE(array_agg(\"personal\"), '{}')",
         | 
| 101 | 
            -
                    as:    :cool_dudes | 
| 101 | 
            +
                    as:    :cool_dudes
         | 
| 102 102 | 
             
                  )
         | 
| 103 103 |  | 
| 104 104 | 
             
                  expect(query.take.cool_dudes["personal"]).to be_a(Array).and(be_empty)
         | 
| @@ -110,7 +110,7 @@ RSpec.describe "Active Record JSON methods" do | |
| 110 110 | 
             
                      :personal,
         | 
| 111 111 | 
             
                      sub_query.where.not(id: user_one),
         | 
| 112 112 | 
             
                      value: "COALESCE(array_agg(personal), '{}')",
         | 
| 113 | 
            -
                      as:    :cool_dudes | 
| 113 | 
            +
                      as:    :cool_dudes
         | 
| 114 114 | 
             
                    )
         | 
| 115 115 | 
             
                  end.to output.to_stderr
         | 
| 116 116 | 
             
                end
         | 
| @@ -15,7 +15,7 @@ RSpec.describe "Active Record Select Methods" do | |
| 15 15 | 
             
                    it "can accept a subquery" do
         | 
| 16 16 | 
             
                      subquery = Tag.select("count(*)").joins("JOIN users u ON tags.user_id = u.id").where("u.ip = users.ip")
         | 
| 17 17 | 
             
                      query    =
         | 
| 18 | 
            -
                        User.foster_select(tag_count: [subquery, cast_with: :array_agg, distinct: true])
         | 
| 18 | 
            +
                        User.foster_select(tag_count: [subquery, { cast_with: :array_agg, distinct: true }])
         | 
| 19 19 | 
             
                            .joins(:hm_tags)
         | 
| 20 20 | 
             
                            .group(:ip)
         | 
| 21 21 | 
             
                            .take
         | 
| @@ -25,8 +25,8 @@ RSpec.describe "Active Record Select Methods" do | |
| 25 25 |  | 
| 26 26 | 
             
                    it "can be ordered" do
         | 
| 27 27 | 
             
                      query = User.foster_select(
         | 
| 28 | 
            -
                        asc_ordered_numbers:  [:number, cast_with: :array_agg, order_by: { number: :asc }],
         | 
| 29 | 
            -
                        desc_ordered_numbers: [:number, cast_with: :array_agg, order_by: { number: :desc }] | 
| 28 | 
            +
                        asc_ordered_numbers:  [:number, { cast_with: :array_agg, order_by: { number: :asc } }],
         | 
| 29 | 
            +
                        desc_ordered_numbers: [:number, { cast_with: :array_agg, order_by: { number: :desc } }]
         | 
| 30 30 | 
             
                      ).take
         | 
| 31 31 |  | 
| 32 32 | 
             
                      expect(query.asc_ordered_numbers).to eq(number_set.to_a.sort)
         | 
| @@ -50,10 +50,10 @@ RSpec.describe "Active Record Select Methods" do | |
| 50 50 |  | 
| 51 51 | 
             
                    it "will return a boolean expression" do
         | 
| 52 52 | 
             
                      query = User.foster_select(
         | 
| 53 | 
            -
                        truthly_expr:     ["users.number > 0",   cast_with: :bool_and],
         | 
| 54 | 
            -
                        falsey_expr:      ["users.number > 200", cast_with: :bool_and],
         | 
| 55 | 
            -
                        other_true_expr:  ["users.number > 4",   cast_with: :bool_or],
         | 
| 56 | 
            -
                        other_false_expr: ["users.number > 6",   cast_with: :bool_or] | 
| 53 | 
            +
                        truthly_expr:     ["users.number > 0",   { cast_with: :bool_and }],
         | 
| 54 | 
            +
                        falsey_expr:      ["users.number > 200", { cast_with: :bool_and }],
         | 
| 55 | 
            +
                        other_true_expr:  ["users.number > 4",   { cast_with: :bool_or }],
         | 
| 56 | 
            +
                        other_false_expr: ["users.number > 6",   { cast_with: :bool_or }]
         | 
| 57 57 | 
             
                      ).take
         | 
| 58 58 |  | 
| 59 59 | 
             
                      expect(query.truthly_expr).to be_truthy
         | 
| @@ -67,19 +67,19 @@ RSpec.describe "Active Record Select Methods" do | |
| 67 67 | 
             
                    before { 2.times.flat_map { |i| Array.new(2) { |j| User.create!(number: (i + 1) * j + 3) } } }
         | 
| 68 68 |  | 
| 69 69 | 
             
                    it "max" do
         | 
| 70 | 
            -
                      query = User.foster_select(max_num: [:number, cast_with: :max]).take
         | 
| 70 | 
            +
                      query = User.foster_select(max_num: [:number, { cast_with: :max }]).take
         | 
| 71 71 | 
             
                      expect(query.max_num).to eq(5)
         | 
| 72 72 | 
             
                    end
         | 
| 73 73 |  | 
| 74 74 | 
             
                    it "min" do
         | 
| 75 | 
            -
                      query = User.foster_select(max_num: [:number, cast_with: :min]).take
         | 
| 75 | 
            +
                      query = User.foster_select(max_num: [:number, { cast_with: :min }]).take
         | 
| 76 76 | 
             
                      expect(query.max_num).to eq(3)
         | 
| 77 77 | 
             
                    end
         | 
| 78 78 |  | 
| 79 79 | 
             
                    it "sum" do
         | 
| 80 80 | 
             
                      query = User.foster_select(
         | 
| 81 | 
            -
                        num_sum:      [:number, cast_with: :sum],
         | 
| 82 | 
            -
                        distinct_sum: [:number, cast_with: :sum, distinct: true] | 
| 81 | 
            +
                        num_sum:      [:number, { cast_with: :sum }],
         | 
| 82 | 
            +
                        distinct_sum: [:number, { cast_with: :sum, distinct: true }]
         | 
| 83 83 | 
             
                      ).take
         | 
| 84 84 |  | 
| 85 85 | 
             
                      expect(query.num_sum).to eq(15)
         | 
| @@ -88,8 +88,8 @@ RSpec.describe "Active Record Select Methods" do | |
| 88 88 |  | 
| 89 89 | 
             
                    it "avg" do
         | 
| 90 90 | 
             
                      query = User.foster_select(
         | 
| 91 | 
            -
                        num_avg:      [:number, cast_with: :avg],
         | 
| 92 | 
            -
                        distinct_avg: [:number, cast_with: :avg, distinct: true] | 
| 91 | 
            +
                        num_avg:      [:number, { cast_with: :avg }],
         | 
| 92 | 
            +
                        distinct_avg: [:number, { cast_with: :avg, distinct: true }]
         | 
| 93 93 | 
             
                      ).take
         | 
| 94 94 |  | 
| 95 95 | 
             
                      expect(query.num_avg).to eq(3.75)
         |