active_merge 1.0.5 → 1.1.0
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.
- 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
|