class ActiveRecord::Relation

def build_arel(*args)

def build_arel(*args)
  arel = super
  build_with(arel) if @values[:with]
  arel
end

def build_with(arel) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity

rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
def build_with(arel) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
  return if with_values.empty?
  with_statements = with_values.map do |with_value|
    case with_value
    when String then Arel::Nodes::SqlLiteral.new(with_value)
    when Arel::Nodes::As then with_value
    when Hash then build_with_value_from_hash(with_value)
    when Array then build_with_value_from_array(with_value)
    else
      raise ArgumentError, "Unsupported argument type: #{with_value} #{with_value.class}"
    end
  end
  recursive_with? ? arel.with(:recursive, with_statements) : arel.with(with_statements)
end

def build_with_value_from_array(array)

def build_with_value_from_array(array)
  unless array.map(&:class).uniq == [Arel::Nodes::As]
    raise ArgumentError, "Unsupported argument type: #{array} #{array.class}"
  end
  array
end

def build_with_value_from_hash(hash) # rubocop:disable Metrics/MethodLength

rubocop:disable Metrics/MethodLength
def build_with_value_from_hash(hash) # rubocop:disable Metrics/MethodLength
  hash.map do |name, value|
    table = Arel::Table.new(name)
    expression = case value
                 when String then Arel::Nodes::SqlLiteral.new("(#{value})")
                 when ActiveRecord::Relation then value.arel
                 when Arel::SelectManager, Arel::Nodes::Union then value
                 else
                   raise ArgumentError, "Unsupported argument type: #{value} #{value.class}"
                 end
    Arel::Nodes::As.new(table, expression)
  end
end

def recursive_with=(value)

def recursive_with=(value)
  raise ImmutableRelation if @loaded
  @values[:recursive_with] = value
end

def recursive_with?

def recursive_with?
  @values[:recursive_with]
end

def with(opts, *rest)

def with(opts, *rest)
  spawn.with!(opts, *rest)
end

def with!(opts, *rest)

def with!(opts, *rest)
  if opts == :recursive
    self.recursive_with = true
    self.with_values += rest
  else
    self.with_values += [opts] + rest
  end
  self
end

def with_values

def with_values
  @values[:with] || []
end

def with_values=(values)

def with_values=(values)
  raise ImmutableRelation if @loaded
  @values[:with] = values
end