rom-repository 0.3.1 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -13
  3. data/CHANGELOG.md +25 -0
  4. data/Gemfile +13 -3
  5. data/lib/rom/repository.rb +57 -19
  6. data/lib/rom/repository/changeset.rb +89 -26
  7. data/lib/rom/repository/changeset/create.rb +34 -0
  8. data/lib/rom/repository/changeset/delete.rb +15 -0
  9. data/lib/rom/repository/changeset/pipe.rb +11 -4
  10. data/lib/rom/repository/changeset/update.rb +11 -1
  11. data/lib/rom/repository/command_compiler.rb +51 -30
  12. data/lib/rom/repository/command_proxy.rb +3 -1
  13. data/lib/rom/repository/header_builder.rb +3 -3
  14. data/lib/rom/repository/mapper_builder.rb +2 -2
  15. data/lib/rom/repository/relation_proxy.rb +26 -35
  16. data/lib/rom/repository/relation_proxy/combine.rb +59 -27
  17. data/lib/rom/repository/root.rb +4 -6
  18. data/lib/rom/repository/session.rb +55 -0
  19. data/lib/rom/repository/struct_builder.rb +29 -17
  20. data/lib/rom/repository/version.rb +1 -1
  21. data/lib/rom/struct.rb +11 -20
  22. data/rom-repository.gemspec +4 -3
  23. data/spec/integration/command_macros_spec.rb +5 -2
  24. data/spec/integration/command_spec.rb +0 -6
  25. data/spec/integration/multi_adapter_spec.rb +8 -5
  26. data/spec/integration/repository_spec.rb +58 -2
  27. data/spec/integration/root_repository_spec.rb +9 -2
  28. data/spec/integration/typed_structs_spec.rb +31 -0
  29. data/spec/shared/database.rb +5 -1
  30. data/spec/shared/relations.rb +3 -1
  31. data/spec/shared/repo.rb +13 -1
  32. data/spec/shared/structs.rb +39 -0
  33. data/spec/spec_helper.rb +7 -5
  34. data/spec/support/mutant.rb +10 -0
  35. data/spec/unit/changeset/map_spec.rb +42 -0
  36. data/spec/unit/changeset_spec.rb +32 -6
  37. data/spec/unit/relation_proxy_spec.rb +27 -9
  38. data/spec/unit/repository/changeset_spec.rb +125 -0
  39. data/spec/unit/repository/inspect_spec.rb +18 -0
  40. data/spec/unit/repository/session_spec.rb +251 -0
  41. data/spec/unit/session_spec.rb +54 -0
  42. data/spec/unit/struct_builder_spec.rb +45 -1
  43. metadata +41 -17
  44. data/lib/rom/repository/struct_attributes.rb +0 -46
  45. data/spec/unit/header_builder_spec.rb +0 -73
  46. data/spec/unit/plugins/view_spec.rb +0 -29
  47. data/spec/unit/sql/relation_spec.rb +0 -54
  48. data/spec/unit/struct_spec.rb +0 -22
