rom-sql 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +16 -12
- data/CHANGELOG.md +23 -0
- data/Gemfile +11 -3
- data/README.md +1 -7
- data/lib/rom/sql.rb +4 -7
- data/lib/rom/sql/association.rb +1 -1
- data/lib/rom/sql/association/one_to_many.rb +44 -1
- data/lib/rom/sql/association/one_to_one.rb +1 -38
- data/lib/rom/sql/commands.rb +0 -3
- data/lib/rom/sql/commands/error_wrapper.rb +1 -1
- data/lib/rom/sql/errors.rb +4 -1
- data/lib/rom/sql/extensions.rb +19 -0
- data/lib/rom/sql/{support → extensions}/active_support_notifications.rb +0 -0
- data/lib/rom/sql/extensions/postgres.rb +3 -0
- data/lib/rom/sql/{commands/postgres.rb → extensions/postgres/commands.rb} +38 -0
- data/lib/rom/sql/extensions/postgres/inferrer.rb +64 -0
- data/lib/rom/sql/extensions/postgres/types.rb +65 -0
- data/lib/rom/sql/{support → extensions}/rails_log_subscriber.rb +0 -0
- data/lib/rom/sql/gateway.rb +15 -4
- data/lib/rom/sql/relation.rb +6 -2
- data/lib/rom/sql/relation/reading.rb +18 -0
- data/lib/rom/sql/schema/dsl.rb +7 -4
- data/lib/rom/sql/schema/inferrer.rb +44 -31
- data/lib/rom/sql/types.rb +5 -1
- data/lib/rom/sql/version.rb +1 -1
- data/rom-sql.gemspec +14 -13
- data/spec/extensions/postgres/inferrer_spec.rb +40 -0
- data/spec/extensions/postgres/integration_spec.rb +38 -0
- data/spec/extensions/postgres/types_spec.rb +115 -0
- data/spec/integration/association/many_to_many_spec.rb +2 -1
- data/spec/integration/association/one_to_one_spec.rb +6 -4
- data/spec/integration/combine_spec.rb +1 -1
- data/spec/integration/commands/create_spec.rb +46 -21
- data/spec/integration/commands/delete_spec.rb +13 -38
- data/spec/integration/commands/update_spec.rb +19 -41
- data/spec/integration/commands/upsert_spec.rb +1 -1
- data/spec/integration/gateway_spec.rb +5 -9
- data/spec/integration/migration_spec.rb +6 -7
- data/spec/integration/read_spec.rb +30 -38
- data/spec/integration/schema_inference_spec.rb +211 -49
- data/spec/integration/setup_spec.rb +5 -5
- data/spec/integration/support/active_support_notifications_spec.rb +4 -3
- data/spec/integration/support/rails_log_subscriber_spec.rb +5 -4
- data/spec/shared/database_setup.rb +21 -6
- data/spec/spec_helper.rb +44 -35
- data/spec/unit/association/one_to_many_spec.rb +20 -0
- data/spec/unit/association/one_to_one_spec.rb +23 -2
- data/spec/unit/association_errors_spec.rb +1 -1
- data/spec/unit/gateway_spec.rb +9 -8
- data/spec/unit/logger_spec.rb +1 -1
- data/spec/unit/migration_tasks_spec.rb +3 -3
- data/spec/unit/migrator_spec.rb +3 -2
- data/spec/unit/plugin/assoc_macros/combined_associations_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/many_to_many_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/many_to_one_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +1 -1
- data/spec/unit/relation/associations_spec.rb +27 -0
- data/spec/unit/relation/avg_spec.rb +11 -0
- data/spec/unit/relation/by_pk_spec.rb +15 -0
- data/spec/unit/relation/dataset_spec.rb +48 -0
- data/spec/unit/relation/distinct_spec.rb +14 -0
- data/spec/unit/relation/exclude_spec.rb +13 -0
- data/spec/unit/relation/fetch_spec.rb +21 -0
- data/spec/unit/relation/having_spec.rb +20 -0
- data/spec/unit/relation/inner_join_spec.rb +22 -0
- data/spec/unit/relation/inspect_spec.rb +11 -0
- data/spec/unit/relation/invert_spec.rb +12 -0
- data/spec/unit/relation/left_join_spec.rb +16 -0
- data/spec/unit/relation/map_spec.rb +16 -0
- data/spec/unit/relation/max_spec.rb +11 -0
- data/spec/unit/relation/min_spec.rb +11 -0
- data/spec/unit/relation/pluck_spec.rb +11 -0
- data/spec/unit/relation/prefix_spec.rb +27 -0
- data/spec/unit/relation/primary_key_spec.rb +27 -0
- data/spec/unit/relation/project_spec.rb +22 -0
- data/spec/unit/relation/qualified_columns_spec.rb +27 -0
- data/spec/unit/relation/rename_spec.rb +21 -0
- data/spec/unit/relation/sum_spec.rb +11 -0
- data/spec/unit/relation/union_spec.rb +19 -0
- data/spec/unit/relation/unique_predicate_spec.rb +18 -0
- data/spec/unit/schema_spec.rb +1 -1
- data/spec/unit/types_spec.rb +4 -21
- metadata +79 -11
- data/lib/rom/sql/commands_ext/postgres.rb +0 -45
- data/lib/rom/sql/types/pg.rb +0 -26
- data/spec/unit/relation_spec.rb +0 -272
@@ -1,7 +1,8 @@
|
|
1
1
|
RSpec.describe ROM::SQL::Association::ManyToMany do
|
2
2
|
include_context 'users and tasks'
|
3
3
|
|
4
|
-
|
4
|
+
# FIXME: Figure out what is wrong with sqlite
|
5
|
+
with_adapters(:postgres, :mysql) do
|
5
6
|
context 'with two associations pointing to the same target relation' do
|
6
7
|
let(:container) do
|
7
8
|
ROM.container(:sql, uri) do |conf|
|
@@ -25,30 +25,32 @@ RSpec.describe ROM::SQL::Association::OneToOne do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
describe '#call' do
|
28
|
-
it 'prepares joined relations' do
|
28
|
+
it 'prepares joined relations' do |example|
|
29
29
|
relation = assoc.call(container.relations)
|
30
30
|
|
31
31
|
expect(relation.attributes).to eql(%i[id user_id number balance])
|
32
32
|
|
33
33
|
# TODO: this if caluse should be removed when (and if) https://github.com/xerial/sqlite-jdbc/issues/112
|
34
34
|
# will be resolved. See https://github.com/rom-rb/rom-sql/issues/49 for details
|
35
|
-
if
|
35
|
+
if jruby? && sqlite?(example)
|
36
36
|
expect(relation.to_a).to eql([id: 1, user_id: 1, number: '42', balance: 10_000])
|
37
37
|
else
|
38
|
+
pending 'find out why mysql returns integer here' if !jruby? && mysql?(example)
|
38
39
|
expect(relation.to_a).to eql([id: 1, user_id: 1, number: '42', balance: 10_000.to_d])
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
44
|
describe ROM::Plugins::Relation::SQL::AutoCombine, '#for_combine' do
|
44
|
-
it 'preloads relation based on association' do
|
45
|
+
it 'preloads relation based on association' do |example|
|
45
46
|
relation = accounts.for_combine(assoc).call(users.call)
|
46
47
|
|
47
48
|
# TODO: this if caluse should be removed when (and if) https://github.com/xerial/sqlite-jdbc/issues/112
|
48
49
|
# will be resolved. See https://github.com/rom-rb/rom-sql/issues/49 for details
|
49
|
-
if
|
50
|
+
if jruby? && sqlite?(example)
|
50
51
|
expect(relation.to_a).to eql([id: 1, user_id: 1, number: '42', balance: 10_000])
|
51
52
|
else
|
53
|
+
pending 'find out why mysql returns integer here' if !jruby? && mysql?(example)
|
52
54
|
expect(relation.to_a).to eql([id: 1, user_id: 1, number: '42', balance: 10_000.to_d])
|
53
55
|
end
|
54
56
|
end
|
@@ -1,27 +1,31 @@
|
|
1
|
-
require '
|
1
|
+
require 'dry-struct'
|
2
2
|
|
3
|
-
RSpec.describe 'Commands / Create' do
|
3
|
+
RSpec.describe 'Commands / Create', :postgres do
|
4
4
|
include_context 'relations'
|
5
5
|
|
6
6
|
let(:users) { commands[:users] }
|
7
7
|
let(:tasks) { commands[:tasks] }
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
11
|
-
|
10
|
+
module Test
|
11
|
+
class Params < Dry::Struct
|
12
|
+
attribute :name, Types::Strict::String.optional
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
new(input)
|
14
|
+
def self.[](input)
|
15
|
+
new(input)
|
16
|
+
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
conn.add_index :users, :name, unique: true
|
21
21
|
|
22
|
+
conf.relation(:puppies) do
|
23
|
+
schema(infer: true)
|
24
|
+
end
|
25
|
+
|
22
26
|
conf.commands(:users) do
|
23
27
|
define(:create) do
|
24
|
-
input Params
|
28
|
+
input Test::Params
|
25
29
|
|
26
30
|
validator -> tuple {
|
27
31
|
raise ROM::CommandError, 'name cannot be empty' if tuple[:name] == ''
|
@@ -38,6 +42,10 @@ RSpec.describe 'Commands / Create' do
|
|
38
42
|
conf.commands(:tasks) do
|
39
43
|
define(:create)
|
40
44
|
end
|
45
|
+
|
46
|
+
conf.commands(:puppies) do
|
47
|
+
define(:create)
|
48
|
+
end
|
41
49
|
end
|
42
50
|
|
43
51
|
with_adapters do
|
@@ -183,6 +191,14 @@ RSpec.describe 'Commands / Create' do
|
|
183
191
|
}.to raise_error(ROM::SQL::NotNullConstraintError)
|
184
192
|
end
|
185
193
|
|
194
|
+
it 're-raises not-null constraint violation error with nil boolean' do
|
195
|
+
puppies = commands[:puppies]
|
196
|
+
|
197
|
+
expect {
|
198
|
+
puppies.try { puppies.create.call(name: 'Charlie', cute: nil) }
|
199
|
+
}.to raise_error(ROM::SQL::NotNullConstraintError)
|
200
|
+
end
|
201
|
+
|
186
202
|
it 're-raises uniqueness constraint violation error' do
|
187
203
|
expect {
|
188
204
|
users.try {
|
@@ -198,19 +214,19 @@ RSpec.describe 'Commands / Create' do
|
|
198
214
|
tasks.try {
|
199
215
|
tasks.create.call(user_id: 918_273_645)
|
200
216
|
}
|
201
|
-
}.to raise_error(ROM::SQL::ForeignKeyConstraintError
|
217
|
+
}.to raise_error(ROM::SQL::ForeignKeyConstraintError)
|
202
218
|
end
|
203
219
|
|
204
220
|
it 're-raises database errors' do
|
205
221
|
expect {
|
206
|
-
Params.attribute :bogus_field
|
222
|
+
Test::Params.attribute :bogus_field, Types::Int
|
207
223
|
users.try { users.create.call(name: 'some name', bogus_field: 23) }
|
208
224
|
}.to raise_error(ROM::SQL::DatabaseError)
|
209
225
|
end
|
210
226
|
|
211
227
|
it 'supports [] syntax instead of call' do
|
212
228
|
expect {
|
213
|
-
Params.attribute :bogus_field
|
229
|
+
Test::Params.attribute :bogus_field, Types::Int
|
214
230
|
users.try { users.create[name: 'some name', bogus_field: 23] }
|
215
231
|
}.to raise_error(ROM::SQL::DatabaseError)
|
216
232
|
end
|
@@ -259,29 +275,38 @@ RSpec.describe 'Commands / Create' do
|
|
259
275
|
conn.drop_table(:user_group)
|
260
276
|
end
|
261
277
|
|
262
|
-
|
263
|
-
|
278
|
+
# with a composite pk sequel returns 0 when inserting for MySQL
|
279
|
+
if !metadata[:mysql]
|
280
|
+
it 'materializes the result' do
|
281
|
+
command = container.commands[:user_group][:create]
|
282
|
+
result = command.call(user_id: 1, group_id: 2)
|
264
283
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
expect(result).to eql(user_id: 1, group_id: 2)
|
284
|
+
expect(result).to eql(user_id: 1, group_id: 2)
|
285
|
+
end
|
269
286
|
end
|
270
287
|
end
|
271
288
|
end
|
272
289
|
end
|
273
290
|
|
274
|
-
describe '#call'
|
275
|
-
it 're-raises check constraint violation error'
|
291
|
+
describe '#call' do
|
292
|
+
it 're-raises check constraint violation error' do
|
276
293
|
expect {
|
277
294
|
users.try {
|
278
295
|
users.create.call(name: 'J')
|
279
296
|
}
|
280
297
|
}.to raise_error(ROM::SQL::CheckConstraintError, /name/)
|
281
298
|
end
|
299
|
+
|
300
|
+
it 're-raises constraint violation error' do
|
301
|
+
expect {
|
302
|
+
users.try {
|
303
|
+
tasks.create.call(title: '')
|
304
|
+
}
|
305
|
+
}.to raise_error(ROM::SQL::ConstraintError, /title/)
|
306
|
+
end
|
282
307
|
end
|
283
308
|
|
284
|
-
describe '#upsert'
|
309
|
+
describe '#upsert' do
|
285
310
|
let(:task) { { title: 'task 1' } }
|
286
311
|
|
287
312
|
before { tasks.create.call(task) }
|
@@ -61,47 +61,22 @@ RSpec.describe 'Commands / Delete' do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
describe '#execute' do
|
64
|
-
context 'with
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
])
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
context 'with multiple records' do
|
75
|
-
it 'materializes the results' do
|
76
|
-
result = container.command(:users).delete.by_name(%w(Jade John)).execute
|
77
|
-
expect(result).to eq([
|
78
|
-
{ id: 3, name: 'Jade' },
|
79
|
-
{ id: 4, name: 'John' }
|
80
|
-
])
|
81
|
-
end
|
64
|
+
context 'with a single record' do
|
65
|
+
it 'materializes the result' do
|
66
|
+
result = container.command(:users).delete.by_name(%w(Jade)).execute
|
67
|
+
expect(result).to eq([
|
68
|
+
{ id: 3, name: 'Jade' }
|
69
|
+
])
|
82
70
|
end
|
83
71
|
end
|
84
72
|
|
85
|
-
context 'with
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
{ id: 3, name: 'Jade' }
|
93
|
-
])
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'with multiple records' do
|
98
|
-
it 'materializes the results' do
|
99
|
-
result = container.command(:users).delete.by_name(%w(Jade John)).execute
|
100
|
-
expect(result).to eq([
|
101
|
-
{ id: 3, name: 'Jade' },
|
102
|
-
{ id: 4, name: 'John' }
|
103
|
-
])
|
104
|
-
end
|
73
|
+
context 'with multiple records' do
|
74
|
+
it 'materializes the results' do
|
75
|
+
result = container.command(:users).delete.by_name(%w(Jade John)).execute
|
76
|
+
expect(result).to eq([
|
77
|
+
{ id: 3, name: 'Jade' },
|
78
|
+
{ id: 4, name: 'John' }
|
79
|
+
])
|
105
80
|
end
|
106
81
|
end
|
107
82
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'dry-struct'
|
2
2
|
|
3
3
|
RSpec.describe 'Commands / Update' do
|
4
4
|
include_context 'database setup'
|
@@ -37,6 +37,11 @@ RSpec.describe 'Commands / Update' do
|
|
37
37
|
|
38
38
|
context 'without a schema' do
|
39
39
|
before do
|
40
|
+
Test::User = Class.new(Dry::Struct) {
|
41
|
+
attribute :id, Types::Strict::Int
|
42
|
+
attribute :name, Types::Strict::String
|
43
|
+
}
|
44
|
+
|
40
45
|
conf.relation(:users) do
|
41
46
|
def by_id(id)
|
42
47
|
where(id: id).limit(1)
|
@@ -51,8 +56,6 @@ RSpec.describe 'Commands / Update' do
|
|
51
56
|
define(:update)
|
52
57
|
end
|
53
58
|
|
54
|
-
Test::User = Class.new { include Anima.new(:id, :name) }
|
55
|
-
|
56
59
|
conf.mappers do
|
57
60
|
register :users, entity: -> tuples { tuples.map { |tuple| Test::User.new(tuple) } }
|
58
61
|
end
|
@@ -122,47 +125,22 @@ RSpec.describe 'Commands / Update' do
|
|
122
125
|
end
|
123
126
|
|
124
127
|
describe '#execute' do
|
125
|
-
context 'with
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
])
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'with multiple records' do
|
136
|
-
it 'materializes the results' do
|
137
|
-
result = users.update.by_name(%w(Piotr Jane)).execute(name: 'Josie')
|
138
|
-
expect(result).to eq([
|
139
|
-
{ id: 1, name: 'Josie' },
|
140
|
-
{ id: 2, name: 'Josie' }
|
141
|
-
])
|
142
|
-
end
|
128
|
+
context 'with a single record' do
|
129
|
+
it 'materializes the result' do
|
130
|
+
result = users.update.by_name('Piotr').execute(name: 'Pete')
|
131
|
+
expect(result).to eq([
|
132
|
+
{ id: 1, name: 'Pete' }
|
133
|
+
])
|
143
134
|
end
|
144
135
|
end
|
145
136
|
|
146
|
-
context 'with
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
{ id: 1, name: 'Pete' }
|
154
|
-
])
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
context 'with multiple records' do
|
159
|
-
it 'materializes the results' do
|
160
|
-
result = users.update.by_name(%w(Piotr Jane)).execute(name: 'Josie')
|
161
|
-
expect(result).to eq([
|
162
|
-
{ id: 1, name: 'Josie' },
|
163
|
-
{ id: 2, name: 'Josie' }
|
164
|
-
])
|
165
|
-
end
|
137
|
+
context 'with multiple records' do
|
138
|
+
it 'materializes the results' do
|
139
|
+
result = users.update.by_name(%w(Piotr Jane)).execute(name: 'Josie')
|
140
|
+
expect(result).to eq([
|
141
|
+
{ id: 1, name: 'Josie' },
|
142
|
+
{ id: 2, name: 'Josie' }
|
143
|
+
])
|
166
144
|
end
|
167
145
|
end
|
168
146
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
describe ROM::SQL::Gateway do
|
2
|
-
|
3
|
-
let(:conn) { Sequel.connect(POSTGRES_DB_URI) }
|
1
|
+
RSpec.describe ROM::SQL::Gateway, :postgres, skip_tables: true do
|
2
|
+
include_context 'database setup'
|
4
3
|
|
4
|
+
describe 'migration' do
|
5
5
|
context 'creating migrations inline' do
|
6
6
|
subject(:gateway) { container.gateways[:default] }
|
7
7
|
|
8
8
|
let(:conf) { ROM::Configuration.new(:sql, conn) }
|
9
|
-
let
|
9
|
+
let(:container) { ROM.container(conf) }
|
10
10
|
|
11
11
|
after do
|
12
12
|
[:rabbits, :carrots].each do |name|
|
@@ -39,15 +39,13 @@ describe ROM::SQL::Gateway do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
context 'running migrations from a file system' do
|
42
|
-
include_context 'database setup'
|
43
|
-
|
44
42
|
let(:migration_dir) do
|
45
43
|
Pathname(__FILE__).dirname.join('../fixtures/migrations').realpath
|
46
44
|
end
|
47
45
|
|
48
46
|
let(:migrator) { ROM::SQL::Migration::Migrator.new(conn, path: migration_dir) }
|
49
47
|
let(:conf) { ROM::Configuration.new(:sql, [conn, migrator: migrator]) }
|
50
|
-
let
|
48
|
+
let(:container) { ROM.container(conf) }
|
51
49
|
|
52
50
|
it 'returns true for pending migrations' do
|
53
51
|
expect(container.gateways[:default].pending_migrations?).to be_truthy
|
@@ -65,8 +63,6 @@ describe ROM::SQL::Gateway do
|
|
65
63
|
end
|
66
64
|
|
67
65
|
context 'setting up' do
|
68
|
-
include_context 'database setup'
|
69
|
-
|
70
66
|
it 'skips settings up associations when tables are missing' do
|
71
67
|
conf = ROM::Configuration.new(:sql, uri) do |config|
|
72
68
|
config.relation(:foos) do
|
@@ -1,13 +1,12 @@
|
|
1
|
-
RSpec.describe ROM::SQL, '.migration' do
|
2
|
-
|
3
|
-
let(:conf) { ROM::Configuration.new(:sql, POSTGRES_DB_URI) }
|
1
|
+
RSpec.describe ROM::SQL, '.migration', :postgres, skip_tables: true do
|
2
|
+
include_context 'database setup'
|
4
3
|
|
5
4
|
before do
|
6
5
|
conf
|
7
|
-
|
6
|
+
conn.drop_table?(:dragons)
|
8
7
|
end
|
9
8
|
|
10
|
-
|
9
|
+
it 'creates a migration for a specific gateway' do
|
11
10
|
migration = ROM::SQL.migration do
|
12
11
|
change do
|
13
12
|
create_table :dragons do
|
@@ -17,8 +16,8 @@ RSpec.describe ROM::SQL, '.migration' do
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
migration.apply(
|
19
|
+
migration.apply(conn, :up)
|
21
20
|
|
22
|
-
expect(
|
21
|
+
expect(conn.table_exists?(:dragons)).to be(true)
|
23
22
|
end
|
24
23
|
end
|
@@ -1,37 +1,26 @@
|
|
1
|
-
require '
|
1
|
+
require 'dry-struct'
|
2
2
|
|
3
|
-
RSpec.describe 'Reading relations' do
|
3
|
+
RSpec.describe 'Reading relations using custom mappers' do
|
4
4
|
include_context 'users and tasks'
|
5
5
|
|
6
|
-
|
7
|
-
with_adapters(:postgres, :sqlite) do
|
6
|
+
with_adapters do
|
8
7
|
before :each do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
attribute :id, Integer
|
14
|
-
attribute :title, String
|
8
|
+
module Test
|
9
|
+
class Goal < Dry::Struct
|
10
|
+
attribute :id, Types::Strict::Int
|
11
|
+
attribute :title, Types::Strict::String
|
15
12
|
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class User
|
19
|
-
include Virtus.value_object(coerce: true)
|
20
13
|
|
21
|
-
|
22
|
-
attribute :id,
|
23
|
-
attribute :name, String
|
24
|
-
attribute :goals, Array
|
14
|
+
class User < Dry::Struct
|
15
|
+
attribute :id, Types::Strict::Int
|
16
|
+
attribute :name, Types::Strict::String
|
17
|
+
attribute :goals, Types::Strict::Array.member(Goal)
|
25
18
|
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class UserGoalCount
|
29
|
-
include Virtus.value_object(coerce: true)
|
30
19
|
|
31
|
-
|
32
|
-
attribute :id,
|
33
|
-
attribute :name, String
|
34
|
-
attribute :goal_count,
|
20
|
+
class UserGoalCount < Dry::Struct
|
21
|
+
attribute :id, Types::Strict::Int
|
22
|
+
attribute :name, Types::Strict::String
|
23
|
+
attribute :goal_count, Types::Strict::Int
|
35
24
|
end
|
36
25
|
end
|
37
26
|
|
@@ -80,10 +69,10 @@ RSpec.describe 'Reading relations' do
|
|
80
69
|
|
81
70
|
conf.mappers do
|
82
71
|
define(:users) do
|
83
|
-
model User
|
72
|
+
model Test::User
|
84
73
|
|
85
74
|
group :goals do
|
86
|
-
model Goal
|
75
|
+
model Test::Goal
|
87
76
|
|
88
77
|
attribute :id, from: :tasks_id
|
89
78
|
attribute :title
|
@@ -91,7 +80,7 @@ RSpec.describe 'Reading relations' do
|
|
91
80
|
end
|
92
81
|
|
93
82
|
define(:user_goal_counts) do
|
94
|
-
model UserGoalCount
|
83
|
+
model Test::UserGoalCount
|
95
84
|
end
|
96
85
|
end
|
97
86
|
end
|
@@ -100,20 +89,23 @@ RSpec.describe 'Reading relations' do
|
|
100
89
|
user = container.relation(:users).as(:users).with_goals.by_name('Jane').to_a.first
|
101
90
|
|
102
91
|
expect(user).to eql(
|
103
|
-
User.new(
|
104
|
-
id: 1, name: 'Jane', goals: [Goal.new(id: 2, title: "Jane's task")]
|
92
|
+
Test::User.new(
|
93
|
+
id: 1, name: 'Jane', goals: [Test::Goal.new(id: 2, title: "Jane's task")]
|
105
94
|
))
|
106
95
|
end
|
107
96
|
|
108
|
-
|
109
|
-
|
97
|
+
# FIXME: on mysql and sqlite
|
98
|
+
if metadata[:postgres]
|
99
|
+
it 'works with grouping and aggregates' do
|
100
|
+
container.relations[:goals].insert(id: 3, user_id: 1, title: 'Get Milk')
|
110
101
|
|
111
|
-
|
102
|
+
users_with_goal_count = container.relation(:user_goal_counts).as(:user_goal_counts).all
|
112
103
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
104
|
+
expect(users_with_goal_count.to_a).to eq([
|
105
|
+
Test::UserGoalCount.new(id: 1, name: "Jane", goal_count: 2),
|
106
|
+
Test::UserGoalCount.new(id: 2, name: "Joe", goal_count: 1)
|
107
|
+
])
|
108
|
+
end
|
117
109
|
end
|
118
110
|
end
|
119
111
|
end
|