rom-sql 2.0.0.beta1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -1
  3. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +19 -6
  4. data/lib/rom/sql/attribute.rb +9 -0
  5. data/lib/rom/sql/commands/create.rb +0 -2
  6. data/lib/rom/sql/commands/delete.rb +0 -2
  7. data/lib/rom/sql/commands/update.rb +0 -2
  8. data/lib/rom/sql/extensions/postgres/attributes_inferrer.rb +1 -1
  9. data/lib/rom/sql/index.rb +16 -0
  10. data/lib/rom/sql/migration/inline_runner.rb +12 -4
  11. data/lib/rom/sql/migration/schema_diff.rb +23 -3
  12. data/lib/rom/sql/plugin/associates.rb +3 -3
  13. data/lib/rom/sql/plugin/timestamps.rb +2 -2
  14. data/lib/rom/sql/relation.rb +7 -0
  15. data/lib/rom/sql/relation/reading.rb +1 -1
  16. data/lib/rom/sql/schema.rb +2 -1
  17. data/lib/rom/sql/schema/dsl.rb +26 -0
  18. data/lib/rom/sql/schema/index_dsl.rb +50 -0
  19. data/lib/rom/sql/schema/inferrer.rb +11 -5
  20. data/lib/rom/sql/version.rb +1 -1
  21. data/spec/integration/auto_migrations/indexes_spec.rb +147 -3
  22. data/spec/integration/{graph_spec.rb → combine_with_spec.rb} +1 -1
  23. data/spec/integration/commands/create_spec.rb +37 -62
  24. data/spec/integration/commands/delete_spec.rb +6 -6
  25. data/spec/integration/commands/update_spec.rb +6 -8
  26. data/spec/integration/plugins/associates/many_to_many_spec.rb +2 -2
  27. data/spec/integration/plugins/associates_spec.rb +7 -7
  28. data/spec/integration/plugins/auto_restrictions_spec.rb +31 -0
  29. data/spec/integration/relation_schema_spec.rb +68 -0
  30. data/spec/integration/schema/inferrer_spec.rb +31 -6
  31. data/spec/support/helpers.rb +1 -1
  32. data/spec/unit/plugin/timestamp_spec.rb +2 -2
  33. data/spec/unit/types_spec.rb +1 -1
  34. metadata +6 -5
  35. data/lib/rom/sql/commands/transaction.rb +0 -33
@@ -1,5 +1,5 @@
1
1
  module ROM
2
2
  module SQL
3
- VERSION = '2.0.0.beta1'.freeze
3
+ VERSION = '2.0.0.beta2'.freeze
4
4
  end
5
5
  end
@@ -6,6 +6,7 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
6
6
  end
7
7
 
8
8
  let(:table_name) { :users }
9
+ let(:relation_name) { ROM::Relation::Name.new(table_name) }
9
10
 
10
11
  subject(:gateway) { container.gateways[:default] }
11
12
 
@@ -18,6 +19,14 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
18
19
 
19
20
  let(:attributes) { migrated_schema.to_a }
20
21
 
22
+ def indexdef(index)
23
+ gateway.connection[<<-SQL, index].first[:indexdef]
24
+ select indexdef
25
+ from pg_indexes
26
+ where indexname = ?
27
+ SQL
28
+ end
29
+
21
30
  describe 'create table' do
22
31
  describe 'one-column indexes' do
23
32
  before do
@@ -25,6 +34,10 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
25
34
  schema do
26
35
  attribute :id, ROM::SQL::Types::Serial
27
36
  attribute :name, ROM::SQL::Types::String.meta(index: true)
37
+
38
+ indexes do
39
+ index :name, name: :unique_name, unique: true
40
+ end
28
41
  end
29
42
  end
30
43
  end
@@ -43,6 +56,13 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
43
56
  [:definition, [String, {}]],
44
57
  source: :users]],
45
58
  ])
59
+
60
+ expect(migrated_schema.indexes.first).
61
+ to eql(ROM::SQL::Index.new(
62
+ [define_attribute(:name, :String, source: relation_name)],
63
+ name: :unique_name,
64
+ unique: true
65
+ ))
46
66
  end
47
67
  end
48
68
  end
@@ -51,23 +71,54 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
51
71
  describe 'one-column indexes' do
52
72
  context 'adding' do
53
73
  before do
74
+ end
75
+
76
+ it 'adds indexed column' do
77
+ conn.create_table :users do
78
+ primary_key :id
79
+ end
80
+
54
81
  conf.relation(:users) do
55
82
  schema do
56
83
  attribute :id, ROM::SQL::Types::Serial
57
84
  attribute :name, ROM::SQL::Types::String.meta(index: true)
