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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9908a773d23bf5b0b67850538549bf130646b257
4
- data.tar.gz: e61c2879ca3ff6a256ac575651c69432b97c3c15
3
+ metadata.gz: 3a852fec8deb9d07be3fc9c377d1094a12a3574c
4
+ data.tar.gz: 9dc95d04b550e50ffbf1250c11ee578a35c60729
5
5
  SHA512:
6
- metadata.gz: d189fc4e6e22e3806c1bcf7350130718c4e85170c6d9b51924a7b206cbbdbec9ecae588d339a36bec6fc2650b7a75d3883d51d718663159226b94e7cae6b4511
7
- data.tar.gz: eccd6bbd86184af049cb31f3229b4c575ab9ba478c7b0b4a7e3ba97af443ec62467424761f25f8e5bd89ea9c3446dc000f3f73b789a787a1c16d13eb961fd1ac
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
- == Example
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-LICENSE[link:LICENSE].
51
+ This project rocks and uses MIT LICENSE[link:LICENSE].
@@ -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).submit!
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 occures, all
65
- # the changes rolled back.
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
- # then merging won't be finished.
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
- # Not only shires, but also the scots remain living in their Scotland,
76
- # not in the United Kingdom!
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
- change(second) { second.destroy! }
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
@@ -1,3 +1,3 @@
1
1
  module ActiveMerge
2
- VERSION = "1.0.5"
2
+ VERSION = "1.1.0"
3
3
  end
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
- context "если перепривязка разрешена" do
152
+ context "and no exceptions raised:" do
151
153
 
152
- it "не вызывает исключений" do
153
- expect{ service.provide }.not_to raise_error
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
- it "объединяет записи" do
157
- begin; service.provide; rescue; end
158
- Lord.count.should eq 1
159
- Lord.first.domains.count.should eq 2
160
- Lord.first.men.count.should eq 2
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 "если перепривязка не может быть выполнена" do
225
+ context "if :validate option is set to false" do
226
+
227
+ context "and no exceptions raised:" do
165
228
 
166
- before do
167
- service.items.last.stub(:destroy!).and_raise
168
- service.items.last.stub(:errors).and_return({ base: "Удаление запрещено." })
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
- it "вызывает исключение" do
172
- expect{ service.provide }.to raise_error
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
- it "не объединяет записи" do
176
- begin; service.provide; rescue; end
177
- Lord.count.should eq 2
178
- Lord.all.each do |lord|
179
- lord.domains.count.should eq 1
180
- lord.men.count.should eq 1
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
- it "добавляет ошибки в массив" do
185
- begin; service.provide; rescue; end
186
- service.errors.should_not be_blank
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
- context "если атрибут #items пуст" do
283
+ context "and db recording fails when rebinding a link:" do
191
284
 
192
- before { service.stub(:items).and_return [] }
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
- it "вызывает исключение" do
195
- expect{ service.provide }.to raise_error
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
- it "добавляет ошибки в массив" do
199
- begin; service.provide; rescue; end
200
- service.errors.should_not be_blank
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
@@ -9,11 +9,15 @@ describe ActiveMerge do
9
9
 
10
10
  let!(:lords) { 3.times.map{ Lord.create! }}
11
11
 
12
- it "доступен" do
12
+ it "defined" do
13
13
  Lord.should respond_to :merge_all
14
14
  end
15
15
 
16
- it "объединяет записи указанного запроса" do
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
Binary file