rom-repository 1.0.0.beta3 → 1.0.0.rc1

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.
@@ -12,23 +12,15 @@ require 'rom/repository/session'
12
12
  module ROM
13
13
  # Abstract repository class to inherit from
14
14
  #
15
- # A repository provides access to composable relations and commands. Its job is
16
- # to provide application-specific data that is already materialized, so that
15
+ # A repository provides access to composable relations, commands and changesets.
16
+ # Its job is to provide application-specific data that is already materialized, so that
17
17
  # relations don't leak into your application layer.
18
18
  #
19
- # Typically, you're going to work with Repository::Root that are configured to
20
- # use a single relation as its root, and compose aggregates and use commands
19
+ # Typically, you're going to work with Repository::Root that is configured to
20
+ # use a single relation as its root, and compose aggregates and use changesets and commands
21
21
  # against the root relation.
22
22
  #
23
23
  # @example
24
- # class MyRepo < ROM::Repository[:users]
25
- # relations :users, :tasks
26
- #
27
- # def users_with_tasks
28
- # users.combine_children(tasks: tasks).to_a
29
- # end
30
- # end
31
- #
32
24
  # rom = ROM.container(:sql, 'sqlite::memory') do |conf|
33
25
  # conf.default.create_table(:users) do
34
26
  # primary_key :id
@@ -40,15 +32,36 @@ module ROM
40
32
  # column :user_id, Integer
41
33
  # column :title, String
42
34
  # end
35
+ #
36
+ # conf.relation(:users) do
37
+ # associations do
38
+ # has_many :tasks
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # class UserRepo < ROM::Repository[:users]
44
+ # relations :tasks
45
+ #
46
+ # def users_with_tasks
47
+ # aggregate(:tasks).to_a
48
+ # end
43
49
  # end
44
50
  #
45
- # my_repo = MyRepo.new(rom)
46
- # my_repo.users_with_tasks
51
+ # user_repo = UserRepo.new(rom)
52
+ # user_repo.users_with_tasks
47
53
  #
48
54
  # @see Repository::Root
49
55
  #
50
56
  # @api public
51
57
  class Repository
58
+ # Mapping for supported changeset classes used in #changeset(type => relation) method
59
+ CHANGESET_TYPES = {
60
+ create: Changeset::Create,
61
+ update: Changeset::Update,
62
+ delete: Changeset::Delete
63
+ }.freeze
64
+
52
65
  extend ClassInterface
53
66
 
54
67
  # @!attribute [r] container
@@ -63,7 +76,8 @@ module ROM
63
76
  # @return [MapperBuilder] The auto-generated mappers for repo relations
64
77
  attr_reader :mappers
65
78
 
66
- # @api private
79
+ # @!attribute [r] commmand_compiler
80
+ # @return [Method] Function for compiling commands bound to a repo instance
67
81
  attr_reader :command_compiler
68
82
 
69
83
  # Initializes a new repo by establishing configured relation proxies from
@@ -91,6 +105,8 @@ module ROM
91
105
  @command_compiler = method(:command)
92
106
  end
93
107
 
108
+ # Return a command for a relation
109
+ #
94
110
  # @overload command(type, relation)
95
111
  # Returns a command for a relation
96
112
  #
@@ -138,6 +154,8 @@ module ROM
138
154
  end
139
155
  end
140
156
 
157
+ # Return a changeset for a relation
158
+ #
141
159
  # @overload changeset(name, attributes)
142
160
  # Return a create changeset for a given relation identifier
143
161
  #
@@ -170,6 +188,16 @@ module ROM
170
188
  #
171
189
  # @return [Changeset]
172
190
  #
191
+ # @overload changeset(opts)
192
+ # Return a changeset object using provided changeset type and relation
193
+ #
194
+ # @example
195
+ # repo.changeset(delete: repo.users.where { id > 10 })
196
+ #
197
+ # @param [Hash<Symbol=>Relation] opts Command type => Relation config
198
+ #
199
+ # @return [Changeset]
200
+ #
173
201
  # @api public
174
202
  def changeset(*args)
175
203
  opts = { command_compiler: command_compiler }
@@ -179,10 +207,14 @@ module ROM
179
207
  elsif args.size == 3
180
208
  name, pk, data = args
181
209
  elsif args.size == 1
182
- type = args[0]
210
+ if args[0].is_a?(Class)
211
+ klass = args[0]
183
212
 
