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.
1085 1086 1087 1088 1089 1090 1091 |
# File 'lib/active_record/refined.rb', line 1085 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.
960 961 962 |
# File 'lib/active_record/refined.rb', line 960 def distinct_on(*columns, &block) spawn.distinct_on!(*columns, &block) end |
#distinct_on!(*columns, &block) ⇒ Object
#distinct_on on the relation itself.
965 966 967 968 969 970 971 972 |
# File 'lib/active_record/refined.rb', line 965 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.
905 906 907 908 909 910 911 912 913 914 915 |
# File 'lib/active_record/refined.rb', line 905 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.
931 932 933 934 935 936 937 938 |
# File 'lib/active_record/refined.rb', line 931 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.
1073 1074 1075 1076 1077 |
# File 'lib/active_record/refined.rb', line 1073 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.
884 885 886 887 888 889 890 891 892 |
# File 'lib/active_record/refined.rb', line 884 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.
857 858 859 860 861 862 863 |
# File 'lib/active_record/refined.rb', line 857 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.
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
# File 'lib/active_record/refined.rb', line 1025 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.
994 995 996 |
# File 'lib/active_record/refined.rb', line 994 def lateral spawn.lateral! end |
#lateral! ⇒ Object
#lateral on the relation itself.
999 1000 1001 1002 |
# File 'lib/active_record/refined.rb', line 999 def lateral! self.lateral_value = true self end |
#left_outer_joins(*args, as: nil, &block) ⇒ Object
LEFT OUTER JOIN, as #joins takes it.
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
# File 'lib/active_record/refined.rb', line 1041 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.
870 871 872 873 874 875 876 |
# File 'lib/active_record/refined.rb', line 870 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.
1064 1065 1066 1067 |
# File 'lib/active_record/refined.rb', line 1064 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.
845 846 847 848 849 850 851 |
# File 'lib/active_record/refined.rb', line 845 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 !.
832 833 834 835 836 837 838 |
# File 'lib/active_record/refined.rb', line 832 def where(opts = nil, *rest, &block) if block super(to_arel_condition(evaluate_block(&block))) else super end end |