deferred_associations 0.5.7 → 0.5.8

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: 80ea93f4951198652fc52f31762e2bbfba01b748
4
- data.tar.gz: 5ac0e402760f95e2cdca7eda2b2f7be32c20e295
3
+ metadata.gz: 868bf45a163b6381d3c286bf65251e0b3e6235f6
4
+ data.tar.gz: 1e12fb0d5b42f22ce07a12f01fe3fab812508779
5
5
  SHA512:
6
- metadata.gz: ffb66400841e0f0b3ff3c94578c819d6f3048b9992399af672dba43aa46ee9aa80f645dfb543702f471c0de24cbeb677eb5b93e714e9e1f9e641270d276bc4b5
7
- data.tar.gz: be23972de665dfbcbe5be755a439ad98816caaf787db53c794311e03da12d7966f198d1b7ca0e8c1f7de5ce3446cccf677b835b9aee42505c10b42eb8dda92f1
6
+ metadata.gz: 99860be2db82c21a509355252729c0f4b716d1b99e46d2c2203feba4053dc1cc93a758a46d0725fd639c72aaddbe76104f3825e275a5e815bf2e98bdfb46149f
7
+ data.tar.gz: 216aebba563c6374607036215dec3ee3bc55f1a7d9ccf3863f9cc9aa4c8327571696998567594d05b219df6455ed9888d186e94e334f7b4e18f0a9284fa6926e
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 0.5.8
2
+ =====
3
+ * fix preloading of HABTM associations in ActiveRecord >= 4.0
4
+
1
5
  0.5.7
2
6
  =====
3
7
  * fix a problem, when model is loaded multiple times
data/Readme.markdown CHANGED
@@ -39,7 +39,7 @@ Usage
39
39
  Compatibility
40
40
  =============
41
41
 
42
- Tested with Rails 2.3.14, 3.2.3, 3.2.14, 4.0 and 4.1 on Ruby 1.8.7, 1.9.3 and JRuby 1.7.12
42
+ Tested with Rails 2.3.18, 3.2.22, 4.1.14, 4.2.5 on Ruby 1.9.3, 2.2.4, 2.3.0 and JRuby 1.7, JRuby 9.0.5.0
43
43
 
44
44
  Note, that Rails 3.2.14 associations are partly broken under JRuby cause of https://github.com/rails/rails/issues/11595
45
45
  You'll need to upgrade activerecord-jdbc-adapter to >= 1.3.0.beta1, if you want to use this combination.
