Class: AppQuery::Result

Inherits:
ActiveRecord::Result
  • Object
show all
Defined in:
lib/app_query.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(columns, rows, overrides = nil, cast: false, row_builder: nil) ⇒ Result

Returns a new instance of Result.



200
201
202
203
204
205
206
# File 'lib/app_query.rb', line 200

def initialize(columns, rows, overrides = nil, cast: false, row_builder: nil)
  super(columns, rows, overrides)
  @cast = cast
  @row_builder = row_builder
  # Rails v6.1: prevent mutate on frozen object on #first
  @hash_rows = [] if columns.empty?
end

Instance Attribute Details

#castObject Also known as: cast?

Returns the value of attribute cast.



197
198
199
# File 'lib/app_query.rb', line 197

def cast
  @cast
end

#row_builderObject

Returns the value of attribute row_builder.



197
198
199
# File 'lib/app_query.rb', line 197

def row_builder
  @row_builder
end

Class Method Details

.from_ar_result(r, cast = nil, row_builder: nil) ⇒ Object



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/app_query.rb', line 295

def self.from_ar_result(r, cast = nil, row_builder: nil)
  if r.empty?
    r.columns.empty? ? EMPTY : new(r.columns, [], r.column_types, row_builder:)
  else
    cast &&= case cast
    when Array
      r.columns.zip(cast).to_h
    when Hash
      cast.transform_keys(&:to_s).transform_values { |v| resolve_cast_type(v) }
    else
      {}
    end
    if !cast || (cast.empty? && r.column_types.empty?)
      # nothing to cast
      new(r.columns, r.rows, r.column_types, row_builder:)
    else
      overrides = (r.column_types || {}).merge(cast)
      rows = r.cast_values(overrides)
      # One column is special :( ;(
      # > ActiveRecord::Base.connection.select_all("select array[1,2]").rows
      # => [["{1,2}"]]
      # > ActiveRecord::Base.connection.select_all("select array[1,2]").cast_values
      # => [[1, 2]]
      rows = rows.zip if r.columns.one?
      new(r.columns, rows, overrides, cast: true, row_builder:)
    end
  end
end

.resolve_cast_type(value) ⇒ Object

Resolves a cast type value, converting symbols to ActiveRecord types.

Examples:

resolve_cast_type(:date)  #=> ActiveRecord::Type::Date instance
resolve_cast_type(ActiveRecord::Type::Json.new)  #=> returns as-is

Parameters:

  • value (Symbol, Object)

    the cast type (symbol shorthand or type instance)

Returns:

  • (Object)

    the resolved type instance



286
287
288
289
290
291
292
293
# File 'lib/app_query.rb', line 286

def self.resolve_cast_type(value)
  case value
  when Symbol
    ActiveRecord::Type.lookup(value)
  else
    value
  end
end

Instance Method Details

#column(name = nil, unique: false) ⇒ Array

Note:

If you only need a single column, prefer Q#column which selects only that column from the database, avoiding fetching all columns.

Returns an array of values for a single column.

Examples:

Get values by column name

result.column(:name)        # => ["Alice", "Bob"]
result.column("name")       # => ["Alice", "Bob"]

Get first column (no name)

result.column               # => [1, 2, 3]

Get unique values

result.column(:status, unique: true)  # => ["active", "pending"]

Parameters:

  • name (String, Symbol, nil) (defaults to: nil)

    the column name (nil returns first column)

  • unique (Boolean) (defaults to: false)

    whether to return only unique values

Returns:

  • (Array)

    the column values

Raises:

  • (ArgumentError)

    if the column doesn't exist

See Also:



235
236
237
238
239
240
241
242
243
# File 'lib/app_query.rb', line 235

def column(name = nil, unique: false)
  return [] if empty?
  name = name&.to_s
  unless name.nil? || includes_column?(name)
    raise ArgumentError, "Unknown column #{name.inspect}. Should be one of #{columns.inspect}."
  end
  ix = name.nil? ? 0 : columns.index(name)
  rows.map { _1[ix] }.then { unique ? _1.uniq! : _1 }
end

#firstObject

AR::Result#first reads @hash_rows directly when not yet memoized, bypassing our hash_rows override. Force it through.



210
211
212
# File 'lib/app_query.rb', line 210

def first
  hash_rows.first
end

#sizeObject



245
246
247
# File 'lib/app_query.rb', line 245

def size
  count
end

#transform! {|Hash| ... } ⇒ self

Transforms each record in-place using the provided block.

Examples:

Add a computed field

result = AppQuery[:users].select_all
result.transform! { |r| r.merge("full_name" => "#{r['first']} #{r['last']}") }

Yields:

  • (Hash)

    each record as a hash with indifferent access

Yield Returns:

  • (Hash)

    the transformed record

Returns:

  • (self)

    the result object for chaining



273
274
275
276
# File 'lib/app_query.rb', line 273

def transform!
  @hash_rows = hash_rows.map { |r| yield(r) } unless empty?
  self
end