rom-repository 0.3.1 → 1.0.0.beta1

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.
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