184
- if type.is_a?(Class) && type < Changeset
185
- return type.new(relations[type.relation], opts)
213
+ if klass < Changeset
214
+ return klass.new(relations[klass.relation], opts)
215
+ else
216
+ raise ArgumentError, "+#{klass.name}+ is not a Changeset subclass"
217
+ end
186
218
  else
187
219
  type, relation = args[0].to_a[0]
188
220
  end
@@ -191,41 +223,69 @@ module ROM
191
223
  end
192
224
 
193
225
  if type
194
- if type.equal?(:delete)
195
- Changeset::Delete.new(relation, opts)
196
- end
226
+ klass = CHANGESET_TYPES.fetch(type) {
227
+ raise ArgumentError, "+#{type.inspect}+ is not a valid changeset type. Must be one of: #{CHANGESET_TYPES.keys.inspect}"
228
+ }
229
+
230
+ klass.new(relation, opts)
197
231
  else
198
232
  relation = relations[name]
199
233
 
200
234
  if pk
201
- Changeset::Update.new(relation, opts.merge(__data__: data, primary_key: pk))
235
+ Changeset::Update.new(relation.by_pk(pk), opts.update(__data__: data))
202
236
  else
203
- Changeset::Create.new(relation, opts.merge(__data__: data))
237
+ Changeset::Create.new(relation, opts.update(__data__: data))
204
238
  end
205
239
  end
206
240
  end
207
241
 
208
- # TODO: document me, please
242
+ # Open a database transaction
209
243
  #
210
- # @api public
211
- def session(&block)
212
- session = Session.new(self)
213
- yield(session)
214
- transaction { session.commit! }
215
- end
216
-
217
- # TODO: document me, please
244
+ # @example commited transaction
245
+ # user = transaction do |t|
246
+ # create(changeset(name: 'Jane'))
247
+ # end
248
+ #
249
+ # user
250
+ # # => #<ROM::Struct[User] id=1 name="Jane">
251
+ #
252
+ # @example with a rollback
253
+ # user = transaction do |t|
254
+ # changeset(name: 'Jane').commit
255
+ # t.rollback!
256
+ # end
257
+ #
258
+ # user
259
+ # # nil
218
260
  #
219
261
  # @api public
220
262
  def transaction(&block)
221
263
  container.gateways[:default].transaction(&block)
222
264
  end
223
265
 
266
+ # Return a string representation of a repository object
267
+ #
268
+ # @return [String]
269
+ #
224
270
  # @api public
225
271
  def inspect
