activerecord-refined 0.5.0 → 0.6.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/.github/workflows/sandbox.yml +162 -26
- data/.github/workflows/test.yml +15 -2
- data/README.md +486 -52
- data/activerecord-refined.gemspec +3 -2
- data/examples/ctes.rb +41 -10
- data/examples/expressions.rb +71 -5
- data/examples/json.rb +94 -0
- data/examples/postgresql.rb +89 -6
- data/examples/predicates.rb +32 -5
- data/examples/windows.rb +96 -0
- data/examples/writes.rb +84 -0
- data/lib/active_record/refined/ast.rb +831 -94
- data/lib/active_record/refined.rb +351 -26
- data/lib/activerecord-refined/version.rb +1 -1
- data/lib/activerecord-refined.rb +7 -0
- data/test/test_block_syntax.rb +1100 -51
- data/test/test_helper.rb +82 -0
- metadata +4 -1
data/test/test_helper.rb
CHANGED
|
@@ -6,6 +6,7 @@ Bundler.require
|
|
|
6
6
|
|
|
7
7
|
require 'active_record'
|
|
8
8
|
require 'etc'
|
|
9
|
+
require 'json'
|
|
9
10
|
|
|
10
11
|
# Which database to generate SQL for. The tests only build queries, so any
|
|
11
12
|
# server reachable without further setup will do; the devcontainer runs
|
|
@@ -62,6 +63,66 @@ module SqlAssertions
|
|
|
62
63
|
skip "#{ADAPTER} has no regexp operator" unless REGEXP_OPERATORS.key?(ADAPTER)
|
|
63
64
|
end
|
|
64
65
|
|
|
66
|
+
# upsert_all wants to be told which unique index it is upserting against,
|
|
67
|
+
# except on MySQL, which does not accept being told.
|
|
68
|
+
def upsert_target
|
|
69
|
+
ADAPTER == 'mysql2' ? {} : {unique_by: :page}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# JSON containment: PostgreSQL has @>, MySQL JSON_CONTAINS, SQLite neither.
|
|
73
|
+
def skip_without_json_containment
|
|
74
|
+
skip "#{ADAPTER} has no JSON containment" if ADAPTER == 'sqlite3'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# MariaDB's json is a checked longtext, which ActiveRecord sees as a string
|
|
78
|
+
# and does not serialise a hash into -- what would go in is Ruby's inspect,
|
|
79
|
+
# which the check refuses. MySQL's json is a type of its own and takes the
|
|
80
|
+
# hash; handing that one a string would store the document as a JSON string
|
|
81
|
+
# rather than as the object, and every path would find nothing. Both answer
|
|
82
|
+
# to the mysql2 adapter, so the two have to be told apart.
|
|
83
|
+
def json_document(hash)
|
|
84
|
+
mariadb? ? JSON.generate(hash) : hash
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# How each adapter spells a cast to a whole number: MySQL casts to SIGNED
|
|
88
|
+
# and has no integer at all, PostgreSQL the other way about, SQLite either.
|
|
89
|
+
def integer_type
|
|
90
|
+
ADAPTER == 'mysql2' ? 'signed' : 'integer'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def mariadb?
|
|
94
|
+
ADAPTER == 'mysql2' && ActiveRecord::Base.connection.mariadb?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# GROUPING SETS, ROLLUP and CUBE are PostgreSQL's; MySQL has only WITH
|
|
98
|
+
# ROLLUP, which is a different clause, and SQLite has none.
|
|
99
|
+
def skip_without_grouping_sets
|
|
100
|
+
skip "#{ADAPTER} has no GROUPING SETS" unless ADAPTER == 'postgresql'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# LATERAL is PostgreSQL's and MySQL 8's; MariaDB and SQLite have none.
|
|
104
|
+
def skip_without_lateral
|
|
105
|
+
return if ADAPTER == 'postgresql'
|
|
106
|
+
skip "#{mariadb? ? 'MariaDB' : ADAPTER} has no LATERAL" if ADAPTER != 'mysql2' || mariadb?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# DISTINCT ON is PostgreSQL's; Arel refuses to write it for the others.
|
|
110
|
+
def skip_without_distinct_on
|
|
111
|
+
skip "#{ADAPTER} has no DISTINCT ON" unless ADAPTER == 'postgresql'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# ANY and ALL over a subquery are PostgreSQL's and MySQL's alike; SQLite
|
|
115
|
+
# has neither quantifier.
|
|
116
|
+
def skip_without_quantifiers
|
|
117
|
+
skip "#{ADAPTER} has no ANY or ALL" if ADAPTER == 'sqlite3'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# BIT_AND, BIT_OR and BIT_XOR are PostgreSQL's and MySQL's; SQLite has
|
|
121
|
+
# none of the three, nor BIT_COUNT.
|
|
122
|
+
def skip_without_bit_aggregates
|
|
123
|
+
skip "#{ADAPTER} has no bit aggregates" if ADAPTER == 'sqlite3'
|
|
124
|
+
end
|
|
125
|
+
|
|
65
126
|
def skip_without_array_columns
|
|
66
127
|
skip "#{ADAPTER} has no array columns" unless ADAPTER == 'postgresql'
|
|
67
128
|
end
|
|
@@ -114,20 +175,41 @@ end
|
|
|
114
175
|
class Node < ActiveRecord::Base
|
|
115
176
|
end
|
|
116
177
|
|
|
178
|
+
# For the writing statements, which need something to conflict with.
|
|
179
|
+
class Tally < ActiveRecord::Base
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# For the JSON tests; see json_document for how the two MySQLs differ.
|
|
183
|
+
class Doc < ActiveRecord::Base
|
|
184
|
+
end
|
|
185
|
+
|
|
117
186
|
class CreateAllTables < ActiveRecord::Migration[8.1]
|
|
118
187
|
def up
|
|
119
188
|
drop_table(:users, if_exists: true)
|
|
120
189
|
drop_table(:authors, if_exists: true)
|
|
121
190
|
drop_table(:posts, if_exists: true)
|
|
122
191
|
drop_table(:nodes, if_exists: true)
|
|
192
|
+
drop_table(:tallies, if_exists: true)
|
|
193
|
+
drop_table(:docs, if_exists: true)
|
|
123
194
|
create_table(:users) do |t|
|
|
124
195
|
t.string :name
|
|
125
196
|
t.integer :age
|
|
197
|
+
t.boolean :active
|
|
198
|
+
t.integer :flags
|
|
126
199
|
t.string :tags, array: true if ADAPTER == 'postgresql'
|
|
127
200
|
end
|
|
128
201
|
create_table(:authors) {|t| t.string :name}
|
|
129
202
|
create_table(:posts) {|t| t.string :title; t.integer :author_id}
|
|
130
203
|
create_table(:nodes) {|t| t.string :name; t.integer :parent_id}
|
|
204
|
+
create_table(:tallies) do |t|
|
|
205
|
+
t.string :page
|
|
206
|
+
t.integer :hits
|
|
207
|
+
t.index :page, unique: true
|
|
208
|
+
end
|
|
209
|
+
create_table(:docs) do |t|
|
|
210
|
+
t.string :name
|
|
211
|
+
ADAPTER == 'postgresql' ? t.jsonb(:meta) : t.json(:meta)
|
|
212
|
+
end
|
|
131
213
|
end
|
|
132
214
|
end
|
|
133
215
|
ActiveRecord::Migration.verbose = false
|
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.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shugo Maeda
|
|
@@ -94,9 +94,12 @@ files:
|
|
|
94
94
|
- examples/complex_joins.rb
|
|
95
95
|
- examples/ctes.rb
|
|
96
96
|
- examples/expressions.rb
|
|
97
|
+
- examples/json.rb
|
|
97
98
|
- examples/postgresql.rb
|
|
98
99
|
- examples/predicates.rb
|
|
99
100
|
- examples/subqueries.rb
|
|
101
|
+
- examples/windows.rb
|
|
102
|
+
- examples/writes.rb
|
|
100
103
|
- lib/active_record/refined.rb
|
|
101
104
|
- lib/active_record/refined/ast.rb
|
|
102
105
|
- lib/activerecord-refined.rb
|