deferring 0.1.5 → 0.2.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/gemfiles/rails_30.gemfile.lock +1 -1
- data/gemfiles/rails_32.gemfile.lock +1 -1
- data/gemfiles/rails_40.gemfile.lock +1 -1
- data/gemfiles/rails_41.gemfile.lock +1 -1
- data/lib/deferring.rb +17 -14
- data/lib/deferring/deferred_association.rb +23 -6
- data/lib/deferring/version.rb +1 -1
- data/spec/lib/deferring_habtm_spec.rb +101 -6
- data/spec/support/models/person.rb +8 -1
- 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: 9640c37fd475fa2857be6686a2f2b726a4f6a6ef
|
4
|
+
data.tar.gz: dfd12c320ff1fdcd8f2331a6b3180c473a894524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8b85bcb7252d0af7f901ea97db87b1b0556ab3061f43d8dd9fa37b5dcc431cd59878a4dd0300571b241036f072a06d6a17d26db6374c0ce30712b2455952844
|
7
|
+
data.tar.gz: a614b0996a14b2ba60b5faa317e9f1d481685442c333dcb459b87f7240fc8b3e8df2a91c237188615a82fd5dcfca4fe62def66ca7ba05afe1ca57e90135f48bb
|
data/lib/deferring.rb
CHANGED
@@ -21,6 +21,7 @@ module Deferring
|
|
21
21
|
args.first.to_s,
|
22
22
|
listeners,
|
23
23
|
autosave: autosave,
|
24
|
+
type: :habtm,
|
24
25
|
validate: validate)
|
25
26
|
end
|
26
27
|
|
@@ -42,6 +43,7 @@ module Deferring
|
|
42
43
|
inverse_association_name: inverse_association_name,
|
43
44
|
autosave: autosave,
|
44
45
|
type: :has_many,
|
46
|
+
dependent: options[:dependent],
|
45
47
|
validate: validate)
|
46
48
|
end
|
47
49
|
|
@@ -136,8 +138,9 @@ module Deferring
|
|
136
138
|
def generate_deferred_association_methods(association_name, listeners, options = {})
|
137
139
|
inverse_association_name = options[:inverse_association_name]
|
138
140
|
autosave = options.fetch(:autosave, true)
|
139
|
-
type = options.fetch(:type
|
141
|
+
type = options.fetch(:type)
|
140
142
|
validate = options.fetch(:validate, true)
|
143
|
+
dependent = options[:dependent]
|
141
144
|
|
142
145
|
# Store the original accessor methods of the association.
|
143
146
|
alias_method :"original_#{association_name}", :"#{association_name}"
|
@@ -152,7 +155,7 @@ module Deferring
|
|
152
155
|
# if none are found.
|
153
156
|
# TODO: add force_reload argument?
|
154
157
|
define_method :"#{association_name}" do
|
155
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
158
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
156
159
|
send(:"deferred_#{association_name}")
|
157
160
|
end
|
158
161
|
|
@@ -161,7 +164,7 @@ module Deferring
|
|
161
164
|
# Replaces the collection's content by deleting and adding objects as
|
162
165
|
# appropriate.
|
163
166
|
define_method :"#{association_name}=" do |objects|
|
164
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
167
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
165
168
|
send(:"deferred_#{association_name}").objects = objects
|
166
169
|
end
|
167
170
|
|
@@ -170,7 +173,7 @@ module Deferring
|
|
170
173
|
# Replace the collection by the objects identified by the primary keys in
|
171
174
|
# ids.
|
172
175
|
define_method :"#{association_name.singularize}_ids=" do |ids|
|
173
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
176
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
174
177
|
|
175
178
|
ids ||= []
|
176
179
|
klass = self.class.reflect_on_association(:"#{association_name}").klass
|
@@ -183,7 +186,7 @@ module Deferring
|
|
183
186
|
#
|
184
187
|
# Returns an array of the associated objects' ids.
|
185
188
|
define_method :"#{association_name.singularize}_ids" do
|
186
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
189
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
187
190
|
send(:"deferred_#{association_name}").ids
|
188
191
|
end
|
189
192
|
|
@@ -197,7 +200,7 @@ module Deferring
|
|
197
200
|
|
198
201
|
after_validation :"perform_deferred_#{association_name}_validation!"
|
199
202
|
define_method :"perform_deferred_#{association_name}_validation!" do
|
200
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
203
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
201
204
|
|
202
205
|
# Do not perform validations for HABTM associations as they are always
|
203
206
|
# validated by Rails upon saving.
|
@@ -233,21 +236,21 @@ module Deferring
|
|
233
236
|
# the save after the parent object has been saved
|
234
237
|
after_save :"perform_deferred_#{association_name}_save!"
|
235
238
|
define_method :"perform_deferred_#{association_name}_save!" do
|
236
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
239
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
237
240
|
|
238
241
|
# Send the objects of our delegated association to the original
|
239
242
|
# association and store the result.
|
240
243
|
send(:"original_#{association_name}=", send(:"deferred_#{association_name}").objects)
|
241
244
|
|
242
245
|
# Store the new value of the association into our delegated association.
|
243
|
-
update_deferred_association(association_name, listeners, inverse_association_name)
|
246
|
+
update_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
244
247
|
end
|
245
248
|
|
246
249
|
define_method :"reload_with_deferred_#{association_name}" do |*args|
|
247
|
-
find_or_create_deferred_association(association_name, listeners, inverse_association_name)
|
250
|
+
find_or_create_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
248
251
|
|
249
252
|
send(:"reload_without_deferred_#{association_name}", *args).tap do
|
250
|
-
update_deferred_association(association_name, listeners, inverse_association_name)
|
253
|
+
update_deferred_association(association_name, listeners, inverse_association_name, dependent)
|
251
254
|
end
|
252
255
|
end
|
253
256
|
alias_method_chain :reload, :"deferred_#{association_name}"
|
@@ -257,11 +260,11 @@ module Deferring
|
|
257
260
|
end
|
258
261
|
|
259
262
|
def generate_update_deferred_assocation_method
|
260
|
-
define_method :update_deferred_association do |name, listeners, inverse_association_name|
|
263
|
+
define_method :update_deferred_association do |name, listeners, inverse_association_name, dependent|
|
261
264
|
klass = self.class.reflect_on_association(:"#{name}").klass
|
262
265
|
send(
|
263
266
|
:"deferred_#{name}=",
|
264
|
-
DeferredAssociation.new(send(:"original_#{name}"), klass, self, inverse_association_name))
|
267
|
+
DeferredAssociation.new(send(:"original_#{name}"), klass, self, inverse_association_name, dependent))
|
265
268
|
listeners.each do |event_name, callback_method|
|
266
269
|
l = DeferredCallbackListener.new(event_name, self, callback_method)
|
267
270
|
send(:"deferred_#{name}").add_callback_listener(l)
|
@@ -270,9 +273,9 @@ module Deferring
|
|
270
273
|
end
|
271
274
|
|
272
275
|
def generate_find_or_create_deferred_association_method
|
273
|
-
define_method :find_or_create_deferred_association do |name, listeners, inverse_association_name|
|
276
|
+
define_method :find_or_create_deferred_association do |name, listeners, inverse_association_name, dependent|
|
274
277
|
if send(:"deferred_#{name}").nil?
|
275
|
-
update_deferred_association(name, listeners, inverse_association_name)
|
278
|
+
update_deferred_association(name, listeners, inverse_association_name, dependent)
|
276
279
|
end
|
277
280
|
end
|
278
281
|
end
|
@@ -7,14 +7,19 @@ 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,
|
11
|
+
:klass,
|
12
|
+
:parent_record,
|
13
|
+
:inverse_name,
|
14
|
+
:dependent
|
11
15
|
|
12
|
-
def initialize(original_association, klass, parent_record, inverse_name)
|
16
|
+
def initialize(original_association, klass, parent_record, inverse_name, dependent)
|
13
17
|
super(original_association)
|
14
18
|
@load_state = :ghost
|
15
19
|
@klass = klass
|
16
20
|
@parent_record = parent_record
|
17
21
|
@inverse_name = inverse_name
|
22
|
+
@dependent = dependent
|
18
23
|
end
|
19
24
|
alias_method :original_association, :__getobj__
|
20
25
|
|
@@ -101,10 +106,10 @@ module Deferring
|
|
101
106
|
objects.map(&:id)
|
102
107
|
end
|
103
108
|
|
104
|
-
def <<(records)
|
109
|
+
def <<(*records)
|
105
110
|
# TODO: Do we want to prevent including the same object twice? Not sure,
|
106
111
|
# but it will probably be filtered after saving and retrieving as well.
|
107
|
-
|
112
|
+
records.flatten.uniq.each do |record|
|
108
113
|
run_deferring_callbacks(:link, record) do
|
109
114
|
if inverse_name && record.class.reflect_on_association(inverse_name)
|
110
115
|
record.send(:"#{inverse_name}=", parent_record)
|
@@ -119,13 +124,25 @@ module Deferring
|
|
119
124
|
alias_method :concat, :<<
|
120
125
|
alias_method :append, :<<
|
121
126
|
|
122
|
-
def delete(records)
|
123
|
-
|
127
|
+
def delete(*records)
|
128
|
+
records.flatten.uniq.each do |record|
|
124
129
|
run_deferring_callbacks(:unlink, record) { objects.delete(record) }
|
125
130
|
end
|
126
131
|
self
|
127
132
|
end
|
128
133
|
|
134
|
+
def destroy(*records)
|
135
|
+
records.flatten.uniq.each do |record|
|
136
|
+
record = record.to_i if record.is_a? String
|
137
|
+
record = objects.detect { |o| o.id == record } if record.is_a? Fixnum
|
138
|
+
|
139
|
+
run_deferring_callbacks(:unlink, record) {
|
140
|
+
objects.delete(record)
|
141
|
+
record.mark_for_destruction if dependent && [:destroy, :delete_all].include?(dependent)
|
142
|
+
}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
129
146
|
def build(*args, &block)
|
130
147
|
klass.new(*args, &block).tap do |record|
|
131
148
|
run_deferring_callbacks(:link, record) do
|
data/lib/deferring/version.rb
CHANGED
@@ -447,14 +447,14 @@ RSpec.describe 'deferred has_and_belongs_to_many associations' do
|
|
447
447
|
])
|
448
448
|
end
|
449
449
|
|
450
|
-
it '
|
450
|
+
it 'calls the unlink callbacks when removing a record using destroy' do
|
451
451
|
bob = Person.where(name: 'Bob').first
|
452
|
-
bob.teams.destroy(3)
|
452
|
+
bob.teams.destroy(Team.find(3))
|
453
453
|
|
454
454
|
expect(bob.audit_log.length).to eq(2)
|
455
455
|
expect(bob.audit_log).to eq([
|
456
|
-
'Before
|
457
|
-
'After
|
456
|
+
'Before unlinking team 3',
|
457
|
+
'After unlinking team 3'
|
458
458
|
])
|
459
459
|
end
|
460
460
|
|
@@ -467,14 +467,19 @@ RSpec.describe 'deferred has_and_belongs_to_many associations' do
|
|
467
467
|
bob.teams.delete(Team.find(1))
|
468
468
|
bob.teams << Team.find(2)
|
469
469
|
bob.teams.build(name: 'Service Desk')
|
470
|
+
bob.teams.destroy(Team.find(3))
|
470
471
|
bob.save!
|
471
472
|
|
472
|
-
expect(bob.audit_log.length).to eq(
|
473
|
+
expect(bob.audit_log.length).to eq(16)
|
473
474
|
expect(bob.audit_log).to eq([
|
474
475
|
'Before unlinking team 1', 'After unlinking team 1',
|
475
476
|
'Before linking team 2', 'After linking team 2',
|
476
477
|
'Before linking new team', 'After linking new team',
|
477
|
-
'Before
|
478
|
+
'Before unlinking team 3', 'After unlinking team 3',
|
479
|
+
'Before removing team 3',
|
480
|
+
'Before removing team 1',
|
481
|
+
'After removing team 3',
|
482
|
+
'After removing team 1',
|
478
483
|
'Before adding team 2', 'After adding team 2',
|
479
484
|
'Before adding new team', 'After adding team 4'
|
480
485
|
])
|
@@ -607,6 +612,96 @@ RSpec.describe 'deferred has_and_belongs_to_many associations' do
|
|
607
612
|
|
608
613
|
end
|
609
614
|
|
615
|
+
describe '#destroy' do
|
616
|
+
context 'when called on has_many association with dependent: :delete_all' do
|
617
|
+
it 'destroys the records supplied and removes them from the collection' do
|
618
|
+
printer = Issue.create!(subject: 'Printer PRT-001 jammed')
|
619
|
+
database = Issue.create!(subject: 'Database server DB-1337 down')
|
620
|
+
sandwich = Issue.create!(subject: 'Make me a sandwich!')
|
621
|
+
|
622
|
+
bob.issues << printer << database << sandwich
|
623
|
+
bob.save!
|
624
|
+
|
625
|
+
expect {
|
626
|
+
bob.issues.destroy(printer)
|
627
|
+
bob.save!
|
628
|
+
}.to change {
|
629
|
+
Person.find(bob.id).issues.size
|
630
|
+
}.from(3).to(2)
|
631
|
+
expect { Issue.find(printer.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
context 'when called on has_many association without dependent: :delete_all' do
|
636
|
+
it 'removes the records supplied from the collection' do
|
637
|
+
printer = Issue.create!(subject: 'Printer PRT-001 jammed')
|
638
|
+
database = Issue.create!(subject: 'Database server DB-1337 down')
|
639
|
+
sandwich = Issue.create!(subject: 'Make me a sandwich!')
|
640
|
+
|
641
|
+
bob.other_issues << printer << database << sandwich
|
642
|
+
bob.save!
|
643
|
+
|
644
|
+
expect {
|
645
|
+
bob.other_issues.destroy(printer)
|
646
|
+
bob.save!
|
647
|
+
}.to change {
|
648
|
+
Person.find(bob.id).other_issues.size
|
649
|
+
}.from(3).to(2)
|
650
|
+
expect(Issue.find(printer.id)).to eq(printer)
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
context 'when called on has_and_belongs_to_many association' do
|
655
|
+
it 'removes the records supplied from the collection' do
|
656
|
+
bob.teams << dba << operations
|
657
|
+
bob.save!
|
658
|
+
|
659
|
+
expect {
|
660
|
+
bob.teams.destroy(dba)
|
661
|
+
bob.save!
|
662
|
+
}.to change {
|
663
|
+
Person.find(bob.id).teams.size
|
664
|
+
}.from(2).to(1)
|
665
|
+
expect(Team.find(dba.id)).to eq(dba)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
it 'returns an array with the removed records' do
|
670
|
+
printer = Issue.create!(subject: 'Printer PRT-001 jammed')
|
671
|
+
database = Issue.create!(subject: 'Database server DB-1337 down')
|
672
|
+
sandwich = Issue.create!(subject: 'Make me a sandwich!')
|
673
|
+
|
674
|
+
bob.issues << printer << database << sandwich
|
675
|
+
bob.save!
|
676
|
+
|
677
|
+
expect(bob.issues.destroy(database)).to eq([database])
|
678
|
+
end
|
679
|
+
|
680
|
+
it 'accepts Fixnum values' do
|
681
|
+
bob.teams << dba << operations
|
682
|
+
bob.save!
|
683
|
+
|
684
|
+
expect {
|
685
|
+
bob.teams.destroy(dba.id)
|
686
|
+
bob.save!
|
687
|
+
}.to change {
|
688
|
+
Person.find(bob.id).teams.size
|
689
|
+
}.from(2).to(1)
|
690
|
+
end
|
691
|
+
|
692
|
+
it 'accepts String values' do
|
693
|
+
bob.teams << dba << operations
|
694
|
+
bob.save!
|
695
|
+
|
696
|
+
expect {
|
697
|
+
bob.teams.destroy("#{dba.id}", "#{operations.id}")
|
698
|
+
bob.save!
|
699
|
+
}.to change {
|
700
|
+
Person.find(bob.id).teams.size
|
701
|
+
}.from(2).to(0)
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
610
705
|
it 'should allow ActiveRecord::QueryMethods' do
|
611
706
|
p = Person.first
|
612
707
|
p.teams << dba << operations
|
@@ -12,9 +12,16 @@ class Person < ActiveRecord::Base
|
|
12
12
|
deferred_accepts_nested_attributes_for :teams, allow_destroy: true
|
13
13
|
|
14
14
|
deferred_has_many :issues, before_remove: :remove_issue,
|
15
|
-
after_remove: :removed_issue
|
15
|
+
after_remove: :removed_issue,
|
16
|
+
dependent: :delete_all
|
16
17
|
deferred_accepts_nested_attributes_for :issues, allow_destroy: true
|
17
18
|
|
19
|
+
# has_many without dependent: :delete_all, calling destroy on this association
|
20
|
+
# will not destroy the the Issue record
|
21
|
+
deferred_has_many :other_issues, before_remove: :remove_issue,
|
22
|
+
after_remove: :removed_issue,
|
23
|
+
class_name: 'Issue'
|
24
|
+
|
18
25
|
deferred_has_many :non_validated_issues, before_remove: :remove_issue,
|
19
26
|
after_remove: :removed_issue,
|
20
27
|
validate: false
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deferring
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin Roestenburg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|