@@ -0,0 +1,18 @@
1
+ RSpec.describe ROM::Repository, '#inspect' do
2
+ subject(:repo) do
3
+ Class.new(ROM::Repository) do
4
+ relations :users
5
+
6
+ def self.to_s
7
+ 'UserRepo'
8
+ end
9
+ end.new(rom)
10
+ end
11
+
12
+ include_context 'database'
13
+ include_context 'relations'
14
+
15
+ specify do
16
+ expect(repo.inspect).to eql('#<UserRepo relations=[:users]>')
17
+ end
18
+ end
@@ -0,0 +1,251 @@
1
+ RSpec.describe ROM::Repository, '#session' do
2
+ subject(:repo) do
3
+ Class.new(ROM::Repository) { relations :users, :posts, :labels }.new(rom)
4
+ end
5
+
6
+ include_context 'database'
7
+ include_context 'relations'
8
+
9
+ describe 'with :create command' do
10
+ let(:user_changeset) do
11
+ repo.changeset(:users, name: 'Jane')
12
+ end
13
+
14
+ it 'saves data in a transaction' do
15
+ repo.session do |s|
16
+ s.add(user_changeset)
17
+ end
18
+
19
+ user = repo.users.where(name: 'Jane').one
20
+ expect(user.to_h).to eql(id: 1, name: 'Jane')
21
+ end
22
+ end
23
+
24
+ describe 'with :update command' do
25
+ let(:user_changeset) do
26
+ repo.changeset(:users, user.id, user.to_h.merge(name: 'Jane Doe'))
27
+ end
28
+
29
+ let(:user) do
30
+ repo.users.where(name: 'Jane').one
31
+ end
32
+
33
+ before do
34
+ repo.command(:create, repo.users).call(name: 'John')
35
+ repo.command(:create, repo.users).call(name: 'Jane')
36
+ end
37
+
38
+ it 'saves data in a transaction' do
39
+ repo.session do |s|
40
+ s.add(user_changeset)
41
+ end
42
+
43
+ updated_user = repo.users.fetch(user.id)
44
+
45
+ expect(updated_user).to eql(id: 2, name: 'Jane Doe')
46
+ end
47
+ end
48
+
49
+ describe 'with :delete command' do
50
+ let(:user) do
51
+ repo.users.where(name: 'Jane').one
52
+ end
53
+
54
+ before do
55
+ repo.command(:create, repo.users).call(name: 'John')
56
+ repo.command(:create, repo.users).call(name: 'Jane')
57
+ end
58
+
59
+ let(:user_changeset) do
60
+ repo.changeset(delete: repo.users.by_pk(user.id))
61
+ end
62
+
63
+ it 'saves data in a transaction' do
64
+ repo.session do |t|
65
+ t.add(user_changeset)
66
+ end
67
+
68
+ expect(repo.users.by_pk(user.id).one).to be(nil)
69
+ expect(repo.users.count).to be(1)
70
+ end
71
+ end
72
+
73
+ describe 'with :custom command', :postgres do
74
+ before do
75
+ configuration.commands(:users) do
76
+ define(:create) do
77
+ register_as :custom
78
+ end
79
+ end
80
+ end
81
+
82
+ let(:user_changeset) do
83
+ repo.changeset(:users, name: 'John').with(command_type: :custom)
84
+ end
85
+
86
+ it 'saves data in a transaction' do
87
+ repo.session do |t|
88
+ t.add(user_changeset)
89
+ end
90
+
91
+ user = repo.users.first
92
+
93
+ expect(user.id).to_not be(nil)
94
+ expect(user.name).to eql('John')
95
+ expect(repo.users.count).to be(1)
96
+ end
97
+ end
98
+
99
+ describe 'creating a user with its posts' do
100
+ let(:posts_changeset) do
101
+ repo.changeset(:posts, [{ title: 'Post 1' }, { title: 'Post 2' }])
102
+ end
103
+
104
+ let(:user_changeset) do
105
+ repo.changeset(:users, name: 'Jane')
106
+ end
107
+
108
+ it 'saves data in a transaction' do
109
+ repo.session do |s|
110
+ s.add(user_changeset.associate(posts_changeset, :author))
111
+ end
112
+
113
+ user = repo.users.combine(:posts).one
114
+
115
+ expect(user.name).to eql('Jane')
116
+ expect(user.posts.size).to be(2)
117
+ expect(user.posts[0].title).to eql('Post 1')
118
+ expect(user.posts[1].title).to eql('Post 2')
119
+ end
120
+ end
121
+
122
+ describe 'creating a user with its posts and their labels' do
123
+ let(:posts_data) do
124
+ { title: 'Post 1' }
125
+ end
126
+
127
+ let(:posts_changeset) do
128
+ repo.
129
+ changeset(:posts, posts_data).
130
+ associate(labels_changeset, :posts)
131
+ end
132
+
133
+ let(:labels_changeset) do
134
+ repo.changeset(:labels, [{ name: 'red' }, { name: 'green' }])
135
+ end
136
+
137
+ let(:user_changeset) do
138
+ repo.
139
+ changeset(:users, name: 'Jane').
140
+ associate(posts_changeset, :author)
141
+ end
142
+
143
+ it 'saves data in a transaction' do
144
+ repo.session do |t|
145
+ t.add(user_changeset)
146
+ end
147
+
148
+ user = repo.users.combine(posts: [:labels]).one
149
+
150
+ expect(user.name).to eql('Jane')
151
+ expect(user.posts.size).to be(1)
152
+ expect(user.posts[0].title).to eql('Post 1')
153
+ expect(user.posts[0].labels.size).to be(2)
154
+ expect(user.posts[0].labels[0].name).to eql('red')
155
+ expect(user.posts[0].labels[1].name).to eql('green')
156
+ end
157
+
158
+ context 'with invalid data' do
159
+ let(:posts_data) do
160
+ [{ title: nil }]
161
+ end
162
+
163
+ it 'rolls back the transaction' do
164
+ expect {
165
+ repo.session do |t|
166
+ t.add(user_changeset)
167
+ end
168
+ }.to raise_error(ROM::SQL::ConstraintError)
169
+
170
+ expect(repo.users.count).to be(0)
171
+ expect(repo.posts.count).to be(0)
172
+ expect(repo.labels.count).to be(0)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe 'creating new posts for existing user' do
178
+ let(:posts_changeset) do
179
+ repo.
180
+ changeset(:posts, [{ title: 'Post 1' }, { title: 'Post 2' }]).
181
+ associate(user, :author)
182
+ end
183
+
184
+ let(:user) do
185
+ repo.command(:create, repo.users).call(name: 'Jane')
186
+ end
187
+
188
+ it 'saves data in a transaction' do
189
+ repo.session do |s|
190
+ s.add(posts_changeset)
191
+ end
192
+
193
+ user = repo.users.combine(:posts).one
194
+
195
+ expect(user.posts.size).to be(2)
196
+ expect(user.posts[0].title).to eql('Post 1')
197
+ expect(user.posts[1].title).to eql('Post 2')
198
+ end
199
+ end
200
+
201
+ describe 'nesting sessions' do
202
+ let(:user_changeset) do
203
+ repo.changeset(:users, name: 'Jane')
204
+ end
205
+
206
+ let(:posts_changeset) do
207
+ repo.changeset(:posts, post_data)
208
+ end
209
+
210
+ let(:user) do
211
+ repo.users.where(name: 'Jane').one
212
+ end
213
+
214
+ context 'when data is valid' do
215
+ let(:post_data) do
216
+ [{ title: 'Post 1' }, { title: 'Post 2' }]
217
+ end
218
+
219
+ it 'saves data in transactions' do
220
+ repo.send(:transaction) do |t|
221
+ repo.session { |s| s.add(user_changeset) }
222
+ repo.session { |s| s.add(posts_changeset.associate(user, :author)) }
223
+ end
224
+
225
+ user = repo.users.combine(:posts).one
226
+
227
+ expect(user.posts.size).to be(2)
228
+ expect(user.posts[0].title).to eql('Post 1')
229
+ expect(user.posts[1].title).to eql('Post 2')
230
+ end
231
+ end
232
+
233
+ context 'when data is not valid' do
234
+ let(:post_data) do
235
+ [{ title: 'Post 1' }, { title: nil }]
236
+ end
237
+
238
+ it 'rolls back transaction' do
239
+ expect {
240
+ repo.send(:transaction) do |t|
241
+ repo.session { |s| s.add(user_changeset) }
242
+ repo.session { |s| s.add(posts_changeset.associate(user, :author)) }
243
+ end
244
+ }.to raise_error(ROM::SQL::ConstraintError, /title/)
245
+
246
+ expect(repo.users.count).to be(0)
247
+ expect(repo.posts.count).to be(0)
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,54 @@
1
+ RSpec.describe ROM::Session do
2
+ subject(:session) do
3
+ ROM::Session.new(repo)
4
+ end
5
+
6
+ let(:repo) { instance_double(ROM::Repository) }
7
+ let(:create_changeset) { instance_double(ROM::Changeset::Create, relation: relation) }
8
+ let(:delete_changeset) { instance_double(ROM::Changeset::Delete, relation: relation) }
9
+ let(:relation) { double.as_null_object }
10
+ let(:create_command) { spy(:create_command) }
11
+ let(:delete_command) { spy(:delete_command) }
12
+
13
+ describe '#pending?' do
14
+ it 'returns true before commit' do
15
+ expect(session).to be_pending
16
+ end
17
+
18
+ it 'returns false after commit' do
19
+ expect(session.commit!).to_not be_pending
20
+ end
21
+ end
22
+
23
+ describe '#commit!' do
24
+ it 'executes ops and restores pristine state' do
25
+ expect(create_changeset).to receive(:command).and_return(create_command)
26
+
27
+ session.add(create_changeset).commit!
28
+ session.commit!
29
+
30
+ expect(session).to be_success
31
+
32
+ expect(create_command).to have_received(:call)
33
+ end
34
+
35
+ it 'executes ops and restores pristine state when exception was raised' do
36
+ expect(create_changeset).to receive(:command).and_return(create_command)
37
+ expect(delete_changeset).to receive(:command).and_return(delete_command)
38
+
39
+ expect(delete_command).to receive(:call).and_raise(StandardError, 'oops')
40
+
41
+ expect {
42
+ session.add(delete_changeset)
43
+ session.add(create_changeset)
44
+ session.commit!
45
+ }.to raise_error(StandardError, 'oops')
46
+
47
+ expect(session).to be_failure
48
+
49
+ session.commit!
50
+
51
+ expect(create_command).not_to have_received(:call)
52
+ end
53
+ end
54
+ end
@@ -1,7 +1,22 @@
1
1
  RSpec.describe 'struct builder', '#call' do