58
85
  end
59
86
  end
87
+
88
+ gateway.auto_migrate!(conf)
89
+
90
+ name_index = migrated_schema.indexes.first
91
+
92
+ expect(migrated_schema.attributes[1].name).to eql(:name)
93
+ expect(migrated_schema.indexes.size).to eql(1)
94
+ expect(name_index.name).to eql(:users_name_index)
95
+ expect(name_index.attributes.map(&:name)).to eql(%i(name))
60
96
  end
61
97
 
62
- it 'adds indexed column' do
98
+ it 'supports custom names' do
63
99
  conn.create_table :users do
64
100
  primary_key :id
65
101
  end
66
102
 
103
+ conf.relation(:users) do
104
+ schema do
105
+ attribute :id, ROM::SQL::Types::Serial
106
+ attribute :name, ROM::SQL::Types::String
107
+
108
+ indexes do
109
+ index :name, name: :custom_idx
110
+ end
111
+ end
112
+ end
113
+
67
114
  gateway.auto_migrate!(conf)
68
115
 
116
+ name_index = migrated_schema.indexes.first
117
+
69
118
  expect(migrated_schema.attributes[1].name).to eql(:name)
70
- expect(migrated_schema.indexes.map { |idx| idx.attributes.map(&:name) }).to eql([%i(name)])
119
+ expect(migrated_schema.indexes.size).to eql(1)
120
+ expect(name_index.name).to eql(:custom_idx)
121
+ expect(name_index.attributes.map(&:name)).to eql(%i(name))
71
122
  end
72
123
 
73
124
  it 'adds index to existing column' do
@@ -76,9 +127,98 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
76
127
  column :name, String
77
128
  end
78
129
 
130
+ conf.relation(:users) do
131
+ schema do
132
+ attribute :id, ROM::SQL::Types::Serial
133
+ attribute :name, ROM::SQL::Types::String
134
+
135
+ indexes do
136
+ index :name
137
+ end
138
+ end
139
+ end
140
+
141
+ gateway.auto_migrate!(conf)
142
+
143
+ name_index = migrated_schema.indexes.first
144
+
145
+ expect(name_index.name).to eql(:users_name_index)
146
+ expect(name_index.attributes.map(&:name)).to eql(%i(name))
147
+ expect(name_index).not_to be_unique
148
+ end
149
+
150
+ it 'supports unique indexes' do
151
+ conn.create_table :users do
152
+ primary_key :id
153
+ column :name, String
154
+ end
155
+
156
+ conf.relation(:users) do
157
+ schema do
158
+ attribute :id, ROM::SQL::Types::Serial
159
+ attribute :name, ROM::SQL::Types::String
160
+
161
+ indexes do
162
+ index :name, unique: true
163
+ end
164
+ end
165
+ end
166
+
79
167
  gateway.auto_migrate!(conf)
80
168
 
81
- expect(migrated_schema.indexes.map { |idx| idx.attributes.map(&:name) }).to eql([%i(name)])
169
+ name_index = migrated_schema.indexes.first
170
+
171
+ expect(name_index.name).to eql(:users_name_index)
172
+ expect(name_index.attributes.map(&:name)).to eql(%i(name))
173
+ expect(name_index).to be_unique
174
+ end
175
+
176
+ if metadata[:postgres]
177
+ it 'uses index method' do
178
+ conn.create_table :users do
179
+ primary_key :id
180
+ column :props, :jsonb, null: false
181
+ end
182
+
183
+ conf.relation(:users) do
184
+ schema do
185
+ attribute :id, ROM::SQL::Types::Serial
186
+ attribute :props, ROM::SQL::Types::PG::JSONB
187
+
188
+ indexes do
189
+ index :props, type: :gin
190
+ end
191
+ end
192
+ end
193
+
194
+ gateway.auto_migrate!(conf)
195
+
196
+ expect(indexdef('users_props_index')).
197
+ to eql('CREATE INDEX users_props_index ON users USING gin (props)')
198
+ end
199
+
200
+ it 'supports partial indexes' do
201
+ conn.create_table :users do
202
+ primary_key :id
203
+ column :name, String
204
+ end
205
+
206
+ conf.relation(:users) do
207
+ schema do
208
+ attribute :id, ROM::SQL::Types::Serial
209
+ attribute :name, ROM::SQL::Types::String
210
+
211
+ indexes do
212
+ index :name, name: :long_names_only, predicate: 'length(name) > 10'
213
+ end
214
+ end
215
+ end
216
+
217
+ gateway.auto_migrate!(conf)
218
+
219
+ expect(indexdef('long_names_only')).
220
+ to eql('CREATE INDEX long_names_only ON users USING btree (name) WHERE (length(name) > 10)')
221
+ end
82
222
  end