226
272
  %(#<#{self.class} relations=[#{self.class.relations.map(&:inspect).join(' ')}]>)
227
273
  end
228
274
 
275
+ # Start a session for multiple changesets
276
+ #
277
+ # TODO: this is partly done, needs tweaks in changesets so that we can gather
278
+ # command results and return them in a nice way
279
+ #
280
+ # @!visibility private
281
+ #
282
+ # @api public
283
+ def session(&block)
284
+ session = Session.new(self)
285
+ yield(session)
286
+ transaction { session.commit! }
287
+ end
288
+
229
289
  private
230
290
 
231
291
  # Local command cache
data/lib/rom/struct.rb CHANGED
@@ -1,9 +1,47 @@
1
1
  require 'dry/struct'
2
2
 
3
3
  module ROM
4
- # Simple data-struct
4
+ # Simple data-struct class
5
5
  #
6
- # By default mappers use this as the model
6
+ # ROM structs are plain data structures loaded by repositories.
7
+ # They implement Hash protocol which means that they can be used
8
+ # in places where Hash-like objects are supported.
9
+ #
10
+ # Repositories define subclasses of ROM::Struct automatically, they are not
11
+ # defined as constants in any module, instead, generated mappers are configured
12
+ # to use anonymous struct classes as models.
13
+ #
14
+ # Structs are based on dry-struct gem, they include `schema` with detailed information
15
+ # about attribute types returned from relations, thus can be introspected to build
16
+ # additional functionality when desired.
17
+ #
18
+ # @example accessing relation struct model
19
+ # rom = ROM.container(:sql, 'sqlite::memory') do |conf|
20
+ # conf.default.create_table(:users) do
21
+ # primary_key :id
22
+ # column :name, String
23
+ # end
24
+ # end
25
+ #
26
+ # class UserRepo < ROM::Repository[:users]
27
+ # end
28
+ #
29
+ # user_repo = UserRepo.new(rom)
30
+ #
31
+ # # get auto-generated User struct
32
+ # model = user_repo.users.mapper.model
33
+ # # => ROM::Struct[User]
34
+ #
35
+ # # see struct's schema attributes
36
+ #
37
+ # # model.schema[:id]
38
+ # # => #<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=Integer options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>, :meta=>{:primary_key=>true, :name=>:id, :source=>ROM::Relation::Name(users)}} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>>
39
+ #
40
+ # model.schema[:name]
41
+ # # => #<Dry::Types::Sum left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Definition primitive=String options={}> options={:meta=>{:name=>:name, :source=>ROM::Relation::Name(users)}}>
42
+ #
43
+ # @see http://dry-rb.org/gems/dry-struct dry-struct
44
+ # @see http://dry-rb.org/gems/dry-types dry-types
7
45
  #
8
46
  # @api public
9
47
  class Struct < Dry::Struct
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
15
15
  gem.test_files = `git ls-files -- {spec}/*`.split("\n")
16
16
  gem.license = 'MIT'
17
17
 
18
- gem.add_runtime_dependency 'rom', '~> 3.0.0.beta'
19
- gem.add_runtime_dependency 'rom-mapper', '~> 0.5.0.beta'
18
+ gem.add_runtime_dependency 'rom', '~> 3.0.0.rc'
19
+ gem.add_runtime_dependency 'rom-mapper', '~> 0.5.0.rc'
20
20
  gem.add_runtime_dependency 'dry-core', '~> 0.2', '>= 0.2.1'
21
21
  gem.add_runtime_dependency 'dry-struct', '~> 0.1'
22
22
 
@@ -43,6 +43,33 @@ RSpec.describe 'Using changesets' do
43
43
  expect(result.created_at).to be_instance_of(Time)
44
44
  expect(result.updated_at).to be_instance_of(Time)
45
45
  end
46
+
47
+ it 'preprocesses data using custom block' do
48
+ changeset = repo.
49
+ changeset(:books, title: "rom-rb is awesome").
50
+ map { |tuple| tuple.merge(created_at: Time.now) }
51
+
52
+ command = repo.command(:create, repo.books)
53
+ result = command.(changeset)
54
+
55
+ expect(result.id).to_not be(nil)
56
+ expect(result.title).to eql("rom-rb is awesome")
57
+ expect(result.created_at).to be_instance_of(Time)
58
+ end
59
+
60
+ it 'preprocesses data using built-in steps and custom block' do
61
+ changeset = repo.
62
+ changeset(:books, title: "rom-rb is awesome").
63
+ map(:touch) { |tuple| tuple.merge(created_at: Time.now) }
64
+
65
+ command = repo.command(:create, repo.books)
66
+ result = command.(changeset)
67
+
68
+ expect(result.id).to_not be(nil)
69
+ expect(result.title).to eql("rom-rb is awesome")
70
+ expect(result.created_at).to be_instance_of(Time)
71
+ expect(result.updated_at).to be_instance_of(Time)
72
+ end
46
73
  end
47
74
 
48
75
  describe 'Update' do
@@ -1,6 +1,6 @@
1
1
  RSpec.describe ROM::Changeset do
2
2
  let(:jane) { { id: 2, name: "Jane" } }
3
- let(:relation) { double(ROM::Relation, primary_key: :id) }
3
+ let(:relation) { double(ROM::Relation, name: :users) }
4
4
 
5
5
  describe '.[]' do
6
6
  it 'returns a changeset preconfigured for a specific relation' do
@@ -24,9 +24,9 @@ RSpec.describe ROM::Changeset do
24
24
 
25
25
  describe '#diff' do
26
26
  it 'returns a hash with changes' do
27
- expect(relation).to receive(:fetch).with(2).and_return(jane)
27
+ expect(relation).to receive(:one).and_return(jane)
28
28
 
29
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" }, primary_key: 2)
29
+ changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" })
30
30
 
31
31
  expect(changeset.diff).to eql(name: "Jane Doe")
32
32
  end
@@ -34,22 +34,40 @@ RSpec.describe ROM::Changeset do
34
34
 
35
35
  describe '#diff?' do
36
36
  it 'returns true when data differs from the original tuple' do
37
- expect(relation).to receive(:fetch).with(2).and_return(jane)
37
+ expect(relation).to receive(:one).and_return(jane)
38
38
 
39
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" }, primary_key: 2)
39
+ changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" })
40
40
 
41
41
  expect(changeset).to be_diff
42
42
  end
43
43
 
44
44
  it 'returns false when data are equal to the original tuple' do
45
- expect(relation).to receive(:fetch).with(2).and_return(jane)
45
+ expect(relation).to receive(:one).and_return(jane)
46
46
 
47
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane" }, primary_key: 2)
47
+ changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane" })
48
48
 
49
49
  expect(changeset).to_not be_diff
50
50
  end
51
51
  end
52
52
 
53
+ describe '#clean?' do
54
+ it 'returns true when data are equal to the original tuple' do
55
+ expect(relation).to receive(:one).and_return(jane)
56
+
57
+ changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane" })
58
+
59
+ expect(changeset).to be_clean
60
+ end
61
+
62
+ it 'returns false when data differs from the original tuple' do
63
+ expect(relation).to receive(:one).and_return(jane)
64
+
65
+ changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" })
66
+
67
+ expect(changeset).to_not be_clean
68
+ end
69
+ end
70
+
53
71
  describe 'quacks like a hash' do
54
72
  subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
55
73
 
@@ -101,4 +119,24 @@ RSpec.describe ROM::Changeset do
101
119
  expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
102
120
  end
103
121
  end
122
+
123
+ describe '#inspect' do
124
+ context 'with a stateful changeset' do
125
+ subject(:changeset) { ROM::Changeset::Create.new(relation).data(name: 'Jane') }
126
+
127
+ specify do
128
+ expect(changeset.inspect).
129
+ to eql('#<ROM::Changeset::Create relation=:users data={:name=>"Jane"}>')
130
+ end
131
+ end
132
+
133
+ context 'with a data-less changeset' do
134
+ subject(:changeset) { ROM::Changeset::Delete.new(relation) }
135
+
136
+ specify do
137
+ expect(changeset.inspect).
138
+ to eql('#<ROM::Changeset::Delete relation=:users>')
139
+ end
140
+ end
141
+ end
104
142
  end
@@ -20,8 +20,8 @@ RSpec.describe ROM::Repository, '#changeset' do
20
20
  expect(changeset.relation).to be(repo.users)
21
21
  end
22
22
 
23
- it 'can return a dedicated command' do
24
- expect(changeset.command.call).to eql(id: 1, name: 'Jane')
23
+ it 'can be commited' do
24
+ expect(changeset.commit).to eql(id: 1, name: 'Jane')
25
25
  end
26
26
  end
27
27
 
@@ -42,35 +42,53 @@ RSpec.describe ROM::Repository, '#changeset' do
42
42
  expect(changeset.relation).to be(repo.users)
43
43
  end
44
44
 
45
- it 'can return a dedicated command' do
46
- expect(changeset.command.call).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'Joe' }])
45
+ it 'can be commited' do
46
+ expect(changeset.commit).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'Joe' }])
47
47
  end