2
2
  subject(:builder) { ROM::Repository::StructBuilder.new }
3
3
 
4
- let(:input) { [:users, [:header, [[:attribute, :id], [:attribute, :name]]]] }
4
+ def attr_double(name, type, **opts)
5
+ double(
6
+ name: name,
7
+ aliased?: false,
8
+ wrapped?: false,
9
+ foreign_key?: false,
10
+ type: ROM::Types.const_get(type),
11
+ **opts
12
+ )
13
+ end
14
+
15
+ let(:input) do
16
+ [:users, [:header, [
17
+ [:attribute, attr_double(:id, :Int)],
18
+ [:attribute, attr_double(:name, :String)]]]]
19
+ end
5
20
 
6
21
  before { builder[*input] }
7
22
 
@@ -25,4 +40,33 @@ RSpec.describe 'struct builder', '#call' do
25
40
  it 'stores struct in the cache' do
26
41
  expect(builder.class.cache[input.hash]).to be(builder[*input])
27
42
  end
43
+
44
+ context 'with reserved keywords as attribute names' do
45
+ let(:input) do
46
+ [:users, [:header, [
47
+ [:attribute, attr_double(:id, :Int)],
48
+ [:attribute, attr_double(:name, :String)],
49
+ [:attribute, attr_double(:alias, :String)],
50
+ [:attribute, attr_double(:until, :Time)]]]]
51
+ end
52
+
53
+ it 'allows to build a struct class without complaining' do
54
+ struct = builder.class.cache[input.hash]
55
+
56
+ user = struct.new(id: 1, name: 'Jane', alias: 'JD', until: Time.new(2030))
57
+
58
+ expect(user.id).to be(1)
59
+ expect(user.name).to eql('Jane')
60
+ expect(user.alias).to eql('JD')
61
+ expect(user.until).to eql(Time.new(2030))
62
+ end
63
+ end
64
+
65
+ it 'raise a friendly error on missing keys' do
66
+ struct = builder.class.cache[input.hash]
67
+
68
+ expect { struct.new(id: 1) }.to raise_error(
69
+ Dry::Struct::Error, /:name is missing/
70
+ )
71
+ end
28
72
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom-repository
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-27 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rom
@@ -16,42 +16,62 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: 3.0.0.beta
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: 3.0.0.beta
27
27
  - !ruby/object:Gem::Dependency
