public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Add special AssociationReflection methods for creating association objects, and 
modify the code base to use those methods instead of creating association 
objects directly. This allows plugins to hook into association object creation 
behavior.

[#986 state:resolved]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
Hongli Lai (Phusion) (author)
Sat Sep 06 10:43:14 -0700 2008
jeremy (committer)
Tue Sep 09 13:13:12 -0700 2008
commit  1398db0128be7ae01700712eafc95be5de430f7c
tree    cecf4f2ef0fd7c2747826fd22c56ba8f596ed1c4
parent  16929404417205792165153958ce5effa0b54645
...
1
2
3
 
4
5
6
...
1
2
 
3
4
5
6
0
@@ -1,6 +1,6 @@
0
 *Edge*
0
 
0
-* Internal API: configurable association options so plugins may extend and override.  #985 [Hongli Lai]
0
+* Internal API: configurable association options and build_association method for reflections so plugins may extend and override.  #985 [Hongli Lai]
0
 
0
 * Changed benchmarks to be reported in milliseconds [DHH]
0
 
...
1266
1267
1268
1269
 
1270
1271
1272
...
1266
1267
1268
 
1269
1270
1271
1272
0
@@ -1266,7 +1266,7 @@ module ActiveRecord
0
               association = association_proxy_class.new(self, reflection)
0
             end
0
 
0
-            new_value = reflection.klass.new(new_value) if reflection.options[:accessible] && new_value.is_a?(Hash)
0
+            new_value = reflection.build_association(new_value) if reflection.options[:accessible] && new_value.is_a?(Hash)
0
 
0
             if association_proxy_class == HasOneThroughAssociation
0
               association.create_through_record(new_value)
...
110
111
112
113
 
114
115
116
...
287
288
289
290
 
291
292
293
...
377
378
379
380
 
 
 
381
382
383
...
387
388
389
390
 
391
392
393
...
110
111
112
 
113
114
115
116
...
287
288
289
 
290
291
292
293
...
377
378
379
 
380
381
382
383
384
385
...
389
390
391
 
392
393
394
395
0
@@ -110,7 +110,7 @@ module ActiveRecord
0
 
0
         @owner.transaction do
0
           flatten_deeper(records).each do |record|
0
-            record = @reflection.klass.new(record) if @reflection.options[:accessible] && record.is_a?(Hash)
0
+            record = @reflection.build_association(record) if @reflection.options[:accessible] && record.is_a?(Hash)
0
 
0
             raise_on_type_mismatch(record)
0
             add_record_to_target_with_callbacks(record) do |r|
0
@@ -287,7 +287,7 @@ module ActiveRecord
0
       # This will perform a diff and delete/add only records that have changed.
0
       def replace(other_array)
0
         other_array.map! do |val|
0
-          val.is_a?(Hash) ? @reflection.klass.new(val) : val
0
+          val.is_a?(Hash) ? @reflection.build_association(val) : val
0
         end if @reflection.options[:accessible]
0
 
0
         other_array.each { |val| raise_on_type_mismatch(val) }
0
@@ -377,7 +377,9 @@ module ActiveRecord
0
         def create_record(attrs)
0
           attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
0
           ensure_owner_is_not_new
0
-          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.new(attrs) }
0
+          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
0
+            @reflection.build_association(attrs)
0
+          end
0
           if block_given?
0
             add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
0
           else
0
@@ -387,7 +389,7 @@ module ActiveRecord
0
 
0
         def build_record(attrs)
0
           attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
0
-          record = @reflection.klass.new(attrs)
0
+          record = @reflection.build_association(attrs)
0
           if block_given?
0
             add_record_to_target_with_callbacks(record) { |*block_args| yield(*block_args) }
0
           else
...
2
3
4
5
 
6
7
8
9
 
10
11
12
...
2
3
4
 
5
6
7
8
 
9
10
11
12
0
@@ -2,11 +2,11 @@ module ActiveRecord
0
   module Associations
0
     class BelongsToAssociation < AssociationProxy #:nodoc:
0
       def create(attributes = {})
0
-        replace(@reflection.klass.create(attributes))
0
+        replace(@reflection.create_association(attributes))
0
       end
0
 
0
       def build(attributes = {})
0
-        replace(@reflection.klass.new(attributes))
0
+        replace(@reflection.build_association(attributes))
0
       end
0
 
0
       def replace(record)
...
10
11
12
13
 
14
15
16
17
18
19
20
 
21
22
23
...
47
48
49
50
51
 
 
 
52
53
54
...
10
11
12
 
13
14
15
16
17
18
19
 
