deferring 0.0.4 → 0.0.5
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 +13 -5
- data/README.md +1 -1
- data/lib/deferring.rb +31 -33
- data/lib/deferring/deferred_association.rb +11 -10
- data/lib/deferring/version.rb +1 -1
- data/spec/lib/{deferring_spec.rb → deferring_habtm_spec.rb} +90 -135
- data/spec/lib/deferring_has_many_spec.rb +34 -15
- data/spec/lib/deferring_nested_attributes_spec.rb +50 -0
- data/spec/support/models.rb +12 -4
- metadata +25 -25
- data/gemfiles/rails_4.gemfile +0 -7
- data/gemfiles/rails_4.gemfile.lock +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Mzk2OTEyOGRmMjBkNTIzNzk4MGQ4MmM4ZWE1MmE1MmY4NzliYjEwZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDkyYjNmYWU0OGUxZDFkODkyODhiM2E0MDY3MDgyMDFkYTJiYjlkMw==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjA2OThhM2I1ZTY3MzM5NDA4ZmJlMTQ1YzhmZTQzNmFlZGMzODJmZjc5MjE1
|
10
|
+
NzljMzE0YzE0NmE0MzMzNjJjYjM4MTU1MzU0ZTFmNzM1MDZmNWU5MTBiZTEz
|
11
|
+
MjExYTlmZWVkZDJlM2I3NTIwNTVkNDNjYWE4YWE4MWY4NjAwZmI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YmEwMDNlN2E5ZmVhMGUzODc0YzJmMTAxNWI4MDE5NTNhNDBlNGEyYjFiMzZj
|
14
|
+
YzhjM2U4MGFlZjQzYjFkMWJmNDEwMzEzZmJkMTVmZjMzMjhkZmQ0Mzk2OGIz
|
15
|
+
OWRhMzk2OTExZjczMjk2NDZmNjQ2ZmZmZGZkMGZiOTM4YTMxNGM=
|
data/README.md
CHANGED
@@ -353,7 +353,7 @@ Person.first.teams
|
|
353
353
|
|
354
354
|
##### Original association
|
355
355
|
|
356
|
-
The original association is renamed to
|
356
|
+
The original association is renamed to `original_association_name`. So, the
|
357
357
|
original association of the deferred association named `teams` can be accessed
|
358
358
|
by using `original_teams`.
|
359
359
|
|
data/lib/deferring.rb
CHANGED
@@ -25,9 +25,10 @@ module Deferring
|
|
25
25
|
def deferred_has_many(*args)
|
26
26
|
options = args.extract_options!
|
27
27
|
listeners = create_callback_listeners!(options)
|
28
|
+
inverse_association_name = options.fetch(:as, self.name.underscore.to_sym)
|
28
29
|
|
29
30
|
has_many(*args, options)
|
30
|
-
generate_deferred_association_methods(args.first.to_s, listeners)
|
31
|
+
generate_deferred_association_methods(args.first.to_s, listeners, inverse_association_name)
|
31
32
|
end
|
32
33
|
|
33
34
|
def deferred_accepts_nested_attributes_for(*args)
|
@@ -36,7 +37,7 @@ module Deferring
|
|
36
37
|
|
37
38
|
# teams_attributes=
|
38
39
|
define_method :"#{association_name}_attributes=" do |records|
|
39
|
-
find_or_create_deferred_association(association_name, [])
|
40
|
+
find_or_create_deferred_association(association_name, [], nil)
|
40
41
|
|
41
42
|
# Remove the records that are to be destroyed from the ids that are to be
|
42
43
|
# assigned to the DeferredAssociation instance.
|
@@ -52,7 +53,7 @@ module Deferring
|
|
52
53
|
|
53
54
|
private
|
54
55
|
|
55
|
-
def generate_deferred_association_methods(association_name, listeners)
|
56
|
+
def generate_deferred_association_methods(association_name, listeners, inverse_association_name = nil)
|
56
57
|
# Store the original accessor methods of the association.
|
57
58
|
alias_method :"original_#{association_name}", :"#{association_name}"
|
58
59
|
alias_method :"original_#{association_name}=", :"#{association_name}="
|
@@ -66,7 +67,7 @@ module Deferring
|
|
66
67
|
# if none are found.
|
67
68
|
# TODO: add force_reload argument?
|
68
69
|
define_method :"#{association_name}" do
|
69
|
-
find_or_create_deferred_association(association_name, listeners)
|
70
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
70
71
|
send(:"deferred_#{association_name}")
|
71
72
|
end
|
72
73
|
|
@@ -75,7 +76,7 @@ module Deferring
|
|
75
76
|
# Replaces the collection's content by deleting and adding objects as
|
76
77
|
# appropriate.
|
77
78
|
define_method :"#{association_name}=" do |objects|
|
78
|
-
find_or_create_deferred_association(association_name, listeners)
|
79
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
79
80
|
send(:"deferred_#{association_name}").objects = objects
|
80
81
|
end
|
81
82
|
|
@@ -84,7 +85,7 @@ module Deferring
|
|
84
85
|
# Replace the collection by the objects identified by the primary keys in
|
85
86
|
# ids.
|
86
87
|
define_method :"#{association_name.singularize}_ids=" do |ids|
|
87
|
-
find_or_create_deferred_association(association_name, listeners)
|
88
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
88
89
|
|
89
90
|
klass = self.class.reflect_on_association(:"#{association_name}").klass
|
90
91
|
objects = klass.find(ids.reject(&:blank?))
|
@@ -95,13 +96,13 @@ module Deferring
|
|
95
96
|
#
|
96
97
|
# Returns an array of the associated objects' ids.
|
97
98
|
define_method :"#{association_name.singularize}_ids" do
|
98
|
-
find_or_create_deferred_association(association_name, listeners)
|
99
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
99
100
|
send(:"deferred_#{association_name}").ids
|
100
101
|
end
|
101
102
|
|
102
|
-
#
|
103
|
+
# collection_singular_checked
|
103
104
|
attr_accessor :"#{association_name}_checked"
|
104
|
-
#
|
105
|
+
# collection_singular_checked=
|
105
106
|
define_method(:"#{association_name}_checked=") do |ids|
|
106
107
|
send(:"#{association_name.singularize}_ids=", ids.split(','))
|
107
108
|
end
|
@@ -109,50 +110,47 @@ module Deferring
|
|
109
110
|
# the save after the parent object has been saved
|
110
111
|
after_save :"perform_deferred_#{association_name}_save!"
|
111
112
|
define_method :"perform_deferred_#{association_name}_save!" do
|
112
|
-
|
113
|
+
|
114
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
113
115
|
|
114
116
|
# Send the objects of our delegated association to the original
|
115
117
|
# association and store the result.
|
116
118
|
send(:"original_#{association_name}=", send(:"deferred_#{association_name}").objects)
|
117
119
|
|
118
120
|
# Store the new value of the association into our delegated association.
|
119
|
-
|
120
|
-
:"deferred_#{association_name}=",
|
121
|
-
DeferredAssociation.new(send(:"original_#{association_name}"), association_name))
|
122
|
-
listeners.each do |event_name, callback_method|
|
123
|
-
l = DeferredCallbackListener.new(event_name, self, callback_method)
|
124
|
-
send(:"deferred_#{association_name}").add_callback_listener(l)
|
125
|
-
end
|
121
|
+
save_deferred_association(association_name, listeners, inverse_association_name)
|
126
122
|
end
|
127
123
|
|
128
124
|
define_method :"reload_with_deferred_#{association_name}" do |*args|
|
129
|
-
find_or_create_deferred_association(association_name, listeners)
|
125
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
130
126
|
|
131
127
|
send(:"reload_without_deferred_#{association_name}", *args).tap do
|
132
|
-
|
133
|
-
:"deferred_#{association_name}=",
|
134
|
-
DeferredAssociation.new(send(:"original_#{association_name}"), association_name))
|
135
|
-
listeners.each do |event_name, callback_method|
|
136
|
-
l = DeferredCallbackListener.new(event_name, self, callback_method)
|
137
|
-
send(:"deferred_#{association_name}").add_callback_listener(l)
|
138
|
-
end
|
128
|
+
save_deferred_association(association_name, listeners, inverse_association_name)
|
139
129
|
end
|
140
130
|
end
|
141
131
|
alias_method_chain :reload, :"deferred_#{association_name}"
|
142
132
|
|
133
|
+
generate_save_deferred_assocation_method
|
143
134
|
generate_find_or_create_deferred_association_method
|
144
135
|
end
|
145
136
|
|
137
|
+
def generate_save_deferred_assocation_method
|
138
|
+
define_method :save_deferred_association do |name, listeners, inverse_association_name|
|
139
|
+
klass = self.class.reflect_on_association(:"#{name}").klass
|
140
|
+
send(
|
141
|
+
:"deferred_#{name}=",
|
142
|
+
DeferredAssociation.new(send(:"original_#{name}"), klass, self, inverse_association_name))
|
143
|
+
listeners.each do |event_name, callback_method|
|
144
|
+
l = DeferredCallbackListener.new(event_name, self, callback_method)
|
145
|
+
send(:"deferred_#{name}").add_callback_listener(l)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
146
150
|
def generate_find_or_create_deferred_association_method
|
147
|
-
define_method :find_or_create_deferred_association do |name, listeners|
|
151
|
+
define_method :find_or_create_deferred_association do |name, listeners, inverse_association_name|
|
148
152
|
if send(:"deferred_#{name}").nil?
|
149
|
-
|
150
|
-
:"deferred_#{name}=",
|
151
|
-
DeferredAssociation.new(send(:"original_#{name}"), name))
|
152
|
-
listeners.each do |event_name, callback_method|
|
153
|
-
l = DeferredCallbackListener.new(event_name, self, callback_method)
|
154
|
-
send(:"deferred_#{name}").add_callback_listener(l)
|
155
|
-
end
|
153
|
+
save_deferred_association(name, listeners, inverse_association_name)
|
156
154
|
end
|
157
155
|
end
|
158
156
|
end
|
@@ -7,12 +7,14 @@ module Deferring
|
|
7
7
|
# TODO: Write tests for enumerable.
|
8
8
|
include Enumerable
|
9
9
|
|
10
|
-
attr_reader :load_state
|
10
|
+
attr_reader :load_state, :klass, :parent_record, :inverse_name
|
11
11
|
|
12
|
-
def initialize(original_association,
|
12
|
+
def initialize(original_association, klass, parent_record, inverse_name)
|
13
13
|
super(original_association)
|
14
|
-
@
|
15
|
-
@
|
14
|
+
@load_state = :ghost
|
15
|
+
@klass = klass
|
16
|
+
@parent_record = parent_record
|
17
|
+
@inverse_name = inverse_name
|
16
18
|
end
|
17
19
|
alias_method :original_association, :__getobj__
|
18
20
|
|
@@ -111,14 +113,13 @@ module Deferring
|
|
111
113
|
end
|
112
114
|
|
113
115
|
def build(*args, &block)
|
114
|
-
|
116
|
+
klass.new(*args, &block).tap do |record|
|
115
117
|
run_deferring_callbacks(:link, record) do
|
116
|
-
|
118
|
+
if inverse_name && record.class.reflect_on_association(inverse_name)
|
119
|
+
record.send(:"#{inverse_name}=", parent_record)
|
120
|
+
end
|
117
121
|
|
118
|
-
|
119
|
-
# didn't do this, the new record would be saved to the database when
|
120
|
-
# saving the parent object (and not after, as we want).
|
121
|
-
association.reload
|
122
|
+
objects.push(record)
|
122
123
|
end
|
123
124
|
end
|
124
125
|
end
|
data/lib/deferring/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe 'deferred
|
3
|
+
RSpec.describe 'deferred has_and_belongs_to_many associations' do
|
4
4
|
|
5
5
|
before :each do
|
6
6
|
Person.create!(name: 'Alice')
|
@@ -127,6 +127,43 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
xit 'deferred habtm <=> regular habtm' do
|
131
|
+
alice = Person.where(name: 'Alice').first
|
132
|
+
bob = Person.where(name: 'Bob').first
|
133
|
+
|
134
|
+
team = Team.first
|
135
|
+
team.people << alice << bob
|
136
|
+
team.save!
|
137
|
+
|
138
|
+
bob.reload
|
139
|
+
expect(bob.teams.size).to eq(1)
|
140
|
+
|
141
|
+
alice.reload
|
142
|
+
expect(alice.teams.size).to eq(1)
|
143
|
+
|
144
|
+
team.people.create!(name: 'Chuck')
|
145
|
+
expect(team).to_not be_valid
|
146
|
+
|
147
|
+
bob.reload
|
148
|
+
alice.reload
|
149
|
+
|
150
|
+
expect(bob).to_not be_valid
|
151
|
+
expect(alice).to_not be_valid
|
152
|
+
|
153
|
+
expect(bob.save).to be_falsey
|
154
|
+
expect(alice.save).to be_falsey
|
155
|
+
end
|
156
|
+
|
157
|
+
xit 'does not validate records when validate: false' do
|
158
|
+
pending 'validate: false does not work' do
|
159
|
+
alice = Person.where(name: 'Alice').first
|
160
|
+
alice.teams.build(name: nil)
|
161
|
+
alice.save!
|
162
|
+
|
163
|
+
expect(alice.teams.size).to eq 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
130
167
|
end
|
131
168
|
|
132
169
|
describe 'preloading' do
|
@@ -277,13 +314,13 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
277
314
|
describe 'callbacks' do
|
278
315
|
|
279
316
|
before(:example) do
|
280
|
-
bob = Person.first
|
317
|
+
bob = Person.where(name: 'Bob').first
|
281
318
|
bob.teams = [Team.find(3)]
|
282
319
|
bob.save!
|
283
320
|
end
|
284
321
|
|
285
322
|
it 'calls the link callbacks when adding a record using <<' do
|
286
|
-
bob = Person.first
|
323
|
+
bob = Person.where(name: 'Bob').first
|
287
324
|
bob.teams << Team.find(1)
|
288
325
|
|
289
326
|
expect(bob.audit_log.length).to eq(2)
|
@@ -294,7 +331,7 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
294
331
|
end
|
295
332
|
|
296
333
|
it 'calls the link callbacks when adding a record using push' do
|
297
|
-
bob = Person.first
|
334
|
+
bob = Person.where(name: 'Bob').first
|
298
335
|
bob.teams.push(Team.find(1))
|
299
336
|
|
300
337
|
expect(bob.audit_log.length).to eq(2)
|
@@ -305,7 +342,7 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
305
342
|
end
|
306
343
|
|
307
344
|
it 'calls the link callbacks when adding a record using append' do
|
308
|
-
bob = Person.first
|
345
|
+
bob = Person.where(name: 'Bob').first
|
309
346
|
bob.teams.append(Team.find(1))
|
310
347
|
|
311
348
|
expect(bob.audit_log.length).to eq(2)
|
@@ -315,8 +352,19 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
315
352
|
])
|
316
353
|
end
|
317
354
|
|
355
|
+
it 'calls the link callbacks when adding a record using build' do
|
356
|
+
bob = Person.where(name: 'Bob').first
|
357
|
+
bob.teams.build(name: 'Foooo')
|
358
|
+
|
359
|
+
expect(bob.audit_log.length).to eq(2)
|
360
|
+
expect(bob.audit_log).to eq([
|
361
|
+
'Before linking new team',
|
362
|
+
'After linking new team'
|
363
|
+
])
|
364
|
+
end
|
365
|
+
|
318
366
|
it 'only calls the Rails callbacks when creating a record on the association using create' do
|
319
|
-
bob = Person.first
|
367
|
+
bob = Person.where(name: 'Bob').first
|
320
368
|
bob.teams.create(name: 'HR')
|
321
369
|
|
322
370
|
expect(bob.audit_log.length).to eq(2)
|
@@ -327,7 +375,7 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
327
375
|
end
|
328
376
|
|
329
377
|
it 'only calls the Rails callbacks when creating a record on the association using create!' do
|
330
|
-
bob = Person.first
|
378
|
+
bob = Person.where(name: 'Bob').first
|
331
379
|
bob.teams.create!(name: 'HR')
|
332
380
|
|
333
381
|
expect(bob.audit_log.length).to eq(2)
|
@@ -338,7 +386,7 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
338
386
|
end
|
339
387
|
|
340
388
|
it 'calls the unlink callbacks when removing a record using delete' do
|
341
|
-
bob = Person.first
|
389
|
+
bob = Person.where(name: 'Bob').first
|
342
390
|
bob.teams.delete(Team.find(3))
|
343
391
|
|
344
392
|
expect(bob.audit_log.length).to eq(2)
|
@@ -349,7 +397,7 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
349
397
|
end
|
350
398
|
|
351
399
|
it 'only calls the rails callbacks when removing a record using destroy' do
|
352
|
-
bob = Person.first
|
400
|
+
bob = Person.where(name: 'Bob').first
|
353
401
|
bob.teams.destroy(3)
|
354
402
|
|
355
403
|
expect(bob.audit_log.length).to eq(2)
|
@@ -360,23 +408,24 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
360
408
|
end
|
361
409
|
|
362
410
|
it 'calls the regular Rails callbacks after saving' do
|
363
|
-
bob = Person.first
|
411
|
+
bob = Person.where(name: 'Bob').first
|
364
412
|
bob.teams = [Team.find(1), Team.find(3)]
|
365
413
|
bob.save!
|
366
414
|
|
367
|
-
bob = Person.first
|
415
|
+
bob = Person.where(name: 'Bob').first
|
368
416
|
bob.teams.delete(Team.find(1))
|
369
417
|
bob.teams << Team.find(2)
|
418
|
+
bob.teams.build(name: 'Service Desk')
|
370
419
|
bob.save!
|
371
420
|
|
372
|
-
expect(bob.audit_log.length).to eq(
|
421
|
+
expect(bob.audit_log.length).to eq(12)
|
373
422
|
expect(bob.audit_log).to eq([
|
374
423
|
'Before unlinking team 1', 'After unlinking team 1',
|
375
|
-
'Before linking team 2',
|
376
|
-
'Before
|
377
|
-
'After removing team 1',
|
378
|
-
'Before adding team 2',
|
379
|
-
'After adding team
|
424
|
+
'Before linking team 2', 'After linking team 2',
|
425
|
+
'Before linking new team', 'After linking new team',
|
426
|
+
'Before removing team 1', 'After removing team 1',
|
427
|
+
'Before adding team 2', 'After adding team 2',
|
428
|
+
'Before adding new team', 'After adding team 4'
|
380
429
|
])
|
381
430
|
end
|
382
431
|
|
@@ -460,8 +509,6 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
460
509
|
|
461
510
|
end
|
462
511
|
|
463
|
-
# TODO: Clean up tests.
|
464
|
-
|
465
512
|
describe 'active record api' do
|
466
513
|
|
467
514
|
# it 'should execute first on deferred association' do
|
@@ -480,28 +527,33 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
480
527
|
# expect(Person.first.teams.last).to eq(operations)
|
481
528
|
# end
|
482
529
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
530
|
+
describe '#build' do
|
531
|
+
|
532
|
+
it 'builds a new record' do
|
533
|
+
p = Person.first
|
534
|
+
p.teams.build(name: 'Service Desk')
|
535
|
+
|
536
|
+
expect(p.teams[0]).to be_new_record
|
537
|
+
expect{ p.save }.to change{ Person.first.teams.count }.from(0).to(1)
|
538
|
+
end
|
488
539
|
|
489
|
-
it 'should build and save a new record' do
|
490
|
-
p = Person.first
|
491
|
-
p.teams.build(name: 'Service Desk')
|
492
|
-
expect(p.teams[0]).to be_new_record
|
493
|
-
expect(Person.first.teams.size).to eq(0)
|
494
|
-
p.save
|
495
|
-
expect(Person.first.teams.size).to eq(1)
|
496
540
|
end
|
497
541
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
542
|
+
describe '#create!' do
|
543
|
+
|
544
|
+
it 'should create a persisted record' do
|
545
|
+
p = Person.first
|
546
|
+
p.teams.create!(:name => 'Service Desk')
|
547
|
+
expect(p.teams[0]).to_not be_new_record
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'should automatically save the created record' do
|
551
|
+
p = Person.first
|
552
|
+
p.teams.create!(:name => 'Service Desk')
|
553
|
+
expect(Person.first.teams.size).to eq(1)
|
554
|
+
expect{ p.save }.to_not change{ Person.first.teams.count }
|
555
|
+
end
|
556
|
+
|
505
557
|
end
|
506
558
|
|
507
559
|
it 'should allow ActiveRecord::QueryMethods' do
|
@@ -525,101 +577,4 @@ RSpec.describe 'deferred has-and-belongs-to-many associations' do
|
|
525
577
|
|
526
578
|
end
|
527
579
|
|
528
|
-
describe 'accepts_nested_attributes' do
|
529
|
-
# TODO: Write more tests.
|
530
|
-
it 'should mass assign' do
|
531
|
-
p = Person.first
|
532
|
-
p.teams << Team.first << Team.last << Team.find(2)
|
533
|
-
p.save
|
534
|
-
|
535
|
-
# Destroy team 2 and 3. Keep team 1.
|
536
|
-
p = Person.first
|
537
|
-
p.attributes = {
|
538
|
-
teams_attributes: [
|
539
|
-
{ id: 1 },
|
540
|
-
{ id: 3, _destroy: true },
|
541
|
-
{ id: 2, _destroy: true }
|
542
|
-
]
|
543
|
-
}
|
544
|
-
expect(p.teams.length).to eq(1)
|
545
|
-
expect(p.team_ids.sort).to eq([1])
|
546
|
-
|
547
|
-
Person.first
|
548
|
-
expect(Person.first.teams.length).to eq(3)
|
549
|
-
expect(Person.first.team_ids.sort).to eq([1,2,3])
|
550
|
-
|
551
|
-
p.save!
|
552
|
-
|
553
|
-
p = Person.first
|
554
|
-
expect(p.teams.length).to eq(1)
|
555
|
-
expect(p.team_ids.sort).to eq([1])
|
556
|
-
end
|
557
|
-
|
558
|
-
it 'should mass assign' do
|
559
|
-
p = Person.first
|
560
|
-
p.teams << Team.first << Team.last << Team.find(2)
|
561
|
-
p.save
|
562
|
-
|
563
|
-
# Destroy team 2 and 3. Keep team 1.
|
564
|
-
p = Person.first
|
565
|
-
p.teams_attributes = [
|
566
|
-
{ id: 1 },
|
567
|
-
{ id: 3, _destroy: true },
|
568
|
-
{ id: 2, _destroy: true }
|
569
|
-
]
|
570
|
-
expect(p.teams.length).to eq(1)
|
571
|
-
expect(p.team_ids.sort).to eq([1])
|
572
|
-
|
573
|
-
Person.first
|
574
|
-
expect(Person.first.teams.length).to eq(3)
|
575
|
-
expect(Person.first.team_ids.sort).to eq([1,2,3])
|
576
|
-
|
577
|
-
p.save!
|
578
|
-
|
579
|
-
p = Person.first
|
580
|
-
expect(p.teams.length).to eq(1)
|
581
|
-
expect(p.team_ids.sort).to eq([1])
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
describe 'validations' do
|
586
|
-
|
587
|
-
xit 'deferred habtm <=> regular habtm' do
|
588
|
-
alice = Person.where(name: 'Alice').first
|
589
|
-
bob = Person.where(name: 'Bob').first
|
590
|
-
|
591
|
-
team = Team.first
|
592
|
-
team.people << alice << bob
|
593
|
-
team.save!
|
594
|
-
|
595
|
-
bob.reload
|
596
|
-
expect(bob.teams.size).to eq(1)
|
597
|
-
|
598
|
-
alice.reload
|
599
|
-
expect(alice.teams.size).to eq(1)
|
600
|
-
|
601
|
-
team.people.create!(name: 'Chuck')
|
602
|
-
expect(team).to_not be_valid
|
603
|
-
|
604
|
-
bob.reload
|
605
|
-
alice.reload
|
606
|
-
|
607
|
-
expect(bob).to_not be_valid
|
608
|
-
expect(alice).to_not be_valid
|
609
|
-
|
610
|
-
expect(bob.save).to be_falsey
|
611
|
-
expect(alice.save).to be_falsey
|
612
|
-
end
|
613
|
-
|
614
|
-
xit 'does not validate records when validate: false' do
|
615
|
-
pending 'validate: false does not work' do
|
616
|
-
alice = Person.where(name: 'Alice').first
|
617
|
-
alice.teams.build(name: nil)
|
618
|
-
alice.save!
|
619
|
-
|
620
|
-
expect(alice.teams.size).to eq 1
|
621
|
-
end
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
580
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe 'deferred
|
3
|
+
RSpec.describe 'deferred has_many associations' do
|
4
4
|
|
5
5
|
before(:each) do
|
6
6
|
Person.create!(name: 'Alice')
|
@@ -17,43 +17,62 @@ RSpec.describe 'deferred has-many association' do
|
|
17
17
|
|
18
18
|
describe 'preloading associations' do
|
19
19
|
before do
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
bob = Person.where(name: 'Bob').first
|
21
|
+
bob.issues << printer_issue << db_issue
|
22
|
+
bob.save!
|
23
23
|
end
|
24
24
|
|
25
25
|
if rails30 # old-style preload
|
26
26
|
it 'should have loaded the association' do
|
27
|
-
|
28
|
-
Person.send(:preload_associations,
|
29
|
-
expect(
|
30
|
-
expect(
|
27
|
+
bob = Person.where(name: 'Bob').first
|
28
|
+
Person.send(:preload_associations, bob, [:issues])
|
29
|
+
expect(bob.issues.loaded?).to be_truthy
|
30
|
+
expect(bob.issue_ids).to eq [printer_issue.id, db_issue.id]
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
if rails32 || rails4
|
35
35
|
it 'should have loaded the association when pre-loading' do
|
36
36
|
people = Person.preload(:issues)
|
37
|
-
expect(people[
|
38
|
-
expect(people[
|
37
|
+
expect(people[1].issues.loaded?).to be_truthy
|
38
|
+
expect(people[1].issue_ids).to eq [printer_issue.id, db_issue.id]
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'should have loaded the association when eager loading' do
|
42
42
|
people = Person.eager_load(:issues)
|
43
|
-
expect(people[
|
44
|
-
expect(people[
|
43
|
+
expect(people[1].issues.loaded?).to be_truthy
|
44
|
+
expect(people[1].issue_ids).to eq [db_issue.id, printer_issue.id]
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'should have loaded the association when joining' do
|
48
48
|
people = Person.includes(:issues).all
|
49
|
-
expect(people[
|
50
|
-
expect(people[
|
49
|
+
expect(people[1].issues.loaded?).to be_truthy
|
50
|
+
expect(people[1].issue_ids).to eq [printer_issue.id, db_issue.id]
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'should not have loaded the association when using a regular query' do
|
55
55
|
people = Person.all
|
56
|
-
expect(people[
|
56
|
+
expect(people[1].issues.loaded?).to be_falsey
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'active record api' do
|
61
|
+
|
62
|
+
describe '#build' do
|
63
|
+
it 'builds a new record' do
|
64
|
+
bob = Person.where(name: 'Bob').first
|
65
|
+
bob.issues.build(subject: 'I need coffee!')
|
66
|
+
|
67
|
+
expect(bob.issues.last).to be_new_record
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'sets the belongs_to association of the built record' do
|
71
|
+
bob = Person.where(name: 'Bob').first
|
72
|
+
bob.issues.build(subject: 'I need coffee!')
|
73
|
+
|
74
|
+
expect(bob.issues.last.person).to eq bob
|
75
|
+
end
|
57
76
|
end
|
58
77
|
end
|
59
78
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'deferred accepts_nested_attributes' do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
Person.create!(name: 'Alice')
|
7
|
+
Person.create!(name: 'Bob')
|
8
|
+
|
9
|
+
Team.create!(name: 'Database Administration')
|
10
|
+
Team.create!(name: 'End-User Support')
|
11
|
+
Team.create!(name: 'Operations')
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:bob) { Person.where(name: 'Bob').first }
|
15
|
+
|
16
|
+
let(:dba) { Team.where(name: 'Database Administration').first }
|
17
|
+
let(:support) { Team.where(name: 'End-User Support').first }
|
18
|
+
let(:operations) { Team.where(name: 'Operations').first }
|
19
|
+
|
20
|
+
# TODO: Write more tests.
|
21
|
+
|
22
|
+
it 'should mass assign' do
|
23
|
+
p = Person.first
|
24
|
+
p.teams << Team.first << Team.last << Team.find(2)
|
25
|
+
p.save
|
26
|
+
|
27
|
+
# Destroy team 2 and 3. Keep team 1.
|
28
|
+
p = Person.first
|
29
|
+
p.attributes = {
|
30
|
+
teams_attributes: [
|
31
|
+
{ id: 1 },
|
32
|
+
{ id: 3, _destroy: true },
|
33
|
+
{ id: 2, _destroy: true }
|
34
|
+
]
|
35
|
+
}
|
36
|
+
expect(p.teams.length).to eq(1)
|
37
|
+
expect(p.team_ids.sort).to eq([1])
|
38
|
+
|
39
|
+
Person.first
|
40
|
+
expect(Person.first.teams.length).to eq(3)
|
41
|
+
expect(Person.first.team_ids.sort).to eq([1,2,3])
|
42
|
+
|
43
|
+
p.save!
|
44
|
+
|
45
|
+
p = Person.first
|
46
|
+
expect(p.teams.length).to eq(1)
|
47
|
+
expect(p.team_ids.sort).to eq([1])
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/spec/support/models.rb
CHANGED
@@ -28,11 +28,19 @@ class Person < ActiveRecord::Base
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def link_team(team)
|
31
|
-
|
31
|
+
if team.new_record?
|
32
|
+
log("Before linking new team")
|
33
|
+
else
|
34
|
+
log("Before linking team #{team.id}")
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def linked_team(team)
|
35
|
-
|
39
|
+
if team.new_record?
|
40
|
+
log("After linking new team")
|
41
|
+
else
|
42
|
+
log("After linking team #{team.id}")
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
38
46
|
def unlink_team(team)
|
@@ -63,11 +71,11 @@ class Person < ActiveRecord::Base
|
|
63
71
|
log("After removing team #{team.id}")
|
64
72
|
end
|
65
73
|
|
66
|
-
def
|
74
|
+
def remove_issue(issue)
|
67
75
|
log("Before removing issue #{issue.id}")
|
68
76
|
end
|
69
77
|
|
70
|
-
def
|
78
|
+
def removed_issue(issue)
|
71
79
|
log("After removing issue #{issue.id}")
|
72
80
|
end
|
73
81
|
end
|
metadata
CHANGED
@@ -1,100 +1,100 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deferring
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin Roestenburg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ! '>'
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '3.0'
|
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
26
|
version: '3.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ! '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ! '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ! '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ! '>='
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ! '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: appraisal
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ! '>='
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ! '>='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: "\n The Deferring gem makes it possible to defer saving ActiveRecord\n
|
97
|
+
description: ! "\n The Deferring gem makes it possible to defer saving ActiveRecord\n
|
98
98
|
\ associations until the parent object is saved.\n "
|
99
99
|
email:
|
100
100
|
- robin@roestenburg.io
|
@@ -102,9 +102,9 @@ executables: []
|
|
102
102
|
extensions: []
|
103
103
|
extra_rdoc_files: []
|
104
104
|
files:
|
105
|
-
-
|
106
|
-
-
|
107
|
-
-
|
105
|
+
- .gitignore
|
106
|
+
- .rspec
|
107
|
+
- .travis.yml
|
108
108
|
- Appraisals
|
109
109
|
- Gemfile
|
110
110
|
- LICENSE.txt
|
@@ -115,8 +115,6 @@ files:
|
|
115
115
|
- gemfiles/rails_30.gemfile.lock
|
116
116
|
- gemfiles/rails_32.gemfile
|
117
117
|
- gemfiles/rails_32.gemfile.lock
|
118
|
-
- gemfiles/rails_4.gemfile
|
119
|
-
- gemfiles/rails_4.gemfile.lock
|
120
118
|
- gemfiles/rails_40.gemfile
|
121
119
|
- gemfiles/rails_40.gemfile.lock
|
122
120
|
- gemfiles/rails_41.gemfile
|
@@ -125,8 +123,9 @@ files:
|
|
125
123
|
- lib/deferring/deferred_association.rb
|
126
124
|
- lib/deferring/deferred_callback_listener.rb
|
127
125
|
- lib/deferring/version.rb
|
126
|
+
- spec/lib/deferring_habtm_spec.rb
|
128
127
|
- spec/lib/deferring_has_many_spec.rb
|
129
|
-
- spec/lib/
|
128
|
+
- spec/lib/deferring_nested_attributes_spec.rb
|
130
129
|
- spec/spec_helper.rb
|
131
130
|
- spec/support/active_record.rb
|
132
131
|
- spec/support/models.rb
|
@@ -141,23 +140,24 @@ require_paths:
|
|
141
140
|
- lib
|
142
141
|
required_ruby_version: !ruby/object:Gem::Requirement
|
143
142
|
requirements:
|
144
|
-
- -
|
143
|
+
- - ! '>='
|
145
144
|
- !ruby/object:Gem::Version
|
146
145
|
version: '0'
|
147
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
147
|
requirements:
|
149
|
-
- -
|
148
|
+
- - ! '>='
|
150
149
|
- !ruby/object:Gem::Version
|
151
150
|
version: '0'
|
152
151
|
requirements: []
|
153
152
|
rubyforge_project:
|
154
|
-
rubygems_version: 2.
|
153
|
+
rubygems_version: 2.4.1
|
155
154
|
signing_key:
|
156
155
|
specification_version: 4
|
157
156
|
summary: Defer saving ActiveRecord associations until parent is saved
|
158
157
|
test_files:
|
158
|
+
- spec/lib/deferring_habtm_spec.rb
|
159
159
|
- spec/lib/deferring_has_many_spec.rb
|
160
|
-
- spec/lib/
|
160
|
+
- spec/lib/deferring_nested_attributes_spec.rb
|
161
161
|
- spec/spec_helper.rb
|
162
162
|
- spec/support/active_record.rb
|
163
163
|
- spec/support/models.rb
|
data/gemfiles/rails_4.gemfile
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ..
|
3
|
-
specs:
|
4
|
-
raincheck (0.0.1)
|
5
|
-
activerecord (> 3.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (4.1.0)
|
11
|
-
activesupport (= 4.1.0)
|
12
|
-
builder (~> 3.1)
|
13
|
-
activerecord (4.1.0)
|
14
|
-
activemodel (= 4.1.0)
|
15
|
-
activesupport (= 4.1.0)
|
16
|
-
arel (~> 5.0.0)
|
17
|
-
activesupport (4.1.0)
|
18
|
-
i18n (~> 0.6, >= 0.6.9)
|
19
|
-
json (~> 1.7, >= 1.7.7)
|
20
|
-
minitest (~> 5.1)
|
21
|
-
thread_safe (~> 0.1)
|
22
|
-
tzinfo (~> 1.1)
|
23
|
-
appraisal (1.0.0)
|
24
|
-
bundler
|
25
|
-
rake
|
26
|
-
thor (>= 0.14.0)
|
27
|
-
arel (5.0.1.20140414130214)
|
28
|
-
builder (3.2.2)
|
29
|
-
diff-lcs (1.2.5)
|
30
|
-
i18n (0.6.9)
|
31
|
-
json (1.8.1)
|
32
|
-
minitest (5.3.3)
|
33
|
-
rake (10.3.1)
|
34
|
-
rspec (2.14.1)
|
35
|
-
rspec-core (~> 2.14.0)
|
36
|
-
rspec-expectations (~> 2.14.0)
|
37
|
-
rspec-mocks (~> 2.14.0)
|
38
|
-
rspec-core (2.14.8)
|
39
|
-
rspec-expectations (2.14.5)
|
40
|
-
diff-lcs (>= 1.1.3, < 2.0)
|
41
|
-
rspec-mocks (2.14.6)
|
42
|
-
sqlite3 (1.3.9)
|
43
|
-
thor (0.19.1)
|
44
|
-
thread_safe (0.3.3)
|
45
|
-
tzinfo (1.1.0)
|
46
|
-
thread_safe (~> 0.1)
|
47
|
-
|
48
|
-
PLATFORMS
|
49
|
-
ruby
|
50
|
-
|
51
|
-
DEPENDENCIES
|
52
|
-
activerecord (= 4.1.0)
|
53
|
-
appraisal
|
54
|
-
bundler (~> 1.3)
|
55
|
-
raincheck!
|
56
|
-
rake
|
57
|
-
rspec
|
58
|
-
sqlite3
|