activerecord-refined 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/ctes.md +2 -2
- data/docs/expressions.md +27 -13
- data/docs/functions.md +14 -10
- data/docs/index.md +1 -16
- data/docs/windows.md +2 -2
- data/examples/expressions.rb +13 -7
- data/lib/active_record/refined/ast.rb +330 -55
- data/lib/active_record/refined/dialect/mysql_compat.rb +16 -0
- data/lib/active_record/refined/dialect/mysqlish_json_functions.rb +45 -0
- data/lib/active_record/refined/dialect/oracle.rb +1 -7
- data/lib/active_record/refined/dialect/postgresql.rb +8 -0
- data/lib/active_record/refined/dialect/sql_server.rb +29 -5
- data/lib/active_record/refined/dialect/sqlite.rb +23 -0
- data/lib/active_record/refined/dialect.rb +132 -66
- data/lib/active_record/refined.rb +59 -22
- data/lib/activerecord-refined/version.rb +6 -1
- metadata +2 -1
|
@@ -27,8 +27,10 @@ module ActiveRecord
|
|
|
27
27
|
# @!parse include AST::Arithmetics
|
|
28
28
|
|
|
29
29
|
# @!method as(alias_name, quote: true)
|
|
30
|
-
# The column under an alias: `AS "name"`.
|
|
31
|
-
#
|
|
30
|
+
# The column under an alias: `AS "name"`. A number or a string takes
|
|
31
|
+
# it too, for a literal in a select list; {BlockContext#value} carries
|
|
32
|
+
# the literals that have no `as` of their own. The alias is quoted,
|
|
33
|
+
# so the name asked for is the name that comes back on every adapter;
|
|
32
34
|
# `quote: false` writes it bare, for a schema that wants the folding,
|
|
33
35
|
# and then it has to be a plain name.
|
|
34
36
|
# @param alias_name [Symbol, String]
|
|
@@ -36,6 +38,8 @@ module ActiveRecord
|
|
|
36
38
|
# @return [AST::As]
|
|
37
39
|
# @example
|
|
38
40
|
# Author.select { :name.as(:author) } # "authors"."name" AS "author"
|
|
41
|
+
# Node.select { [:id, 0.as(:depth)] } # 0 AS "depth"
|
|
42
|
+
# Post.select { [:title, "draft".as(:state)] } # 'draft' AS "state"
|
|
39
43
|
|
|
40
44
|
# @!method asc
|
|
41
45
|
# An ascending ordering, which takes `nulls_first` and `nulls_last`.
|
|
@@ -113,6 +117,31 @@ module ActiveRecord
|
|
|
113
117
|
AST::As.new(AST::Value.new(self), alias_name, quote: quote)
|
|
114
118
|
end
|
|
115
119
|
end
|
|
120
|
+
|
|
121
|
+
# true, false and nil are refined not for the query's sake but for the
|
|
122
|
+
# mistake's: their own & | ^ answer a bare boolean, so Ruby reads
|
|
123
|
+
# `:active == true & cond` as `:active == (true & cond)` and the
|
|
124
|
+
# condition vanishes without an error. Beside a column or a node of
|
|
125
|
+
# the query's they refuse instead; over plain values they stay Ruby's
|
|
126
|
+
# through super, so a flag computed in the block still computes.
|
|
127
|
+
[TrueClass, FalseClass, NilClass].each do |klass|
|
|
128
|
+
refine klass do
|
|
129
|
+
def &(other)
|
|
130
|
+
return super unless other.is_a?(::Symbol) || other.is_a?(AST::Node)
|
|
131
|
+
AST.refuse_ruby_operator(self, :&)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def |(other)
|
|
135
|
+
return super unless other.is_a?(::Symbol) || other.is_a?(AST::Node)
|
|
136
|
+
AST.refuse_ruby_operator(self, :|)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def ^(other)
|
|
140
|
+
return super unless other.is_a?(::Symbol) || other.is_a?(AST::Node)
|
|
141
|
+
AST.refuse_ruby_operator(self, :^)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
116
145
|
end
|
|
117
146
|
|
|
118
147
|
# What a block can call: the aggregates, the functions, CASE, and the
|
|
@@ -185,8 +214,8 @@ module ActiveRecord
|
|
|
185
214
|
|
|
186
215
|
# The rows of a group gathered into one JSON array, a value from each:
|
|
187
216
|
# `jsonb_agg` on PostgreSQL, `json_group_array` on SQLite,
|
|
188
|
-
# `JSON_ARRAYAGG`
|
|
189
|
-
# as a dug value does.
|
|
217
|
+
# `JSON_ARRAYAGG` on the MySQL family and Oracle; SQL Server has none.
|
|
218
|
+
# What it gives is JSON, which compares as a dug value does.
|
|
190
219
|
# @return [AST::JsonAggregate]
|
|
191
220
|
# @example
|
|
192
221
|
# Post.group { :author_id }.select { json_arrayagg(:title).as(:titles) }
|
|
@@ -195,7 +224,7 @@ module ActiveRecord
|
|
|
195
224
|
end
|
|
196
225
|
|
|
197
226
|
# The rows of a group gathered into one JSON object, a key and a value
|
|
198
|
-
# from each.
|
|
227
|
+
# from each, named as {#json_arrayagg} is; SQL Server has none.
|
|
199
228
|
# @return [AST::JsonAggregate]
|
|
200
229
|
# @example
|
|
201
230
|
# Post.select { json_objectagg(:title, :meta.dig(:stars)).as(:stars) }
|
|
@@ -219,7 +248,8 @@ module ActiveRecord
|
|
|
219
248
|
# @!endgroup
|
|
220
249
|
# @!group JSON
|
|
221
250
|
|
|
222
|
-
# A JSON array built in the row from the values given.
|
|
251
|
+
# A JSON array built in the row from the values given. SQL Server
|
|
252
|
+
# spells the pair its own way and is not carried yet.
|
|
223
253
|
# @return [AST::JsonBuild]
|
|
224
254
|
# @example
|
|
225
255
|
# Post.select { json_array(:title, :likes).as(:pair) }
|
|
@@ -228,7 +258,8 @@ module ActiveRecord
|
|
|
228
258
|
end
|
|
229
259
|
|
|
230
260
|
# A JSON object built in the row from a hash whose values are
|
|
231
|
-
# expressions.
|
|
261
|
+
# expressions. SQL Server spells the pair its own way and is not
|
|
262
|
+
# carried yet.
|
|
232
263
|
# @param pairs [Hash{Symbol, String => Object}]
|
|
233
264
|
# @return [AST::JsonBuild]
|
|
234
265
|
# @example
|
|
@@ -253,16 +284,16 @@ module ActiveRecord
|
|
|
253
284
|
# `ATAN(x)`.
|
|
254
285
|
# @return [AST::Function]
|
|
255
286
|
# @!method atan2(y, x)
|
|
256
|
-
# `ATAN2(y, x)
|
|
287
|
+
# `ATAN2(y, x)`: `ATN2` on SQL Server.
|
|
257
288
|
# @return [AST::Function]
|
|
258
289
|
# @!method ceil(x)
|
|
259
|
-
# `CEIL(x)
|
|
290
|
+
# `CEIL(x)`: `CEILING` on SQL Server.
|
|
260
291
|
# @return [AST::Function]
|
|
261
292
|
# @!method coalesce(*values)
|
|
262
293
|
# `COALESCE(a, b, ...)`: the first that is not NULL.
|
|
263
294
|
# @return [AST::Function]
|
|
264
295
|
# @!method concat(*strings)
|
|
265
|
-
# `CONCAT(a, b, ...)`.
|
|
296
|
+
# `CONCAT(a, b, ...)`. Oracle's takes exactly two.
|
|
266
297
|
# @return [AST::Function]
|
|
267
298
|
# @!method cos(x)
|
|
268
299
|
# `COS(x)`.
|
|
@@ -274,13 +305,15 @@ module ActiveRecord
|
|
|
274
305
|
# `FLOOR(x)`.
|
|
275
306
|
# @return [AST::Function]
|
|
276
307
|
# @!method length(string)
|
|
277
|
-
# `LENGTH(string)
|
|
308
|
+
# `LENGTH(string)`: `LEN` on SQL Server. What it counts is the
|
|
309
|
+
# family's own -- bytes on MySQL, characters elsewhere, and `LEN`
|
|
310
|
+
# leaves trailing spaces out; {#char_length} is the portable count.
|
|
278
311
|
# @return [AST::Function]
|
|
279
312
|
# @!method ln(x)
|
|
280
|
-
# `LN(x)
|
|
313
|
+
# `LN(x)`: `LOG` on SQL Server.
|
|
281
314
|
# @return [AST::Function]
|
|
282
315
|
# @!method log(base, x)
|
|
283
|
-
# `LOG(base, x)`.
|
|
316
|
+
# `LOG(base, x)`. SQL Server takes the arguments the other way round, and is refused.
|
|
284
317
|
# @return [AST::Function]
|
|
285
318
|
# @!method lower(string)
|
|
286
319
|
# `LOWER(string)`.
|
|
@@ -289,7 +322,7 @@ module ActiveRecord
|
|
|
289
322
|
# `LTRIM(string)`.
|
|
290
323
|
# @return [AST::Function]
|
|
291
324
|
# @!method mod(x, y)
|
|
292
|
-
# `MOD(x, y)`.
|
|
325
|
+
# `MOD(x, y)`. SQL Server has only the % operator.
|
|
293
326
|
# @return [AST::Function]
|
|
294
327
|
# @!method nullif(x, y)
|
|
295
328
|
# `NULLIF(x, y)`: NULL where the two are equal, x otherwise.
|
|
@@ -316,7 +349,7 @@ module ActiveRecord
|
|
|
316
349
|
# `SQRT(x)`.
|
|
317
350
|
# @return [AST::Function]
|
|
318
351
|
# @!method substr(string, from, length = nil)
|
|
319
|
-
# `SUBSTR(string, from, length)
|
|
352
|
+
# `SUBSTR(string, from, length)`: `SUBSTRING` on SQL Server, which insists on the length.
|
|
320
353
|
# @return [AST::Function]
|
|
321
354
|
# @!method tan(x)
|
|
322
355
|
# `TAN(x)`.
|
|
@@ -346,16 +379,16 @@ module ActiveRecord
|
|
|
346
379
|
# `LEAST(a, b, ...)`: `MIN` on SQLite.
|
|
347
380
|
# @return [AST::Function]
|
|
348
381
|
# @!method log2(x)
|
|
349
|
-
# `LOG2(x)`. PostgreSQL and Oracle have none
|
|
382
|
+
# `LOG2(x)`. PostgreSQL and Oracle have none -- `log(2, x)` is their spelling -- and SQL Server has neither.
|
|
350
383
|
# @return [AST::Function]
|
|
351
384
|
# @!method log10(x)
|
|
352
385
|
# `LOG10(x)`. Oracle has none.
|
|
353
386
|
# @return [AST::Function]
|
|
354
387
|
# @!method trunc(x, places = 0)
|
|
355
|
-
# `TRUNC(x, places)`: `TRUNCATE` on MySQL, which insists on the places.
|
|
388
|
+
# `TRUNC(x, places)`: `TRUNCATE` on MySQL, which insists on the places. SQL Server has none.
|
|
356
389
|
# @return [AST::Function]
|
|
357
390
|
# @!method now
|
|
358
|
-
# `NOW()`. SQLite and
|
|
391
|
+
# `NOW()`. SQLite, Oracle and SQL Server have none; {#current_timestamp} reaches all three.
|
|
359
392
|
# @return [AST::Function]
|
|
360
393
|
# @!method bit_and(column)
|
|
361
394
|
# `BIT_AND(column)`, an aggregate. PostgreSQL and MySQL have it.
|
|
@@ -792,9 +825,9 @@ module ActiveRecord
|
|
|
792
825
|
module QueryMethods
|
|
793
826
|
# `WHERE`, from a block: a condition built with the comparisons of
|
|
794
827
|
# {BlockSyntax}, combined with `&`, `|` and `!`.
|
|
795
|
-
# @yieldreturn [AST::Predicate]
|
|
828
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
796
829
|
# @example
|
|
797
|
-
# Author.where { :age >= 18 & :country.in?(%w[JP US]) }
|
|
830
|
+
# Author.where { (:age >= 18) & :country.in?(%w[JP US]) }
|
|
798
831
|
# Author.where { !:name.like?("A%") }
|
|
799
832
|
def where(opts = nil, *rest, &block)
|
|
800
833
|
if block
|
|
@@ -818,7 +851,7 @@ module ActiveRecord
|
|
|
818
851
|
end
|
|
819
852
|
|
|
820
853
|
# `HAVING`, from a block: a condition over the aggregates of a group.
|
|
821
|
-
# @yieldreturn [AST::Predicate]
|
|
854
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
822
855
|
# @example
|
|
823
856
|
# Author.group { :country }.having { count(:*) > 1 }
|
|
824
857
|
def having(opts = nil, *rest, &block)
|
|
@@ -957,7 +990,7 @@ module ActiveRecord
|
|
|
957
990
|
# subquery rather than the join. SQLite and MariaDB have none.
|
|
958
991
|
# @example
|
|
959
992
|
# top = Post.where { :posts[:author_id] == :authors[:id] }.order { :likes.desc }.limit(1)
|
|
960
|
-
# Author.left_outer_joins(top.lateral, as: :top)
|
|
993
|
+
# Author.left_outer_joins(top.lateral, as: :top).select { [:name, :top[:title]] }
|
|
961
994
|
def lateral
|
|
962
995
|
spawn.lateral!
|
|
963
996
|
end
|
|
@@ -985,6 +1018,7 @@ module ActiveRecord
|
|
|
985
1018
|
# table within the query, which is what makes a self join expressible.
|
|
986
1019
|
# Without a block it is Active Record's own `joins`.
|
|
987
1020
|
# @param as [Symbol, nil]
|
|
1021
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
988
1022
|
# @example
|
|
989
1023
|
# Author.joins(:posts) { :posts[:author_id] == :authors[:id] }
|
|
990
1024
|
# Employee.joins(:employees, as: :managers) { :managers[:id] == :employees[:manager_id] }
|
|
@@ -1001,6 +1035,7 @@ module ActiveRecord
|
|
|
1001
1035
|
|
|
1002
1036
|
# `LEFT OUTER JOIN`, as {#joins} takes it.
|
|
1003
1037
|
# @param as [Symbol, nil]
|
|
1038
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
1004
1039
|
# @example
|
|
1005
1040
|
# Author.left_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }
|
|
1006
1041
|
def left_outer_joins(*args, as: nil, &block)
|
|
@@ -1017,6 +1052,7 @@ module ActiveRecord
|
|
|
1017
1052
|
# `RIGHT OUTER JOIN`, as {#joins} takes it, of a table or a relation;
|
|
1018
1053
|
# an association name is not among what it takes.
|
|
1019
1054
|
# @param as [Symbol, nil]
|
|
1055
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
1020
1056
|
# @example
|
|
1021
1057
|
# Post.right_outer_joins(:authors) { :posts[:author_id] == :authors[:id] }
|
|
1022
1058
|
#
|
|
@@ -1033,6 +1069,7 @@ module ActiveRecord
|
|
|
1033
1069
|
# `FULL OUTER JOIN`, as {#right_outer_joins} takes it. The MySQL
|
|
1034
1070
|
# family has none.
|
|
1035
1071
|
# @param as [Symbol, nil]
|
|
1072
|
+
# @yieldreturn [AST::Predicate, AST::Sql, AST::Operation]
|
|
1036
1073
|
def full_outer_joins(*args, as: nil, &block)
|
|
1037
1074
|
check_full_outer_support
|
|
1038
1075
|
outer_joins(:full_outer_joins, Arel::Nodes::FullOuterJoin,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# The gem tooling's namespace, spelled as the gem name is, not as the
|
|
4
|
+
# ActiveRecord constant the gem extends; only the gemspec reads it.
|
|
5
|
+
# @private
|
|
3
6
|
module Activerecord
|
|
7
|
+
# @private
|
|
4
8
|
module Refined
|
|
5
|
-
|
|
9
|
+
# @private
|
|
10
|
+
VERSION = "0.11.0"
|
|
6
11
|
end
|
|
7
12
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-refined
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -272,6 +272,7 @@ files:
|
|
|
272
272
|
- lib/active_record/refined/dialect/mariadb.rb
|
|
273
273
|
- lib/active_record/refined/dialect/mysql.rb
|
|
274
274
|
- lib/active_record/refined/dialect/mysql_compat.rb
|
|
275
|
+
- lib/active_record/refined/dialect/mysqlish_json_functions.rb
|
|
275
276
|
- lib/active_record/refined/dialect/oracle.rb
|
|
276
277
|
- lib/active_record/refined/dialect/postgresql.rb
|
|
277
278
|
- lib/active_record/refined/dialect/sql_server.rb
|