Module: ActiveRecord::Refined::QueryMethods

Defined in:
lib/active_record/refined.rb

Instance Method Summary collapse

Instance Method Details

#distinct_on(*columns, &block) ⇒ Object

DISTINCT ON (...), which keeps the first row of each group the order brings up. PostgreSQL has it and the others do not; Arel carries the node and refuses to write it elsewhere, the way it does a regexp, so there is nothing for this to check:

Post.distinct_on { :author }.order { [:author, :likes.desc] }

The portable shape is a row_number window in a subquery, which the README shows.



422
423
424
# File 'lib/active_record/refined.rb', line 422

def distinct_on(*columns, &block)
  spawn.distinct_on!(*columns, &block)
end

#distinct_on!(*columns, &block) ⇒ Object



426
427
428
429
430
431
432
433
# File 'lib/active_record/refined.rb', line 426

def distinct_on!(*columns, &block)
  columns = Array(evaluate_block(&block)) if block
  if columns.empty?
    raise ArgumentError, "distinct_on needs a column or an expression"
  end
  self.distinct_on_values += columns
  self
end

#distinct_on_valuesObject

ActiveRecord generates these for the values it knows about; this one is ours, and lives in the same place so that it survives a spawn.



437
438
439
# File 'lib/active_record/refined.rb', line 437

def distinct_on_values
  @values.fetch(:distinct_on, ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY)
end

#distinct_on_values=(columns) ⇒ Object



441
442
443
444
# File 'lib/active_record/refined.rb', line 441

def distinct_on_values=(columns)
  assert_modifiable!
  @values[:distinct_on] = columns
end

#from(value, subquery_name = nil, as: nil) ⇒ Object

A symbol names a table, which ActiveRecord's own from only takes as a string. With as it is selected under another name; when that name is the model's own, from_cte says the same thing without repeating it.



373
374
375
376
377
378
379
380
381
382
383
# File 'lib/active_record/refined.rb', line 373

def from(value, subquery_name = nil, as: nil)
  unless value.is_a?(Symbol)
    if as
      raise ArgumentError, "as: needs the table named as a symbol"
    end
    return super(value, subquery_name)
  end
  arel_table = Arel::Table.new(value)
  arel_table = arel_table.alias(as) if as
  super(arel_table, subquery_name)
end

#from_cte(name) ⇒ Object

Selects a CTE in place of the model's own table. The alias is not a choice -- ActiveRecord keeps qualifying columns with the table name, so the model's is the only name that works -- which is why it is taken from the model rather than asked for: with_recursive(tree: [...]).from_cte(:tree)

The name is checked against what with declares, so that a typo is not a query against a table nobody has. Checked when the SQL is built, since the CTE may be declared after this in the chain, or by a scope merged into it.



395
396
397
398
399
400
401
402
# File 'lib/active_record/refined.rb', line 395

def from_cte(name)
  unless name.is_a?(Symbol)
    raise ArgumentError, "from_cte takes the CTE's name as a symbol"
  end
  relation = from(name, as: klass.table_name)
  relation.from_cte_value = name
  relation
end

#from_cte_valueObject



404
405
406
# File 'lib/active_record/refined.rb', line 404

def from_cte_value
  @values[:from_cte]
end

#from_cte_value=(name) ⇒ Object



408
409
410
411
# File 'lib/active_record/refined.rb', line 408

def from_cte_value=(name)
  assert_modifiable!
  @values[:from_cte] = name
end

#group(*args, &block) ⇒ Object



360
361
362
363
364
365
366
367
368
# File 'lib/active_record/refined.rb', line 360

def group(*args, &block)
  if block
    result = evaluate_block(&block)
    arel = Array(result).map {|node| to_arel_field(node) }
    super(*arel, &nil)
  else
    super
  end
end

#having(opts = nil, *rest, &block) ⇒ Object



342
343
344
345
346
347
348
# File 'lib/active_record/refined.rb', line 342

def having(opts = nil, *rest, &block)
  if block
    super(evaluate_block(&block).to_arel(table, klass))
  else
    super
  end
end

#joins(*args, as: nil, lateral: false, &block) ⇒ Object

as names the table within the query, which is what makes a self join expressible: joins(:employees, as: :managers) { ... }.

lateral joins a relation instead of a table, and lets it see the row being joined to -- the top few rows of each group, and the like.



451
452
453
454
455
456
457
458
459
460
# File 'lib/active_record/refined.rb', line 451

def joins(*args, as: nil, lateral: false, &block)
  if lateral
    super(build_lateral_join(args.first, Arel::Nodes::InnerJoin, as, &block))
  elsif block
    super(build_join_node(args.first, Arel::Nodes::InnerJoin, as, &block))
  else
    reject_join_alias(as)
    super(*args, &block)
  end
end

#left_outer_joins(*args, as: nil, lateral: false, &block) ⇒ Object



462
463
464
465
466
467
468
469
470
471
# File 'lib/active_record/refined.rb', line 462

def left_outer_joins(*args, as: nil, lateral: false, &block)
  if lateral
    joins(build_lateral_join(args.first, Arel::Nodes::OuterJoin, as, &block))
  elsif block
    joins(build_join_node(args.first, Arel::Nodes::OuterJoin, as, &block))
  else
    reject_join_alias(as)
    super(*args, &block)
  end
end

#order(*args, &block) ⇒ Object



350
351
352
353
354
355
356
357
358
# File 'lib/active_record/refined.rb', line 350

def order(*args, &block)
  if block
    result = evaluate_block(&block)
    arel = Array(result).map {|node| to_arel_field(node) }
    super(*arel, &nil)
  else
    super
  end
end

#select(*fields, &block) ⇒ Object



332
333
334
335
336
337
338
339
340
# File 'lib/active_record/refined.rb', line 332

def select(*fields, &block)
  if block
    result = evaluate_block(&block)
    arel = Array(result).map {|node| to_arel_field(node) }
    super(*arel, &nil)
  else
    super
  end
end

#where(opts = nil, *rest, &block) ⇒ Object



324
325
326
327
328
329
330
# File 'lib/active_record/refined.rb', line 324

def where(opts = nil, *rest, &block)
  if block
    super(evaluate_block(&block).to_arel(table, klass))
  else
    super
  end
end