active_merge 1.0.5 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +22 -2
- data/lib/active_merge/service.rb +14 -8
- data/lib/active_merge/simple_service.rb +10 -5
- data/lib/active_merge/version.rb +1 -1
- data/lib/active_merge.rb +8 -2
- data/spec/active_merge/service_spec.rb +157 -31
- data/spec/active_merge_spec.rb +6 -2
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +16071 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a852fec8deb9d07be3fc9c377d1094a12a3574c
|
4
|
+
data.tar.gz: 9dc95d04b550e50ffbf1250c11ee578a35c60729
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c57dcfd9df06c76630812169fc38e815b059b33e23b6f9c623eb3324469177ed4f41384ac5eda12ed199e82770b5a4f0bbbcb3c55d239b7b946e6e364edfaf7
|
7
|
+
data.tar.gz: 5a606c8a1aa4ade555780ecad0dee9e303ae76b3a05c118ce42150b4b8d46485ab82009aa3762d52380110772d6d6fca16828db7380e8b9e4177a803a63d1c65
|
data/README.rdoc
CHANGED
@@ -7,14 +7,17 @@
|
|
7
7
|
|
8
8
|
Declares the <tt>ActiveMerge</tt> module for extending ActiveRecord models.
|
9
9
|
|
10
|
+
== Main usage
|
11
|
+
|
10
12
|
The module contains the <tt>merge_all</tt> class method for merging
|
11
13
|
class instances into the first one.
|
12
14
|
|
13
15
|
When merging a list of instances:
|
16
|
+
|
14
17
|
* all "has_many" relatives are reattached to the instance with the lowest id
|
15
18
|
* all the instances except for the first one (with the lowest id) are deleted
|
16
19
|
|
17
|
-
|
20
|
+
=== Example
|
18
21
|
|
19
22
|
class Post < ActiveRecord
|
20
23
|
extend ActiveMerge
|
@@ -26,6 +29,23 @@ When merging a list of instances:
|
|
26
29
|
# The other posts will be deleted after their comment are reattached
|
27
30
|
# to the first post.
|
28
31
|
|
32
|
+
== Skipping validations and callbacks
|
33
|
+
|
34
|
+
You can use the <tt>validate: false</tt> option.
|
35
|
+
|
36
|
+
With this option set any activerecord validation and callback are skipped.
|
37
|
+
|
38
|
+
Post.all.merge_all validate: false
|
39
|
+
|
40
|
+
**Warning**
|
41
|
+
|
42
|
+
Only activerecord validations and callbacks are skipped! Raising other errors
|
43
|
+
(by database etc.) will cause all changes to roll back as before.
|
44
|
+
|
45
|
+
== Version changes
|
46
|
+
|
47
|
+
See the CHANGELOG[link:CHANGELOG.rdoc].
|
48
|
+
|
29
49
|
== License
|
30
50
|
|
31
|
-
This project rocks and uses MIT
|
51
|
+
This project rocks and uses MIT LICENSE[link:LICENSE].
|
data/lib/active_merge/service.rb
CHANGED
@@ -48,7 +48,7 @@ module ActiveMerge
|
|
48
48
|
# 5.times.each{ scotland.shires.create! }
|
49
49
|
#
|
50
50
|
# # Lets merge all the kingdoms:
|
51
|
-
# Service.new(Kingdom.all).
|
51
|
+
# Service.new(Kingdom.all).provide
|
52
52
|
#
|
53
53
|
# # Now union Britain (because it is Britain that was created first)
|
54
54
|
# # has all those 15 shires and 130 thousand men
|
@@ -61,8 +61,8 @@ module ActiveMerge
|
|
61
61
|
#
|
62
62
|
# == Warning!
|
63
63
|
#
|
64
|
-
# The merge provided as a whole transaction. In case any error
|
65
|
-
#
|
64
|
+
# The merge provided as a whole transaction. In case of any error, all
|
65
|
+
# changes roll back.
|
66
66
|
#
|
67
67
|
# Let (see the example above) the shire cannot be rebount to another kingdom:
|
68
68
|
#
|
@@ -70,10 +70,16 @@ module ActiveMerge
|
|
70
70
|
# attr_readonly :kingdom_id
|
71
71
|
# end
|
72
72
|
#
|
73
|
-
#
|
73
|
+
# In this case the merge won't be finished. Not only shires, but also
|
74
|
+
# scots remain living in their old good Scotland!
|
74
75
|
#
|
75
|
-
#
|
76
|
-
#
|
76
|
+
# == Skipping activerecord validations
|
77
|
+
#
|
78
|
+
# You can use the <tt>validate: false</tt> option.
|
79
|
+
#
|
80
|
+
# With this option set any activerecord validation and callback are skipped.
|
81
|
+
#
|
82
|
+
# Service.new(Kingdom.all).provide validate: false
|
77
83
|
#
|
78
84
|
class Service < ActivePatterns::BaseService
|
79
85
|
include ActiveModel::Validations
|
@@ -87,11 +93,11 @@ module ActiveMerge
|
|
87
93
|
validates :items, presence: true
|
88
94
|
|
89
95
|
# Merges all the #items to the #item as a whole transaction
|
90
|
-
def provide
|
96
|
+
def provide(options = {})
|
91
97
|
transaction do
|
92
98
|
items.each do |item|
|
93
99
|
service = SimpleService.new(self.item, item)
|
94
|
-
change(service) { service.provide }
|
100
|
+
change(service) { service.provide(options) }
|
95
101
|
end
|
96
102
|
end
|
97
103
|
end
|
@@ -27,10 +27,10 @@ module ActiveMerge
|
|
27
27
|
|
28
28
|
validates :second, presence: true
|
29
29
|
|
30
|
-
def provide
|
30
|
+
def provide(options = {})
|
31
31
|
transaction do
|
32
|
-
Links.new(second).each { |item, key| rebind(item, key) }
|
33
|
-
|
32
|
+
Links.new(second).each { |item, key| rebind(item, key, options) }
|
33
|
+
remove(options)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -63,11 +63,16 @@ module ActiveMerge
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
# Deletes or destroys second object
|
67
|
+
def remove(validate: true)
|
68
|
+
change(second) { validate ? second.destroy! : second.delete }
|
69
|
+
end
|
70
|
+
|
66
71
|
# Re-assigns given object to the #first
|
67
|
-
def rebind(item, key)
|
72
|
+
def rebind(item, key, options)
|
68
73
|
change item do
|
69
74
|
item.send key, first.id
|
70
|
-
item.save!
|
75
|
+
item.save! options
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
data/lib/active_merge/version.rb
CHANGED
data/lib/active_merge.rb
CHANGED
@@ -20,9 +20,15 @@ module ActiveMerge
|
|
20
20
|
# Lord.all.merge_all # => merges all the class instances
|
21
21
|
# Lord.where("id > :id", id: 100) # => merges instances with id > 100
|
22
22
|
#
|
23
|
+
# You can use the <tt>validate: false</tt> option.
|
24
|
+
#
|
25
|
+
# With this option set any activerecord validation and callback are skipped.
|
26
|
+
#
|
27
|
+
# Post.all.merge_all validate: false
|
28
|
+
#
|
23
29
|
# See details in <tt>ActiveMerge::Service#provide</tt> documentation.
|
24
30
|
#
|
25
|
-
def merge_all
|
26
|
-
ActiveMerge::Service.new(all).provide
|
31
|
+
def merge_all(options = {})
|
32
|
+
ActiveMerge::Service.new(all).provide(options)
|
27
33
|
end
|
28
34
|
end
|
@@ -146,58 +146,184 @@ module ActiveMerge
|
|
146
146
|
let!(:domains) { lords.map{ |lord| lord.domains.create! }}
|
147
147
|
let!(:men) { lords.map{ |lord| lord.men.create! }}
|
148
148
|
let!(:service) { Service.new lords }
|
149
|
+
|
150
|
+
context "if :validate option isn't set" do
|
149
151
|
|
150
|
-
|
152
|
+
context "and no exceptions raised:" do
|
151
153
|
|
152
|
-
|
153
|
-
|
154
|
+
it "merges the objects" do
|
155
|
+
service.provide
|
156
|
+
Lord.count.should eq 1
|
157
|
+
Lord.first.domains.count.should eq 2
|
158
|
+
Lord.first.men.count.should eq 2
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "and the service's own validation returns false:" do
|
163
|
+
|
164
|
+
before { service.stub(:valid?).and_return false }
|
165
|
+
|
166
|
+
it "raises an exception" do
|
167
|
+
expect{ service.provide }.to raise_error
|
168
|
+
end
|
169
|
+
|
170
|
+
it "doesn't make any change to db" do
|
171
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Lord.all.to_a }
|
172
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Domain.all.to_a }
|
173
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Man.all.to_a }
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "and a validation returns false when rebinding a link:" do
|
178
|
+
|
179
|
+
before do
|
180
|
+
Man.any_instance.stub(:valid?).and_return false
|
181
|
+
Man.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
182
|
+
end
|
183
|
+
|
184
|
+
it "raises an exception" do
|
185
|
+
expect{ service.provide }.to raise_error
|
186
|
+
end
|
187
|
+
|
188
|
+
it "doesn't make any change to db" do
|
189
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Lord.all.to_a }
|
190
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Domain.all.to_a }
|
191
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Man.all.to_a }
|
192
|
+
end
|
193
|
+
|
194
|
+
it "adds corresponding errors to the #errors" do
|
195
|
+
begin; service.provide; rescue; end
|
196
|
+
service.errors.should_not be_blank
|
197
|
+
end
|
154
198
|
end
|
155
199
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
200
|
+
context "and a callback returns false when destroying an object:" do
|
201
|
+
|
202
|
+
before do
|
203
|
+
Lord.before_destroy(){ false }
|
204
|
+
Lord.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
205
|
+
end
|
206
|
+
after{ Lord.reset_callbacks(:destroy) }
|
207
|
+
|
208
|
+
it "raises an exception" do
|
209
|
+
expect{ service.provide }.to raise_error
|
210
|
+
end
|
211
|
+
|
212
|
+
it "doesn't make any change to db" do
|
213
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Lord.all.to_a }
|
214
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Domain.all.to_a }
|
215
|
+
expect{ begin; service.provide; rescue; end }.not_to change{ Man.all.to_a }
|
216
|
+
end
|
217
|
+
|
218
|
+
it "adds corresponding errors to the #errors" do
|
219
|
+
begin; service.provide; rescue; end
|
220
|
+
service.errors.should_not be_blank
|
221
|
+
end
|
161
222
|
end
|
162
223
|
end
|
163
224
|
|
164
|
-
context "
|
225
|
+
context "if :validate option is set to false" do
|
226
|
+
|
227
|
+
context "and no exceptions raised:" do
|
165
228
|
|
166
|
-
|
167
|
-
|
168
|
-
|
229
|
+
it "merges the objects" do
|
230
|
+
service.provide validate: false
|
231
|
+
Lord.count.should eq 1
|
232
|
+
Lord.first.domains.count.should eq 2
|
233
|
+
Lord.first.men.count.should eq 2
|
234
|
+
end
|
169
235
|
end
|
170
236
|
|
171
|
-
|
172
|
-
|
237
|
+
context "and a validation returns false when rebinding a link:" do
|
238
|
+
|
239
|
+
before do
|
240
|
+
Man.any_instance.stub(:valid?).and_return false
|
241
|
+
Man.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
242
|
+
end
|
243
|
+
|
244
|
+
it "merges the objects" do
|
245
|
+
service.provide validate: false
|
246
|
+
Lord.count.should eq 1
|
247
|
+
Lord.first.domains.count.should eq 2
|
248
|
+
Lord.first.men.count.should eq 2
|
249
|
+
end
|
173
250
|
end
|
174
251
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
252
|
+
context "and a callback returns false when destroying an object:" do
|
253
|
+
|
254
|
+
before do
|
255
|
+
Lord.before_destroy(){ false }
|
256
|
+
Lord.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
257
|
+
end
|
258
|
+
after{ Lord.reset_callbacks(:destroy) }
|
259
|
+
|
260
|
+
it "merges the objects" do
|
261
|
+
service.provide validate: false
|
262
|
+
Lord.count.should eq 1
|
263
|
+
Lord.first.domains.count.should eq 2
|
264
|
+
Lord.first.men.count.should eq 2
|
181
265
|
end
|
182
266
|
end
|
183
267
|
|
184
|
-
|
185
|
-
|
186
|
-
service.
|
268
|
+
context "and the service's own validation returns false:" do
|
269
|
+
|
270
|
+
before { service.stub(:valid?).and_return false }
|
271
|
+
|
272
|
+
it "raises an exception" do
|
273
|
+
expect{ service.provide validate: false }.to raise_error
|
274
|
+
end
|
275
|
+
|
276
|
+
it "doesn't make any change to db" do
|
277
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Lord.all.to_a }
|
278
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Domain.all.to_a }
|
279
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Man.all.to_a }
|
280
|
+
end
|
187
281
|
end
|
188
|
-
end
|
189
282
|
|
190
|
-
|
283
|
+
context "and db recording fails when rebinding a link:" do
|
191
284
|
|
192
|
-
|
285
|
+
before do
|
286
|
+
Man.any_instance.stub(:save!){ fail }
|
287
|
+
Man.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
288
|
+
end
|
193
289
|
|
194
|
-
|
195
|
-
|
290
|
+
it "raises an exception" do
|
291
|
+
expect{ service.provide validate: false }.to raise_error
|
292
|
+
end
|
293
|
+
|
294
|
+
it "doesn't make any change to db" do
|
295
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Lord.all.to_a }
|
296
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Domain.all.to_a }
|
297
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Man.all.to_a }
|
298
|
+
end
|
299
|
+
|
300
|
+
it "adds corresponding errors to the #errors" do
|
301
|
+
begin; service.provide validate: false; rescue; end
|
302
|
+
service.errors.should_not be_blank
|
303
|
+
end
|
196
304
|
end
|
197
305
|
|
198
|
-
|
199
|
-
|
200
|
-
|
306
|
+
context "and merged object's delete fails:" do
|
307
|
+
|
308
|
+
before do
|
309
|
+
Lord.any_instance.stub(:delete){ fail }
|
310
|
+
Lord.any_instance.stub(:errors).and_return({ base: "record_invalid" })
|
311
|
+
end
|
312
|
+
|
313
|
+
it "raises an exception" do
|
314
|
+
expect{ service.provide validate: false }.to raise_error
|
315
|
+
end
|
316
|
+
|
317
|
+
it "doesn't make any change to db" do
|
318
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Lord.all.to_a }
|
319
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Domain.all.to_a }
|
320
|
+
expect{ begin; service.provide validate: false; rescue; end }.not_to change{ Man.all.to_a }
|
321
|
+
end
|
322
|
+
|
323
|
+
it "adds corresponding errors to the #errors" do
|
324
|
+
begin; service.provide validate: false; rescue; end
|
325
|
+
service.errors.should_not be_blank
|
326
|
+
end
|
201
327
|
end
|
202
328
|
end
|
203
329
|
end
|
data/spec/active_merge_spec.rb
CHANGED
@@ -9,11 +9,15 @@ describe ActiveMerge do
|
|
9
9
|
|
10
10
|
let!(:lords) { 3.times.map{ Lord.create! }}
|
11
11
|
|
12
|
-
it "
|
12
|
+
it "defined" do
|
13
13
|
Lord.should respond_to :merge_all
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "allows options" do
|
17
|
+
Lord.should respond_to(:merge_all).with(1).argument
|
18
|
+
end
|
19
|
+
|
20
|
+
it "merges records from given request" do
|
17
21
|
Lord.where("id < 3").merge_all
|
18
22
|
Lord.all.pluck(:id).should eq [1, 3]
|
19
23
|
end
|
data/spec/dummy/db/test.sqlite3
CHANGED
Binary file
|