48
48
  end
49
49
  end
50
50
 
51
51
  describe ROM::Changeset::Update do
52
- let(:changeset) do
53
- repo.changeset(:users, user[:id], name: 'Jane Doe')
52
+ let(:data) do
53
+ { name: 'Jane Doe' }
54
54
  end
55
55
 
56
- let(:user) do
57
- repo.command(:create, repo.users).call(name: 'Jane')
58
- end
56
+ shared_context 'a valid update changeset' do
57
+ let(:user) do
58
+ repo.command(:create, repo.users).call(name: 'Jane')
59
+ end
59
60
 
60
- it 'has data' do
61
- expect(changeset.to_h).to eql(name: 'Jane Doe')
62
- end
61
+ it 'has data' do
62
+ expect(changeset.to_h).to eql(name: 'Jane Doe')
63
+ end
64
+
65
+ it 'has diff' do
66
+ expect(changeset.diff).to eql(name: 'Jane Doe')
67
+ end
63
68
 
64
- it 'has diff' do
65
- expect(changeset.diff).to eql(name: 'Jane Doe')
69
+ it 'has relation' do
70
+ expect(changeset.relation.one).to eql(repo.users.by_pk(user[:id]).one)
71
+ end
72
+
73
+ it 'can return be commited' do
74
+ expect(changeset.commit).to eql(id: 1, name: 'Jane Doe')
75
+ end
66
76
  end
67
77
 
68
- it 'has relation' do
69
- expect(changeset.relation).to be(repo.users)
78
+ context 'using PK to restrict a relation' do
79
+ let(:changeset) do
80
+ repo.changeset(:users, user[:id], data)
81
+ end
82
+
83
+ include_context 'a valid update changeset'
70
84
  end