83
223
  end
84
224
 
@@ -88,19 +228,23 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
88
228
  schema do
89
229
  attribute :id, ROM::SQL::Types::Serial
90
230
  attribute :name, ROM::SQL::Types::String
231
+ attribute :email, ROM::SQL::Types::String
91
232
  end
92
233
  end
93
234
 
94
235
  conn.create_table :users do
95
236
  primary_key :id
96
237
  column :name, String
238
+ column :email, String
97
239
 
98
240
  index :name
241
+ index :email, name: :email_idx
99
242
  end
100
243
  end
101
244
 
102
245
  it 'removes index' do
103
246
  gateway.auto_migrate!(conf)
247
+
104
248
  expect(migrated_schema.indexes).to be_empty
105
249
  end
106
250
  end
@@ -34,7 +34,7 @@ RSpec.describe 'Eager loading' do
34
34
  tasks = container.relations[:tasks]
35
35
  tags = container.relations[:tags]
36
36
 
37
- relation = users.graph(tasks.for_users.graph(tags.for_tasks))
37
+ relation = users.combine_with(tasks.for_users.combine_with(tags.for_tasks))
38
38
 
39
39
  # TODO: figure out a way to assert correct number of issued queries
40
40
  expect(relation.call).to be_instance_of(ROM::Relation::Loaded)
@@ -46,37 +46,37 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
46
46
  with_adapters do
47
47
  describe '#transaction' do
48
48
  it 'creates record if nothing was raised' do