28
- name: rom-support
28
+ name: rom-mapper
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: 0.5.0.beta
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: 0.5.0.beta
41
41
  - !ruby/object:Gem::Dependency
42
- name: rom-mapper
42
+ name: dry-core
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.4'
47
+ version: '0.2'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 0.2.1
48
51
  type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
55
  - - "~>"
53
56
  - !ruby/object:Gem::Version
54
- version: '0.4'
57
+ version: '0.2'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 0.2.1
61
+ - !ruby/object:Gem::Dependency
62
+ name: dry-struct
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.1'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
55
75
  - !ruby/object:Gem::Dependency
56
76
  name: rake
57
77
  requirement: !ruby/object:Gem::Requirement
@@ -99,6 +119,7 @@ files:
99
119
  - lib/rom/repository.rb
100
120
  - lib/rom/repository/changeset.rb
101
121
  - lib/rom/repository/changeset/create.rb
122
+ - lib/rom/repository/changeset/delete.rb
102
123
  - lib/rom/repository/changeset/pipe.rb
103
124
  - lib/rom/repository/changeset/update.rb
104
125
  - lib/rom/repository/class_interface.rb
@@ -110,7 +131,7 @@ files:
110
131
  - lib/rom/repository/relation_proxy/combine.rb
111
132
  - lib/rom/repository/relation_proxy/wrap.rb