71
85
 
72
- it 'can return a dedicated command' do
73
- expect(changeset.command.call).to eql(id: 1, name: 'Jane Doe')
86
+ context 'using custom relation' do
87
+ let(:changeset) do
88
+ repo.changeset(update: repo.users.by_pk(user[:id])).data(data)
89
+ end
90
+
91
+ include_context 'a valid update changeset'
74
92
  end
75
93
  end
76
94
 
@@ -91,8 +109,8 @@ RSpec.describe ROM::Repository, '#changeset' do
91
109
  expect(changeset.relation).to eql(relation)
92
110
  end
93
111
 
94
- it 'can return a dedicated command' do
95
- expect(changeset.command.call.to_h).to eql(id: 1, name: 'Jane')
112
+ it 'can be commited' do
113
+ expect(changeset.commit.to_h).to eql(id: 1, name: 'Jane')
96
114
  expect(relation.one).to be(nil)
97
115
  end
98
116
  end
@@ -118,8 +136,27 @@ RSpec.describe ROM::Repository, '#changeset' do
118
136
  expect(changeset.relation).to be(repo.users)
119
137
  end
120
138
 
121
- it 'can return a dedicated command' do
122
- expect(changeset.command.call).to eql(id: 1, name: 'Jane')
139
+ it 'can be commited' do
140
+ expect(changeset.commit).to eql(id: 1, name: 'Jane')
123
141
  end
124
142
  end
143
+
144
+ it 'raises ArgumentError when unknown type was used' do
145
+ expect { repo.changeset(not_here: repo.users) }.
146
+ to raise_error(
147
+ ArgumentError,
148
+ '+:not_here+ is not a valid changeset type. Must be one of: [:create, :update, :delete]'
149
+ )
150
+ end
151
+
152
+ it 'raises ArgumentError when unknown class was used' do
153
+ klass = Class.new {
154
+ def self.name
155
+ 'SomeClass'
156
+ end
157
+ }
158
+
159
+ expect { repo.changeset(klass) }.
160
+ to raise_error(ArgumentError, /SomeClass/)
161
+ end
125
162
  end
@@ -4,7 +4,7 @@ RSpec.describe ROM::Repository, '#transaction' do
4
4
  end
5
5
 
6
6
  let(:task_repo) do
7
- Class.new(ROM::Repository[:tasks]) { commands :create }.new(rom)
7
+ Class.new(ROM::Repository[:tasks]) { commands :create, :update }.new(rom)
8
8
  end
9
9
 
10
10
  include_context 'database'
@@ -25,4 +25,18 @@ RSpec.describe ROM::Repository, '#transaction' do
25
25
  expect(task.user_id).to be(user.id)
26
26
  expect(task.title).to eql('Task One')
27
27
  end
28
+
29
+ it 'updating tasks user' do
30
+ jane = user_repo.create(name: 'Jane')
31
+ john = user_repo.create(name: 'John')
32
+ task = task_repo.create(title: 'Jane Task', user_id: jane.id)
33
+
34
+ task = task_repo.transaction do
35
+ task_changeset = task_repo.changeset(task.id, title: 'John Task').associate(john, :user).commit
36
+ task_repo.update(task_changeset)
37
+ end
38
+
39
+ expect(task.user_id).to be(john.id)
40
+ expect(task.title).to eql('John Task')
41
+ end
28
42
  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: 1.0.0.beta3
4
+ version: 1.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-21 00:00:00.000000000 Z
11
+ date: 2017-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rom
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 3.0.0.beta
19
+ version: 3.0.0.rc
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: 3.0.0.beta
26
+ version: 3.0.0.rc
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rom-mapper
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.5.0.beta
33
+ version: 0.5.0.rc
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: 0.5.0.beta
40
+ version: 0.5.0.rc
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: dry-core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -119,9 +119,11 @@ files:
119
119
  - lib/rom-repository.rb
120
120
  - lib/rom/repository.rb
121
121
  - lib/rom/repository/changeset.rb
122
+ - lib/rom/repository/changeset/associated.rb
122
123
  - lib/rom/repository/changeset/create.rb
123
124
  - lib/rom/repository/changeset/delete.rb
124
125
  - lib/rom/repository/changeset/pipe.rb
126
+ - lib/rom/repository/changeset/stateful.rb
125
127
  - lib/rom/repository/changeset/update.rb
126
128
  - lib/rom/repository/class_interface.rb
127
129
  - lib/rom/repository/command_compiler.rb