49
- result = create_user.transaction {
49
+ result = users.transaction {
50
50
  create_user.call(name: 'Jane')
51
51
  }
52
52
 
53
- expect(result.value).to eql(id: 1, name: 'Jane')
53
+ expect(result).to eql(id: 1, name: 'Jane')
54
54
  end
55
55
 
56
56
  it 'creates multiple records if nothing was raised' do
57
- result = create_user.transaction {
57
+ result = users.transaction {
58
58
  create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
59
59
  }
60
60
 
61
- expect(result.value).to match_array([
61
+ expect(result).to match_array([
62
62
  { id: 1, name: 'Jane' }, { id: 2, name: 'Jack' }
63
63
  ])
64
64
  end
65
65
 
66
66
  it 'allows for nested transactions' do
67
- result = create_user.transaction {
68
- create_user.transaction {
67
+ result = users.transaction {
68
+ users.transaction {
69
69
  create_user.call(name: 'Jane')
70
70
  }
71
71
  }
72
72
 
73
- expect(result.value).to eql(id: 1, name: 'Jane')
73
+ expect(result).to eql(id: 1, name: 'Jane')
74
74
  end
75
75
 
76
76
  it 'creates nothing if command error was raised' do
77
77
  expect {
78
78
  begin
79
- create_user.transaction {
79
+ users.transaction {
80
80
  create_user.call(name: 'Jane')
81
81
  create_user.call(name: nil)
82
82
  }
@@ -87,19 +87,13 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
87
87
 
88
88
  it 'creates nothing if rollback was raised' do
89
89
  expect {
90
- passed = false
91
-
92
- result = create_user.transaction {
90
+ result = users.transaction { |t|
93
91
  create_user.call(name: 'Jane')
94
92
  create_user.call(name: 'John')
95
- raise ROM::SQL::Rollback
96
- } >-> _value {
97
- passed = true
93
+ t.rollback!
98
94
  }
99
95
 
100
- expect(result.value).to be(nil)
101
- expect(result.error).to be(nil)
102
- expect(passed).to be(false)
96
+ expect(result).to be(nil)
103
97
  }.to_not change { container.relations.users.count }
104
98
  end
105
99
 
@@ -108,10 +102,9 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
108
102
  begin
109
103
  passed = false
110
104
 
111
- create_user.transaction {
105
+ users.transaction {
112
106
  create_user.call(name: 'Jane')
113
107
  create_user.call(name: 'Jane')
114
- } >-> _value {
115
108
  passed = true
116
109
  }
117
110
  rescue => error
@@ -124,9 +117,10 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
124
117
  it 'creates nothing if anything was raised in any nested transaction' do
125
118
  expect {
126
119
  expect {
127
- create_user.transaction {
120
+ users.transaction {
128
121
  create_user.call(name: 'John')
129
- create_user.transaction {
122
+
123
+ users.transaction {
130
124
  create_user.call(name: 'Jane')
131
125
  raise Exception
132
126
  }
@@ -158,24 +152,22 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
158
152
  end
159
153
 
160
154
  it 'returns a single tuple when result is set to :one' do
161
- result = user_commands.try { create_user.call(name: 'Jane') }
155
+ result = create_user.call(name: 'Jane')
162
156
 
163
- expect(result.value).to eql(id: 1, name: 'Jane')
157
+ expect(result).to eql(id: 1, name: 'Jane')
164
158
  end
165
159
 
166
160
  it 'returns tuples when result is set to :many' do
167
- result = user_commands.try do
168
- create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
169
- end
161
+ result = create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
170
162
 
171
- expect(result.value.to_a).to match_array([
163
+ expect(result.to_a).to match_array([
172
164
  { id: 1, name: 'Jane' }, { id: 2, name: 'Jack' }
173
165
  ])
174
166
  end
175
167
 
176
168
  it 're-raises not-null constraint violation error' do
177
169
  expect {
178
- user_commands.try { create_user.call(name: nil) }
170
+ create_user.call(name: nil)
179
171
  }.to raise_error(ROM::SQL::NotNullConstraintError)
180
172
  end
181
173
 
@@ -198,34 +190,27 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
198
190
  it 're-raises not-null constraint violation error with nil boolean' do
199
191
  puppies = commands[:puppies]
200
192
 
201
- expect {
202
- puppies.try { puppies.create.call(name: 'Charlie', cute: nil) }
203
- }.to raise_error(ROM::SQL::NotNullConstraintError)
193
+ expect { puppies.create.call(name: 'Charlie', cute: nil) }.
194
+ to raise_error(ROM::SQL::NotNullConstraintError)
204
195
  end
205
196
  end
206
197
  end
207
198
 
208
- it 're-raises uniqueness constraint violation error' do
199
+ it 'raises uniqueness constraint violation error' do
209
200
  expect {
210
- user_commands.try {
211
- create_user.call(name: 'Jane')
212
- } >-> user {
213
- user_commands.try { create_user.call(name: user[:name]) }
214
- }
201
+ user = create_user.call(name: 'Jane')
202
+ create_user.call(name: user[:name])
215
203
  }.to raise_error(ROM::SQL::UniqueConstraintError)
216
204
  end
217
205
 
218
206
  it 're-raises fk constraint violation error' do |ex|
219
- expect {
220
- task_commands.try {
221
- create_task.call(user_id: 918_273_645)
222
- }
223
- }.to raise_error(ROM::SQL::ForeignKeyConstraintError)
207
+ expect { create_task.call(user_id: 918_273_645) }.
208
+ to raise_error(ROM::SQL::ForeignKeyConstraintError)
224
209
  end
225
210
 
226
211
  it 're-raises database errors' do
227
212
  expect {
228
- user_commands.try { create_user.call(name: nil) }
213
+ create_user.call(name: nil)
229
214
  }.to raise_error(ROM::SQL::NotNullConstraintError)
230
215
  end
231
216
 
@@ -233,9 +218,8 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
233
218
  context 'with a single record' do
234
219
  it 'materializes the result' do
235
220
  result = create_user.execute(name: 'Jane')
236
- expect(result).to eq([
237
- { id: 1, name: 'Jane' }
238
- ])
221
+
222
+ expect(result).to eq([{ id: 1, name: 'Jane' }])
239
223
  end
240
224
  end
241
225
 
@@ -245,10 +229,8 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
245
229
  { name: 'Jane' },
246
230
  { name: 'John' }
247
231
  ])
248
- expect(result).to eq([
249
- { id: 1, name: 'Jane' },
250
- { id: 2, name: 'John' }
251
- ])
232
+
233
+ expect(result).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'John' }])
252
234
  end
253
235
  end
254
236
 
@@ -288,20 +270,13 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
288
270
  end
289
271
 
290
272
  describe '#call' do
291
- it 're-raises check constraint violation error' do
292
- expect {
293
- user_commands.try {
294
- create_user.call(name: 'J')
295
- }
296
- }.to raise_error(ROM::SQL::CheckConstraintError, /name/)
273
+ it 'raises check constraint violation error' do
274
+ expect { create_user.call(name: 'J') }.
275
+ to raise_error(ROM::SQL::CheckConstraintError, /name/)
297
276
  end
298
277
 
299
- it 're-raises constraint violation error' do
300
- expect {
301
- user_commands.try {
302
- create_task.call(title: '')
303
- }
304
- }.to raise_error(ROM::SQL::ConstraintError, /title/)
278
+ it 'raises constraint violation error' do
279
+ expect { create_task.call(title: '') }.to raise_error(ROM::SQL::ConstraintError, /title/)
305
280
  end
306
281
  end
307
282