20
21
22
23
...
47
48
49
 
 
50
51
52
53
54
55
0
@@ -10,14 +10,14 @@ module ActiveRecord
0
 
0
       def create!(attrs = nil)
0
         @reflection.klass.transaction do
0
-          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create! } : @reflection.klass.create!)
0
+          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association! } : @reflection.create_association!)
0
           object
0
         end
0
       end
0
 
0
       def create(attrs = nil)
0
         @reflection.klass.transaction do
0
-          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.klass.create } : @reflection.klass.create)
0
+          self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association)
0
           object
0
         end
0
       end
0
@@ -47,8 +47,9 @@ module ActiveRecord
0
               return false unless record.save
0
             end
0
           end
0
-          klass = @reflection.through_reflection.klass
0
-          @owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { klass.create! }
0
+          through_reflection = @reflection.through_reflection
0
+          klass = through_reflection.klass
0
+          @owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { through_reflection.create_association! }
0
         end
0
 
0
         # TODO - add dependent option support
...
7
8
9
10
 
 
 
11
12
13
14
 
 
 
15
16
17
18
 
 
 
19
20
21
...
91
92
93
94
 
 
 
95
96
97
...
7
8
9
 
10
11
12
13
14
15
 
16
17
18
19
20
21
 
22
23
24
25
26
27
...
97
98
99
 
100
101
102
103
104
105
0
@@ -7,15 +7,21 @@ module ActiveRecord
0
       end
0
 
0
       def create(attrs = {}, replace_existing = true)
0
-        new_record(replace_existing) { |klass| klass.create(attrs) }
0
+        new_record(replace_existing) do |reflection|
0
+          reflection.create_association(attrs)
0
+        end
0
       end
0
 
0
       def create!(attrs = {}, replace_existing = true)
0
-        new_record(replace_existing) { |klass| klass.create!(attrs) }
0
+        new_record(replace_existing) do |reflection|
0
+          reflection.create_association!(attrs)
0
+        end
0
       end
0
 
0
       def build(attrs = {}, replace_existing = true)
0
-        new_record(replace_existing) { |klass| klass.new(attrs) }
0
+        new_record(replace_existing) do |reflection|
0
+          reflection.build_association(attrs)
0
+        end
0
       end
0
 
0
       def replace(obj, dont_save = false)
0
@@ -91,7 +97,9 @@ module ActiveRecord
0
           # instance. Otherwise, if the target has not previously been loaded
0
           # elsewhere, the instance we create will get orphaned.
0
           load_target if replace_existing
0
-          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { yield @reflection.klass }
0
+          record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
0
+            yield @reflection
0
+          end
0
 
0
           if replace_existing
0
             replace(record, true) 
...
129
130
131
 
 
 
 
 
 
 
 
 
 
 
 
132
133
134
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
137
138
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
0
@@ -129,10 +129,45 @@ module ActiveRecord
0
 
0
     # Holds all the meta-data about an association as it was specified in the Active Record class.
0
     class AssociationReflection < MacroReflection #:nodoc:
0
+      # Returns the target association's class:
0
+      #
0
+      #   class Author < ActiveRecord::Base
0
+      #     has_many :books
0
+      #   end
0
+      #
0
+      #   Author.reflect_on_association(:books).klass
0
+      #   # => Book
0
+      #
0
+      # <b>Note:</b> do not call +klass.new+ or +klass.create+ to instantiate
0
+      # a new association object. Use +build_association+ or +create_association+
0
+      # instead. This allows plugins to hook into association object creation.
0
       def klass
0
         @klass ||= active_record.send(:compute_type, class_name)
0
       end
0
 
0
+      # Returns a new, unsaved instance of the associated class. +options+ will
0
+      # be passed to the class's constructor.
0
+      def build_association(*options)
0
+        klass.new(*options)
0
+      end
0
+
0
+      # Creates a new instance of the associated class, and immediates saves it
0
+      # with ActiveRecord::Base#save. +options+ will be passed to the class's
0
+      # creation method. Returns the newly created object.
0
+      def create_association(*options)
0
+        klass.create(*options)
0
+      end
0
+
0
+      # Creates a new instance of the associated class, and immediates saves it
0
+      # with ActiveRecord::Base#save!. +options+ will be passed to the class's
0
+      # creation method. If the created record doesn't pass validations, then an
0
+      # exception will be raised.
0
+      #
0
+      # Returns the newly created object.
0
+      def create_association!(*options)
0
+        klass.create!(*options)
0
+      end
0
+
0
       def table_name
0
         @table_name ||= klass.table_name
0
       end

Comments