@@ -74,7 +74,7 @@ History
74
74
  Most of the code for the habtm association was written by TylerRick for his gem [has_and_belongs_to_many_with_deferred_save](https://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save)
75
75
  Mainly, I changed two things:
76
76
 
77
- * added ActiveRecord 3 compatibility
77
+ * added compatibility for ActiveRecord 3 and 4
78
78
  * removed singleton methods, because they interfere with caching
79
79
 
80
80
  License
@@ -82,4 +82,4 @@ License
82
82
 
83
83
  This plugin is licensed under the BSD license.
84
84
 
85
- 2013 (c) Martin Körner
85
+ 2016 (c) Martin Körner
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.5
1
+ 0.5.8
@@ -1,61 +1,58 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
5
2
 
6
3
  Gem::Specification.new do |s|
7
- s.name = "deferred_associations"
8
- s.version = "0.5.7"
4
+ s.name = 'deferred_associations'
5
+ s.version = '0.5.8'
9
6
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Martin Koerner", "Tyler Rick", "Alessio Caiazza"]
12
- s.date = "2013-10-25"
7
+ s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
8
+ s.authors = ['Martin Koerner', 'Tyler Rick', 'Alessio Caiazza']
9
+ s.date = '2016-03-08'
13
10
  s.description = "Makes ActiveRecord defer/postpone saving the records you add to an habtm (has_and_belongs_to_many) or has_many\n association until you call model.save, allowing validation in the style of normal attributes. Additionally you\n can check inside before_save filters, if the association was altered."
14
- s.email = "martin.koerner@objectfab.de"
11
+ s.email = 'martin.koerner@objectfab.de'
12
+ s.licenses = 'MIT'
15
13
  s.extra_rdoc_files = [
16
- "CHANGELOG",
17
- "Readme.markdown"
14
+ 'CHANGELOG',
15
+ 'Readme.markdown'
18
16
  ]
19
17
  s.files = [
20
- "CHANGELOG",
21
- "Rakefile",
22
- "Readme.markdown",
23
- "VERSION",
24
- "deferred_associations.gemspec",
25
- "init.rb",
26
- "lib/array_to_association_wrapper.rb",
27
- "lib/deferred_associations.rb",
28
- "lib/has_and_belongs_to_many_with_deferred_save.rb",
29
- "lib/has_many_with_deferred_save.rb",
30
- "spec/db/database.yml",
31
- "spec/db/schema.rb",
32
- "spec/has_and_belongs_to_many_with_deferred_save_spec.rb",
33
- "spec/has_many_with_deferred_save_spec.rb",
34
- "spec/models/chair.rb",
35
- "spec/models/door.rb",
36
- "spec/models/person.rb",
37
- "spec/models/room.rb",
38
- "spec/models/table.rb",
39
- "spec/spec_helper.rb"
18
+ 'CHANGELOG',
19
+ 'Readme.markdown',
20
+ 'VERSION',
21
+ 'deferred_associations.gemspec',
22
+ 'init.rb',
23
+ 'lib/array_to_association_wrapper.rb',
24
+ 'lib/deferred_associations.rb',
25
+ 'lib/has_and_belongs_to_many_with_deferred_save.rb',
26
+ 'lib/has_many_with_deferred_save.rb',
27
+ 'spec/db/database.yml',
28
+ 'spec/db/schema.rb',
29
+ 'spec/habtm_ar4_spec.rb',
30
+ 'spec/has_and_belongs_to_many_with_deferred_save_spec.rb',
31
+ 'spec/has_many_with_deferred_save_spec.rb',
32
+ 'spec/models/chair.rb',
33
+ 'spec/models/door.rb',
34
+ 'spec/models/person.rb',
35
+ 'spec/models/room.rb',
36
+ 'spec/models/table.rb',
37
+ 'spec/spec_helper.rb'
40
38
  ]
41
- s.homepage = "http://github.com/MartinKoerner/deferred_associations"
42
- s.require_paths = ["lib"]
43
- s.rubygems_version = "1.8.24"
44
- s.summary = "Makes ActiveRecord defer/postpone habtm or has_many associations"
39
+ s.homepage = 'http://github.com/MartinKoerner/deferred_associations'
40
+ s.require_paths = ['lib']
41
+ s.rubygems_version = '1.8.24'
42
+ s.summary = 'Makes ActiveRecord defer/postpone habtm or has_many associations'
45
43
 
46
- if s.respond_to? :specification_version then
44
+ if s.respond_to? :specification_version
47
45
  s.specification_version = 3
48
46
 
49
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
- s.add_runtime_dependency(%q<activerecord>, [">= 0"])
51
- s.add_development_dependency(%q<rspec>, [">= 0"])
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0')
48
+ s.add_runtime_dependency('activerecord', ['>= 0'])
49
+ s.add_development_dependency('rspec', ['>= 0'])
52
50
  else
53
- s.add_dependency(%q<activerecord>, [">= 0"])
54
- s.add_dependency(%q<rspec>, [">= 0"])
51
+ s.add_dependency('activerecord', ['>= 0'])
52
+ s.add_dependency('rspec', ['>= 0'])
55
53
  end
56
54
  else
57
- s.add_dependency(%q<activerecord>, [">= 0"])
58
- s.add_dependency(%q<rspec>, [">= 0"])
55
+ s.add_dependency('activerecord', ['>= 0'])
56
+ s.add_dependency('rspec', ['>= 0'])
59
57
  end
60
58
  end
61
-
@@ -1,67 +1,68 @@
1
- class ArrayToAssociationWrapper < Array
2
-
3
- def defer_association_methods_to owner, association_name
4
- @association_owner = owner
5
- @association_name = association_name
6
- end
7
-
8
- # trick collection_name.include?(obj)
9
- # If you use a collection of SingleTableInheritance and didn't :select 'type' the
10
- # include? method will not find any subclassed object.
11
- def include_with_deferred_save?(obj)
12
- if @association_owner.present?
13
- if self.detect { |itm| itm == obj || (itm[:id] == obj[:id] && obj.is_a?(itm.class)) }
14
- return true
15
- else
16
- return false
17
- end
18
- else
19
- include_without_deferred_save?(obj)
20
- end
21
- end
22
-
23
- alias_method_chain :include?, 'deferred_save'
24
-
25
- def find_with_deferred_save *args
26
- if @association_owner.present?
27
- collection_without_deferred_save.send(:find, *args)
28
- else
29
- find_without_deferred_save
30
- end
31
- end
32
-
33
- alias_method_chain :find, :deferred_save
34
-
35
- def first_with_deferred_save *args
36
- if @association_owner.present?
37
- collection_without_deferred_save.send(:first, *args)
38
- else
39
- first_without_deferred_save
40
- end
41
- end
42
-
43
- alias_method_chain :first, :deferred_save
44
-
45
- def last_with_deferred_save *args
46
- if @association_owner.present?
47
- collection_without_deferred_save.send(:last, *args)
48
- else
49
- last_without_deferred_save
50
- end
51
- end
52
-
53
- alias_method_chain :last, :deferred_save
54
-
55
- define_method :method_missing do |method, *args|
56
- #puts "#{self.class}.method_missing(#{method}) (#{collection_without_deferred_save.inspect})"
57
- if @association_owner.present?
58
- collection_without_deferred_save.send(method, *args) unless method == :set_inverse_instance
59
- else
60
- super
61
- end
62
- end
63
-
64
- def collection_without_deferred_save
65
- @association_owner.send("#{@association_name}_without_deferred_save")
66
- end
67
- end
1
+ class ArrayToAssociationWrapper < Array
2
+
3
+ def defer_association_methods_to(owner, association_name)
4
+ @association_owner = owner
5
+ @association_name = association_name
6
+ end
7
+
8
+ # trick collection_name.include?(obj)
9
+ # If you use a collection of SingleTableInheritance and didn't :select 'type' the
10
+ # include? method will not find any subclassed object.
11
+ def include_with_deferred_save?(obj)
12
+ if @association_owner.present?
13
+ if detect { |itm| itm == obj || (itm[:id] == obj[:id] && obj.is_a?(itm.class)) }
14
+ return true
15
+ else
16
+ return false
17
+ end
18
+ else
19
+ include_without_deferred_save?(obj)
20
+ end
21
+ end
22
+
23
+ alias_method_chain :include?, 'deferred_save'
24
+
25
+ def find_with_deferred_save(*args)
26
+ if @association_owner.present?
27
+ collection_without_deferred_save.send(:find, *args)
28
+ else
29
+ find_without_deferred_save
30
+ end
31
+ end
32
+
33
+ alias_method_chain :find, :deferred_save
34
+
35
+ def first_with_deferred_save(*args)
36
+ if @association_owner.present?
37
+ collection_without_deferred_save.send(:first, *args)
38
+ else
39
+ first_without_deferred_save
40
+ end
41
+ end
42
+
43
+ alias_method_chain :first, :deferred_save
44
+
45
+ def last_with_deferred_save(*args)
46
+ if @association_owner.present?
47
+ collection_without_deferred_save.send(:last, *args)
48
+ else
49
+ last_without_deferred_save
50
+ end
51
+ end
52
+
53
+ alias_method_chain :last, :deferred_save
54
+
55
+ define_method :method_missing do |method, *args|
56
+ # puts "#{self.class}.method_missing(#{method}) (#{collection_without_deferred_save.inspect})"
57
+ if @association_owner.present?
58
+ collection_without_deferred_save.send(method, *args) unless method == :set_inverse_instance
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ def collection_without_deferred_save
65
+ @association_owner.send("#{@association_name}_without_deferred_save")
66
+ end
67
+
68
+ end
@@ -1,7 +1,6 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  module ClassMethods
4
-
5
4
  # Instructions:
6
5
  #
7
6
  # Replace your existing call to has_and_belongs_to_many with has_and_belongs_to_many_with_deferred_save.
@@ -17,7 +16,7 @@ module ActiveRecord
17
16
  # end
18
17
  def has_and_belongs_to_many_with_deferred_save(*args)
19
18
  collection_name = args[0].to_s
20
- collection_singular_ids = collection_name.singularize + "_ids"
19
+ collection_singular_ids = collection_name.singularize + '_ids'
21
20
 
22
21
  return if method_defined?("#{collection_name}_with_deferred_save")
23
22
 
@@ -29,44 +28,40 @@ module ActiveRecord
29
28
  attr_accessor :"use_original_collection_reader_behavior_for_#{collection_name}"
30
29
 
31
30
  define_method "#{collection_name}_with_deferred_save=" do |collection|
32
- #puts "has_and_belongs_to_many_with_deferred_save: #{collection_name} = #{collection.collect(&:id).join(',')}"
33
- self.send "unsaved_#{collection_name}=", collection
31
+ # puts "has_and_belongs_to_many_with_deferred_save: #{collection_name} = #{collection.collect(&:id).join(',')}"
32
+ send "unsaved_#{collection_name}=", collection
34
33
  end
35
34
 
36
- define_method "#{collection_name}_with_deferred_save" do |*args|
37
- if self.send("use_original_collection_reader_behavior_for_#{collection_name}")
38
- self.send("#{collection_name}_without_deferred_save")
35
+ define_method "#{collection_name}_with_deferred_save" do |*method_args|
36
+ if send("use_original_collection_reader_behavior_for_#{collection_name}")
37
+ send("#{collection_name}_without_deferred_save")
39
38
  else
40
- if self.send("unsaved_#{collection_name}").nil?
41
- send("initialize_unsaved_#{collection_name}", *args)
42
- end
43
- self.send("unsaved_#{collection_name}")
39
+ send("initialize_unsaved_#{collection_name}", *method_args) if send("unsaved_#{collection_name}").nil?
40
+ send("unsaved_#{collection_name}")
44
41
  end
45
42
  end
46
43
 
47
44
  alias_method_chain :"#{collection_name}=", 'deferred_save'
48
45
  alias_method_chain :"#{collection_name}", 'deferred_save'
49
46
 
50
- define_method "#{collection_singular_ids}_with_deferred_save" do |*args|
51
- if self.send("use_original_collection_reader_behavior_for_#{collection_name}")
52
- self.send("#{collection_singular_ids}_without_deferred_save")
47
+ define_method "#{collection_singular_ids}_with_deferred_save" do |*method_args|
48
+ if send("use_original_collection_reader_behavior_for_#{collection_name}")
49
+ send("#{collection_singular_ids}_without_deferred_save")
53
50
  else
54
- if self.send("unsaved_#{collection_name}").nil?
55
- send("initialize_unsaved_#{collection_name}", *args)
56
- end
57
- self.send("unsaved_#{collection_name}").map { |e| e[:id] }
51
+ send("initialize_unsaved_#{collection_name}", *method_args) if send("unsaved_#{collection_name}").nil?
52
+ send("unsaved_#{collection_name}").map { |e| e[:id] }
58
53
  end
59
54
  end
60
55
 
61
56
  alias_method_chain :"#{collection_singular_ids}", 'deferred_save'
62
57
 
63
58
  # only needed for ActiveRecord >= 3.0
64
- if ActiveRecord::VERSION::STRING >= "3"
59
+ if ActiveRecord::VERSION::STRING >= '3'
65
60
  define_method "#{collection_singular_ids}_with_deferred_save=" do |ids|
66
- ids = Array.wrap(ids).reject { |id| id.blank? }
67
- reflection_wrapper = self.send("#{collection_name}_without_deferred_save")
61
+ ids = Array.wrap(ids).reject(&:blank?)
62
+ reflection_wrapper = send("#{collection_name}_without_deferred_save")
68
63
  new_values = reflection_wrapper.klass.find(ids)
69
- self.send("#{collection_name}=", new_values)
64
+ send("#{collection_name}=", new_values)
70
65
  end
71
66
  alias_method_chain :"#{collection_singular_ids}=", 'deferred_save'
72
67
  end
@@ -82,62 +77,48 @@ module ActiveRecord
82
77
  # But we only want the old behavior in this case -- most of the time we want the *new* behavior -- so we use
83
78
  # @use_original_collection_reader_behavior as a switch.
84
79
 
85
- self.send "use_original_collection_reader_behavior_for_#{collection_name}=", true
86
- if self.send("unsaved_#{collection_name}").nil?
87
- send("initialize_unsaved_#{collection_name}")
88
- end
89
- self.send "#{collection_name}_without_deferred_save=", self.send("unsaved_#{collection_name}")
90
- # /\ This is where the actual save occurs.
91
- self.send "use_original_collection_reader_behavior_for_#{collection_name}=", false
80
+ send "use_original_collection_reader_behavior_for_#{collection_name}=", true
81
+ send("initialize_unsaved_#{collection_name}") if send("unsaved_#{collection_name}").nil?
82
+ send "#{collection_name}_without_deferred_save=", send("unsaved_#{collection_name}")
83
+ # /\ This is where the actual save occurs.
84
+ send "use_original_collection_reader_behavior_for_#{collection_name}=", false
92
85
 
93
86
  true
94
87
  end
95
88
  after_save "do_#{collection_name}_save!"
96
89
 
97
-
98
- define_method "reload_with_deferred_save_for_#{collection_name}" do |*args|
90
+ define_method "reload_with_deferred_save_for_#{collection_name}" do |*method_args|
99
91
  # Reload from the *database*, discarding any unsaved changes.
100
- self.send("reload_without_deferred_save_for_#{collection_name}", *args).tap do
101
- self.send "unsaved_#{collection_name}=", nil
102
- # /\ If we didn't do this, then when we called reload, it would still have the same (possibly invalid) value of
103
- # unsaved_collection that it had before the reload.
92
+ send("reload_without_deferred_save_for_#{collection_name}", *method_args).tap do
93
+ send "unsaved_#{collection_name}=", nil
94
+ # /\ If we didn't do this, then when we called reload, it would still have the same (possibly invalid) value of
95
+ # unsaved_collection that it had before the reload.
104
96
  end
105
97
  end
106
98
  alias_method_chain :reload, "deferred_save_for_#{collection_name}"
107
99
 
100
+ define_method "initialize_unsaved_#{collection_name}" do |*method_args|
101
+ elements = send("#{collection_name}_without_deferred_save", *method_args)
108
102
 
109
- define_method "initialize_unsaved_#{collection_name}" do |*args|
110
- #puts "Initialized to #{self.send("#{collection_name}_without_deferred_save").clone.inspect}"
111
- elements = self.send("#{collection_name}_without_deferred_save", *args).clone
103
+ # here the association will be duped, so changes to "unsaved_#{collection_name}" will not be saved immediately
112
104
  elements = ArrayToAssociationWrapper.new(elements)
113
105
  elements.defer_association_methods_to self, collection_name
114
- self.send "unsaved_#{collection_name}=", elements
115
- # /\ We initialize it to collection_without_deferred_save in case they just loaded the object from the
116
- # database, in which case we want unsaved_collection to start out with the "saved collection".
117
- # Actually, this doesn't clone the Association but the elements array instead (since the clone method is
118
- # proxied like any other methods)
119
- # Important: If we don't use clone, then it does an assignment by reference and any changes to unsaved_collection
120
- # will also change *collection_without_deferred_save*! (Not what we want! Would result in us saving things
121
- # immediately, which is exactly what we're trying to avoid.)
122
-
123
-
124
-
106
+ send "unsaved_#{collection_name}=", elements
125
107
  end
126
108
  private :"initialize_unsaved_#{collection_name}"
127
-
128
109
  end
129
110
 
130
111
  def add_deletion_callback
131
112
  # this will delete all the association into the join table after obj.destroy,
132
113
  # but is only useful/necessary, if the record is not paranoid?
133
- unless (self.respond_to?(:paranoid?) && self.paranoid?)
134
- after_destroy { |record|
114
+ unless respond_to?(:paranoid?) && paranoid?
115
+ after_destroy do |record|
135
116
  begin
136
117
  record.save
137
118
  rescue Exception => e
138
119
  logger.warn "Association cleanup after destroy failed with #{e}"
139
120
  end
140
- }
121
+ end
141
122
  end
142
123
  end
143
124
  end
@@ -1,113 +1,107 @@
1
- module ActiveRecord
2
- module Associations
3
- module ClassMethods
4
-
5
- def has_many_with_deferred_save *args
6
- collection_name = args[0].to_s
7
-
8
- return if method_defined?("#{collection_name}_with_deferred_save")
9
-
10
- has_many *args
11
-
12
- if args[1].is_a?(Hash) && args[1].keys.include?(:through)
13
- logger.warn "You are using the option :through on #{self.name}##{collection_name}. This was not tested very much with has_many_with_deferred_save. Please write many tests for your functionality!"
14
- end
15
-
16
- after_save "hmwds_update_#{collection_name}"
17
-
18
- define_obj_setter collection_name
19
- define_obj_getter collection_name
20
- define_id_setter collection_name
21
- define_id_getter collection_name
22
-
23
- define_update_method collection_name
24
- define_reload_method collection_name
25
- end
26
-
27
-
28
- def define_obj_setter collection_name
29
-
30
- define_method("#{collection_name}_with_deferred_save=") do |objs|
31
- instance_variable_set "@hmwds_temp_#{collection_name}", objs || []
32
- end
33
-
34
- method_name = "#{collection_name}="
35
- alias_method_chain method_name, :deferred_save
36
- end
37
-
38
- def define_obj_getter collection_name
39
-
40
- define_method("#{collection_name}_with_deferred_save") do
41
- save_in_progress = instance_variable_get "@hmwds_#{collection_name}_save_in_progress"
42
-
43
- # while updating the association, rails loads the association object - this needs to be the original one
44
- unless save_in_progress
45
- elements = instance_variable_get "@hmwds_temp_#{collection_name}"
46
- if elements.nil?
47
- elements = ArrayToAssociationWrapper.new(self.send("#{collection_name}_without_deferred_save"))
48
- elements.defer_association_methods_to self, collection_name
49
- instance_variable_set "@hmwds_temp_#{collection_name}", elements
50
- end
51
-
52
- result = elements
53
- else
54
- result = self.send("#{collection_name}_without_deferred_save")
55
- end
56
-
57
- result
58
- end
59
-
60
- alias_method_chain collection_name, :deferred_save
61
- end
62
-
63
- def define_id_setter collection_name
64
- # only needed for ActiveRecord >= 3.0
65
- if ActiveRecord::VERSION::STRING >= "3"
66
- collection_singular_ids = "#{collection_name.singularize}_ids"
67
- define_method "#{collection_singular_ids}_with_deferred_save=" do |ids|
68
- ids = Array.wrap(ids).reject { |id| id.blank? }
69
- new_values = self.send("#{collection_name}_without_deferred_save").klass.find(ids)
70
- self.send("#{collection_name}=", new_values)
71
- end
72
- alias_method_chain :"#{collection_singular_ids}=", 'deferred_save'
73
- end
74
- end
75
-
76
- def define_id_getter collection_name
77
- collection_singular_ids = "#{collection_name.singularize}_ids"
78
- define_method "#{collection_singular_ids}_with_deferred_save" do
79
- self.send(collection_name).map { |e| e[:id] }
80
- end
81
- alias_method_chain :"#{collection_singular_ids}", 'deferred_save'
82
- end
83
-
84
- def define_update_method collection_name
85
-
86
- define_method "hmwds_update_#{collection_name}" do
87
-
88
- unless frozen?
89
- elements = instance_variable_get "@hmwds_temp_#{collection_name}"
90
- unless elements.nil? # nothing has been done with the association
91
- # save is done automatically, if original behaviour is restored
92
- instance_variable_set "@hmwds_#{collection_name}_save_in_progress", true
93
- self.send("#{collection_name}_without_deferred_save=", elements)
94
- instance_variable_set "@hmwds_#{collection_name}_save_in_progress", false
95
-
96
- instance_variable_set "@hmwds_temp_#{collection_name}", nil
97
- end
98
- end
99
- end
100
- end
101
-
102
- def define_reload_method collection_name
103
- define_method "reload_with_deferred_save_for_#{collection_name}" do |*args|
104
- # Reload from the *database*, discarding any unsaved changes.
105
- self.send("reload_without_deferred_save_for_#{collection_name}", *args).tap do
106
- instance_variable_set "@hmwds_temp_#{collection_name}", nil
107
- end
108
- end
109
- alias_method_chain :reload, "deferred_save_for_#{collection_name}"
110
- end
111
- end
112
- end
113
- end
1
+ module ActiveRecord
2
+ module Associations
3
+ module ClassMethods
4
+ def has_many_with_deferred_save(*args)
5
+ collection_name = args[0].to_s
6
+
7
+ return if method_defined?("#{collection_name}_with_deferred_save")
8
+
9
+ has_many *args
10
+
11
+ if args[1].is_a?(Hash) && args[1].keys.include?(:through)
12
+ logger.warn "You are using the option :through on #{name}##{collection_name}. This was not tested very much with has_many_with_deferred_save. Please write many tests for your functionality!"
13
+ end
14
+
15
+ after_save "hmwds_update_#{collection_name}"
16
+
17
+ define_obj_setter collection_name
18
+ define_obj_getter collection_name
19
+ define_id_setter collection_name
20
+ define_id_getter collection_name
21
+
22
+ define_update_method collection_name
23
+ define_reload_method collection_name
24
+ end
25
+
26
+ def define_obj_setter(collection_name)
27
+ define_method("#{collection_name}_with_deferred_save=") do |objs|
28
+ instance_variable_set "@hmwds_temp_#{collection_name}", objs || []
29
+ end
30
+
31
+ method_name = "#{collection_name}="
32
+ alias_method_chain method_name, :deferred_save
33
+ end
34
+
35
+ def define_obj_getter(collection_name)
36
+ define_method("#{collection_name}_with_deferred_save") do
37
+ save_in_progress = instance_variable_get "@hmwds_#{collection_name}_save_in_progress"
38
+
39
+ # while updating the association, rails loads the association object - this needs to be the original one
40
+ unless save_in_progress
41
+ elements = instance_variable_get "@hmwds_temp_#{collection_name}"
42
+ if elements.nil?
43
+ elements = ArrayToAssociationWrapper.new(send("#{collection_name}_without_deferred_save"))
44
+ elements.defer_association_methods_to self, collection_name
45
+ instance_variable_set "@hmwds_temp_#{collection_name}", elements
46
+ end
47
+
48
+ result = elements
49
+ else
50
+ result = send("#{collection_name}_without_deferred_save")
51
+ end
52
+
53
+ result
54
+ end
55
+
56
+ alias_method_chain collection_name, :deferred_save
57
+ end
58
+
59
+ def define_id_setter(collection_name)
60
+ # only needed for ActiveRecord >= 3.0
61
+ if ActiveRecord::VERSION::STRING >= '3'
62
+ collection_singular_ids = "#{collection_name.singularize}_ids"
63
+ define_method "#{collection_singular_ids}_with_deferred_save=" do |ids|
64
+ ids = Array.wrap(ids).reject(&:blank?)
65
+ new_values = send("#{collection_name}_without_deferred_save").klass.find(ids)
66
+ send("#{collection_name}=", new_values)
67
+ end
68
+ alias_method_chain :"#{collection_singular_ids}=", 'deferred_save'
69
+ end
70
+ end
71
+
72
+ def define_id_getter(collection_name)
73
+ collection_singular_ids = "#{collection_name.singularize}_ids"
74
+ define_method "#{collection_singular_ids}_with_deferred_save" do
75
+ send(collection_name).map { |e| e[:id] }
76
+ end
77
+ alias_method_chain :"#{collection_singular_ids}", 'deferred_save'
78
+ end
79
+
80
+ def define_update_method(collection_name)
81
+ define_method "hmwds_update_#{collection_name}" do
82
+ unless frozen?
83
+ elements = instance_variable_get "@hmwds_temp_#{collection_name}"
84
+ unless elements.nil? # nothing has been done with the association
85
+ # save is done automatically, if original behaviour is restored
86
+ instance_variable_set "@hmwds_#{collection_name}_save_in_progress", true
87
+ send("#{collection_name}_without_deferred_save=", elements)
88
+ instance_variable_set "@hmwds_#{collection_name}_save_in_progress", false
89
+
90
+ instance_variable_set "@hmwds_temp_#{collection_name}", nil
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def define_reload_method(collection_name)
97
+ define_method "reload_with_deferred_save_for_#{collection_name}" do |*args|
98
+ # Reload from the *database*, discarding any unsaved changes.
99
+ send("reload_without_deferred_save_for_#{collection_name}", *args).tap do
100
+ instance_variable_set "@hmwds_temp_#{collection_name}", nil
101
+ end
102
+ end
103
+ alias_method_chain :reload, "deferred_save_for_#{collection_name}"
104
+ end
105
+ end
106
+ end
107
+ end