activerecord-refined 0.9.0 → 0.10.1
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/.yardopts +17 -0
- data/README.md +61 -1064
- data/activerecord-refined.gemspec +8 -7
- data/docs/conditions.md +206 -0
- data/docs/ctes.md +65 -0
- data/docs/expressions.md +125 -0
- data/docs/functions.md +219 -0
- data/docs/grouping.md +55 -0
- data/docs/index.md +70 -0
- data/docs/joins.md +73 -0
- data/docs/json.md +230 -0
- data/docs/ordering.md +55 -0
- data/docs/time_zones.md +30 -0
- data/docs/windows.md +41 -0
- data/docs/writing.md +33 -0
- data/examples/aggregations.rb +18 -0
- data/examples/expressions.rb +35 -5
- data/lib/active_record/refined/ast.rb +461 -246
- data/lib/active_record/refined/dialect/mariadb.rb +25 -0
- data/lib/active_record/refined/dialect/mysql.rb +18 -0
- data/lib/active_record/refined/dialect/mysql_compat.rb +67 -0
- data/lib/active_record/refined/dialect/oracle.rb +110 -0
- data/lib/active_record/refined/dialect/postgresql.rb +120 -0
- data/lib/active_record/refined/dialect/sql_server.rb +115 -0
- data/lib/active_record/refined/dialect/sqlite.rb +57 -0
- data/lib/active_record/refined/dialect.rb +340 -0
- data/lib/active_record/refined.rb +682 -192
- data/lib/activerecord-refined/version.rb +1 -1
- data/lib/activerecord-refined.rb +1 -0
- metadata +43 -16
- data/.github/workflows/push_gem.yml +0 -45
- data/.github/workflows/sandbox.yml +0 -295
- data/.github/workflows/test.yml +0 -104
- data/.gitignore +0 -19
- data/.rubocop.yml +0 -393
- data/Gemfile +0 -14
- data/Rakefile +0 -53
- data/benchmark/query_building.rb +0 -129
- data/test/test_block_syntax.rb +0 -2999
- data/test/test_helper.rb +0 -238
data/examples/expressions.rb
CHANGED
|
@@ -16,6 +16,7 @@ class Setup < ActiveRecord::Migration[8.1]
|
|
|
16
16
|
t.integer :price
|
|
17
17
|
t.integer :quantity
|
|
18
18
|
t.integer :flags
|
|
19
|
+
t.date :ordered_on
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
@@ -24,10 +25,16 @@ Setup.new.up
|
|
|
24
25
|
class LineItem < ActiveRecord::Base
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
LineItem.create!(sku: "A-1", category: "tools", price: 1200, quantity: 2, flags: 12
|
|
28
|
-
|
|
29
|
-
LineItem.create!(sku: "
|
|
30
|
-
|
|
28
|
+
LineItem.create!(sku: "A-1", category: "tools", price: 1200, quantity: 2, flags: 12,
|
|
29
|
+
ordered_on: Date.new(2026, 1, 5))
|
|
30
|
+
LineItem.create!(sku: "A-2", category: "tools", price: 300, quantity: 5, flags: 10,
|
|
31
|
+
ordered_on: Date.new(2026, 1, 20))
|
|
32
|
+
LineItem.create!(sku: "B-1", category: "paper", price: 80, quantity: 10, flags: 3,
|
|
33
|
+
ordered_on: Date.new(2026, 2, 3))
|
|
34
|
+
LineItem.create!(sku: "B-2", category: "paper", price: 80, quantity: 10, flags: 3,
|
|
35
|
+
ordered_on: Date.new(2026, 2, 3))
|
|
36
|
+
LineItem.create!(sku: "C-1", category: nil, price: 50, quantity: 1, flags: 4,
|
|
37
|
+
ordered_on: Date.new(2026, 2, 14))
|
|
31
38
|
|
|
32
39
|
def show(title, relation, rows = nil)
|
|
33
40
|
puts "--- #{title} ---"
|
|
@@ -64,6 +71,17 @@ show("a tax through BigDecimal, exact on the wire",
|
|
|
64
71
|
LineItem.select { [:sku, (BigDecimal("1.1") * :price).as(:taxed)] }.
|
|
65
72
|
map { |i| [i.sku, i.taxed] })
|
|
66
73
|
|
|
74
|
+
# A duration moves a date. Each adapter spells the move its own way; SQLite
|
|
75
|
+
# has date() and datetime(), and a date column keeps being a date.
|
|
76
|
+
show("a date moved by a duration",
|
|
77
|
+
LineItem.where { :ordered_on + 30.days < Date.new(2026, 2, 10) },
|
|
78
|
+
LineItem.where { :ordered_on + 30.days < Date.new(2026, 2, 10) }.pluck(:sku))
|
|
79
|
+
|
|
80
|
+
show("a due date a month on",
|
|
81
|
+
LineItem.select { [:sku, (:ordered_on + 1.month).as(:due_on)] },
|
|
82
|
+
LineItem.select { [:sku, (:ordered_on + 1.month).as(:due_on)] }.
|
|
83
|
+
map { |i| [i.sku, i.due_on] })
|
|
84
|
+
|
|
67
85
|
# The bitwise operators. & and | are AND and OR between conditions, which is
|
|
68
86
|
# what leaves them free here. Each expression parenthesises itself, so the
|
|
69
87
|
# grouping is Ruby's rather than the adapter's.
|
|
@@ -89,12 +107,17 @@ rescue ArgumentError => e
|
|
|
89
107
|
end
|
|
90
108
|
|
|
91
109
|
# 2. Aggregates. count takes :* for COUNT(*) and distinct: true for
|
|
92
|
-
# COUNT(DISTINCT ...); the rest are
|
|
110
|
+
# COUNT(DISTINCT ...), which sum and avg take too; the rest are min and max.
|
|
93
111
|
show("COUNT(*) and COUNT(DISTINCT ...)",
|
|
94
112
|
LineItem.select { [count(:*).as(:rows), count(:category, distinct: true).as(:categories)] },
|
|
95
113
|
LineItem.select { [count(:*).as(:rows), count(:category, distinct: true).as(:categories)] }.
|
|
96
114
|
map { |i| [i.rows, i.categories] })
|
|
97
115
|
|
|
116
|
+
show("SUM(DISTINCT ...), each quantity counted once",
|
|
117
|
+
LineItem.select { [sum(:quantity).as(:all), sum(:quantity, distinct: true).as(:once)] },
|
|
118
|
+
LineItem.select { [sum(:quantity).as(:all), sum(:quantity, distinct: true).as(:once)] }.
|
|
119
|
+
map { |i| [i.all, i.once] })
|
|
120
|
+
|
|
98
121
|
# filter takes the aggregate over the rows a condition holds for. SQLite and
|
|
99
122
|
# PostgreSQL have the FILTER clause; MySQL gets the CASE that means the same,
|
|
100
123
|
# since an aggregate passes over the NULL a missed row leaves.
|
|
@@ -173,6 +196,13 @@ show("NULLS LAST",
|
|
|
173
196
|
LineItem.order { [:category.asc.nulls_last, :sku.asc] },
|
|
174
197
|
LineItem.order { [:category.asc.nulls_last, :sku.asc] }.pluck(:category, :sku))
|
|
175
198
|
|
|
199
|
+
# collate names a collation -- how the database compares and orders strings --
|
|
200
|
+
# and gives back an expression, so it carries into a comparison or an order.
|
|
201
|
+
# The names are the database's own; nocase is SQLite's case-insensitive one.
|
|
202
|
+
show("a case-insensitive comparison under a collation",
|
|
203
|
+
LineItem.where { :category.collate(:nocase) == "TOOLS" },
|
|
204
|
+
LineItem.where { :category.collate(:nocase) == "TOOLS" }.pluck(:sku))
|
|
205
|
+
|
|
176
206
|
# Aggregates and expressions can be ordered by, too.
|
|
177
207
|
show("grouped, aggregated and ordered by the aggregate",
|
|
178
208
|
LineItem.
|