rom-sql 0.8.0 → 0.9.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/.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
|