112
133
  - lib/rom/repository/root.rb
113
- - lib/rom/repository/struct_attributes.rb
134
+ - lib/rom/repository/session.rb
114
135
  - lib/rom/repository/struct_builder.rb
115
136
  - lib/rom/repository/version.rb
116
137
  - lib/rom/struct.rb
@@ -122,6 +143,7 @@ files:
122
143
  - spec/integration/multi_adapter_spec.rb
123
144
  - spec/integration/repository_spec.rb
124
145
  - spec/integration/root_repository_spec.rb
146
+ - spec/integration/typed_structs_spec.rb
125
147
  - spec/shared/database.rb
126
148
  - spec/shared/mappers.rb
127
149
  - spec/shared/models.rb
@@ -132,13 +154,15 @@ files:
132
154
  - spec/shared/structs.rb
133
155
  - spec/spec_helper.rb
134
156
  - spec/support/mapper_registry.rb
157
+ - spec/support/mutant.rb
158
+ - spec/unit/changeset/map_spec.rb
135
159
  - spec/unit/changeset_spec.rb
136
- - spec/unit/header_builder_spec.rb
137
- - spec/unit/plugins/view_spec.rb
138
160
  - spec/unit/relation_proxy_spec.rb
139
- - spec/unit/sql/relation_spec.rb
161
+ - spec/unit/repository/changeset_spec.rb
162
+ - spec/unit/repository/inspect_spec.rb
163
+ - spec/unit/repository/session_spec.rb
164
+ - spec/unit/session_spec.rb
140
165
  - spec/unit/struct_builder_spec.rb
141
- - spec/unit/struct_spec.rb
142
166
  homepage: http://rom-rb.org
143
167
  licenses:
144
168
  - MIT
@@ -154,9 +178,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
154
178
  version: '0'
155
179
  required_rubygems_version: !ruby/object:Gem::Requirement
156
180
  requirements:
157
- - - ">="
181
+ - - ">"
158
182
  - !ruby/object:Gem::Version
159
- version: '0'
183
+ version: 1.3.1
160
184
  requirements: []
161
185
  rubyforge_project:
162
186
  rubygems_version: 2.5.1