Module: ActiveRecord::Refined::QueryMethods
- Defined in:
- lib/active_record/refined.rb
Overview
The relation methods a block reaches, prepended to Active Record's
own: where, select, having, order and group take a block
beside what they take already, the joins take one for the ON, and
from, from_cte, distinct_on and lateral are here for what
Active Record has no spelling for. Without a block each is Active
Record's own.
Instance Method Summary collapse
-
#cross_joins(*args, as: nil, &block) ⇒ Object
CROSS JOIN: every row of one table against every row of the other, so there is no condition to give and no block to write it in. -
#distinct_on(*columns, &block) ⇒ Object
SELECT DISTINCT ON (columns): the first row of each group the order brings up. -
#distinct_on!(*columns, &block) ⇒ Object
#distinct_on on the relation itself.
-
#from(value, subquery_name = nil, as: nil) ⇒ Object
FROM, with a table named as a symbol and, withas:, selected under another name; anything else is Active Record's ownfrom. -
#from_cte(name) ⇒ Object
Selects a CTE in place of the model's own table, under the model's own name, so that the columns Active Record qualifies still resolve.
-
#full_outer_joins(*args, as: nil, &block) ⇒ Object
FULL OUTER JOIN, as #right_outer_joins takes it. -
#group(*args, &block) ⇒ Object
GROUP BY, from a block: a column or an expression, an array of them, or one of BlockContext#grouping_sets, BlockContext#rollup and BlockContext#cube. -
#having(opts = nil, *rest, &block) ⇒ Object
HAVING, from a block: a condition over the aggregates of a group. -
#joins(*args, as: nil, &block) ⇒ Object
INNER JOIN, with theONfrom a block:joins(:posts) { ... }joins the table named,joins(relation) { ... }a subquery -- a lateral one when the relation is marked #lateral. -
#lateral ⇒ Object
Marks the relation for a
LATERALjoin, which lets the subquery see the row it is joined to -- the top few rows of each group, and the like. -
#lateral! ⇒ Object
#lateral on the relation itself.
-
#left_outer_joins(*args, as: nil, &block) ⇒ Object
LEFT OUTER JOIN, as #joins takes it. -
#order(*args, &block) ⇒ Object
ORDER BY, from a block: an ordering, or an array of them --:age.desc,count(:*).desc.nulls_last, or a bare column. -
#right_outer_joins(*args, as: nil, &block) ⇒ Object
RIGHT OUTER JOIN, as #joins takes it, of a table or a relation; an association name is not among what it takes. -
#select(*fields, &block) ⇒ Object
SELECT, from a block: an expression, or an array of them, each aliased withasor left to its own name. -
#where(opts = nil, *rest, &block) ⇒ Object
WHERE, from a block: a condition built with the comparisons of BlockSyntax, combined with&,|and!.
Instance Method Details
#cross_joins(*args, as: nil, &block) ⇒ Object
CROSS JOIN: every row of one table against every row of the
other, so there is no condition to give and no block to write it in.
1048 1049 1050 1051 1052 1053 1054 |
# File 'lib/active_record/refined.rb', line 1048 def cross_joins(*args, as: nil, &block) if block raise ArgumentError, "a cross join has no condition; joins is the one that takes a block" end joins(build_cross_join(args.first, as)) end |
#distinct_on(*columns, &block) ⇒ Object
SELECT DISTINCT ON (columns): the first row of each group the
order brings up. PostgreSQL has it; the portable shape is a
row_number window in a subquery.
Arel carries the node and refuses to write it elsewhere, the way it
does a regexp, so there is nothing for this to check.
927 928 929 |
# File 'lib/active_record/refined.rb', line 927 def distinct_on(*columns, &block) spawn.distinct_on!(*columns, &block) end |
#distinct_on!(*columns, &block) ⇒ Object
#distinct_on on the relation itself.
932 933 934 935 936 937 938 939 |
# File 'lib/active_record/refined.rb', line 932 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 |
#from(value, subquery_name = nil, as: nil) ⇒ Object
FROM, with a table named as a symbol and, with as:, selected
under another name; anything else is Active Record's own from.
A symbol names a table, which Active Record'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.
872 873 874 875 876 877 878 879 880 881 882 |
# File 'lib/active_record/refined.rb', line 872 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, under the model's
own name, so that the columns Active Record qualifies still resolve.
The name has to be one with or with_recursive declares.
The alias is not a choice -- Active Record 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.
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.
898 899 900 901 902 903 904 905 |
# File 'lib/active_record/refined.rb', line 898 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 |
#full_outer_joins(*args, as: nil, &block) ⇒ Object
FULL OUTER JOIN, as #right_outer_joins takes it. The MySQL
family has none.
1036 1037 1038 1039 1040 |
# File 'lib/active_record/refined.rb', line 1036 def full_outer_joins(*args, as: nil, &block) check_full_outer_support outer_joins(:full_outer_joins, Arel::Nodes::FullOuterJoin, args, as, &block) end |
#group(*args, &block) ⇒ Object
GROUP BY, from a block: a column or an expression, an array of
them, or one of BlockContext#grouping_sets, BlockContext#rollup
and BlockContext#cube.
851 852 853 854 855 856 857 858 859 |
# File 'lib/active_record/refined.rb', line 851 def group(*args, &block) if block result = evaluate_block(&block) check_rollup_stands_alone(result) super(*to_arel_fields(result), &nil) else super end end |
#having(opts = nil, *rest, &block) ⇒ Object
HAVING, from a block: a condition over the aggregates of a group.
824 825 826 827 828 829 830 |
# File 'lib/active_record/refined.rb', line 824 def having(opts = nil, *rest, &block) if block super(to_arel_condition(evaluate_block(&block))) else super end end |
#joins(*args, as: nil, &block) ⇒ Object
INNER JOIN, with the ON from a block: joins(:posts) { ... }
joins the table named, joins(relation) { ... } a subquery -- a
lateral one when the relation is marked #lateral. as: names the
table within the query, which is what makes a self join expressible.
Without a block it is Active Record's own joins.
991 992 993 994 995 996 997 998 999 1000 |
# File 'lib/active_record/refined.rb', line 991 def joins(*args, as: nil, &block) if args.first.is_a?(ActiveRecord::Relation) 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 |
#lateral ⇒ Object
Marks the relation for a LATERAL join, which lets the subquery see
the row it is joined to -- the top few rows of each group, and the
like. Said on the relation, since in SQL the keyword modifies the
subquery rather than the join. SQLite and MariaDB have none.
961 962 963 |
# File 'lib/active_record/refined.rb', line 961 def lateral spawn.lateral! end |
#lateral! ⇒ Object
#lateral on the relation itself.
966 967 968 969 |
# File 'lib/active_record/refined.rb', line 966 def lateral! self.lateral_value = true self end |
#left_outer_joins(*args, as: nil, &block) ⇒ Object
LEFT OUTER JOIN, as #joins takes it.
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 |
# File 'lib/active_record/refined.rb', line 1006 def left_outer_joins(*args, as: nil, &block) if args.first.is_a?(ActiveRecord::Relation) 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
ORDER BY, from a block: an ordering, or an array of them --
:age.desc, count(:*).desc.nulls_last, or a bare column.
837 838 839 840 841 842 843 |
# File 'lib/active_record/refined.rb', line 837 def order(*args, &block) if block super(*to_arel_fields(evaluate_block(&block)), &nil) else super end end |
#right_outer_joins(*args, as: nil, &block) ⇒ Object
RIGHT OUTER JOIN, as #joins takes it, of a table or a relation;
an association name is not among what it takes.
The other two outer joins, which Active Record has no method for and
Arel has the nodes for. The rules are joins': the block is the ON,
as names the table within the query, a relation marked lateral
joins as one. An association name is not among them -- what Active
Record reads out of one is an inner or a left join and nothing else.
1028 1029 1030 1031 |
# File 'lib/active_record/refined.rb', line 1028 def right_outer_joins(*args, as: nil, &block) outer_joins(:right_outer_joins, Arel::Nodes::RightOuterJoin, args, as, &block) end |
#select(*fields, &block) ⇒ Object
SELECT, from a block: an expression, or an array of them, each
aliased with as or left to its own name.
812 813 814 815 816 817 818 |
# File 'lib/active_record/refined.rb', line 812 def select(*fields, &block) if block super(*to_arel_fields(evaluate_block(&block)), &nil) else super end end |
#where(opts = nil, *rest, &block) ⇒ Object
WHERE, from a block: a condition built with the comparisons of
BlockSyntax, combined with &, | and !.
799 800 801 802 803 804 805 |
# File 'lib/active_record/refined.rb', line 799 def where(opts = nil, *rest, &block) if block super(to_arel_condition(evaluate_block(&block))) else super end end |