Skip to content
This repository
Browse code

Introduce ActiveRecord::Reflection::ThroughReflection to simplify hm:…

…t reflection logic
  • Loading branch information...
commit 25ca21ae21d49f06708357a5ce0670103ced2d58 1 parent 95e1cf4
Pratik authored
119  activerecord/lib/active_record/reflection.rb
@@ -13,14 +13,15 @@ module ClassMethods
13 13
       def create_reflection(macro, name, options, active_record)
14 14
         case macro
15 15
           when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
16  
-            reflection = AssociationReflection.new(macro, name, options, active_record)
  16
+            klass = options[:through] ? ThroughReflection : AssociationReflection
  17
+            reflection = klass.new(macro, name, options, active_record)
17 18
           when :composed_of
18 19
             reflection = AggregateReflection.new(macro, name, options, active_record)
19 20
         end
20 21
         write_inheritable_hash :reflections, name => reflection
21 22
         reflection
22 23
       end
23  
-      
  24
+
24 25
       # Returns a hash containing all AssociationReflection objects for the current class
25 26
       # Example:
26 27
       #
@@ -30,7 +31,7 @@ def create_reflection(macro, name, options, active_record)
30 31
       def reflections
31 32
         read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
32 33
       end
33  
-       
  34
+
34 35
       # Returns an array of AggregateReflection objects for all the aggregations in the class.
35 36
       def reflect_on_all_aggregations
36 37
         reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
@@ -192,6 +193,49 @@ def counter_cache_column
192 193
         end
193 194
       end
194 195
 
  196
+      def check_validity!
  197
+      end
  198
+
  199
+      def through_reflection
  200
+        false
  201
+      end
  202
+
  203
+      def source_reflection
  204
+        nil
  205
+      end
  206
+
  207
+      private
  208
+        def derive_class_name
  209
+          class_name = name.to_s.camelize
  210
+          class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
  211
+          class_name
  212
+        end
  213
+
  214
+        def derive_primary_key_name
  215
+          if macro == :belongs_to
  216
+            "#{name}_id"
  217
+          elsif options[:as]
  218
+            "#{options[:as]}_id"
  219
+          else
  220
+            active_record.name.foreign_key
  221
+          end
  222
+        end
  223
+    end
  224
+
  225
+    # Holds all the meta-data about a :through association as it was specified in the Active Record class.
  226
+    class ThroughReflection < AssociationReflection #:nodoc:
  227
+      # Gets the source of the through reflection.  It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
  228
+      # (The <tt>:tags</tt> association on Tagging below.)
  229
+      #
  230
+      #   class Post < ActiveRecord::Base
  231
+      #     has_many :taggings
  232
+      #     has_many :tags, :through => :taggings
  233
+      #   end
  234
+      #
  235
+      def source_reflection
  236
+        @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
  237
+      end
  238
+
195 239
       # Returns the AssociationReflection object specified in the <tt>:through</tt> option
196 240
       # of a HasManyThrough or HasOneThrough association. Example:
197 241
       #
@@ -204,7 +248,7 @@ def counter_cache_column
204 248
       #   taggings_reflection = tags_reflection.through_reflection
205 249
       #
206 250
       def through_reflection
207  
-        @through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
  251
+        @through_reflection ||= active_record.reflect_on_association(options[:through])
208 252
       end
209 253
 
210 254
       # Gets an array of possible <tt>:through</tt> source reflection names:
@@ -215,63 +259,32 @@ def source_reflection_names
215 259
         @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
216 260
       end
217 261
 
218  
-      # Gets the source of the through reflection.  It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
219  
-      # (The <tt>:tags</tt> association on Tagging below.)
220  
-      # 
221  
-      #   class Post < ActiveRecord::Base
222  
-      #     has_many :taggings
223  
-      #     has_many :tags, :through => :taggings
224  
-      #   end
225  
-      #
226  
-      def source_reflection
227  
-        return nil unless through_reflection
228  
-        @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
229  
-      end
230  
-
231 262
       def check_validity!
232  
-        if options[:through]
233  
-          if through_reflection.nil?
234  
-            raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
235  
-          end
236  
-          
237  
-          if source_reflection.nil?
238  
-            raise HasManyThroughSourceAssociationNotFoundError.new(self)
239  
-          end
  263
+        if through_reflection.nil?
  264
+          raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
  265
+        end
240 266
 
241  
-          if options[:source_type] && source_reflection.options[:polymorphic].nil?
242  
-            raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
243  
-          end
244  
-          
245  
-          if source_reflection.options[:polymorphic] && options[:source_type].nil?
246  
-            raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
247  
-          end
248  
-          
249  
-          unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
250  
-            raise HasManyThroughSourceAssociationMacroError.new(self)
251  
-          end
  267
+        if source_reflection.nil?
  268
+          raise HasManyThroughSourceAssociationNotFoundError.new(self)
  269
+        end
  270
+
  271
+        if options[:source_type] && source_reflection.options[:polymorphic].nil?
  272
+          raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
  273
+        end
  274
+
  275
+        if source_reflection.options[:polymorphic] && options[:source_type].nil?
  276
+          raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
  277
+        end
  278
+
  279
+        unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
  280
+          raise HasManyThroughSourceAssociationMacroError.new(self)
252 281
         end
253 282
       end
254 283
 
255 284
       private
256 285
         def derive_class_name
257 286
           # get the class_name of the belongs_to association of the through reflection
258  
-          if through_reflection
259  
-            options[:source_type] || source_reflection.class_name
260  
-          else
261  
-            class_name = name.to_s.camelize
262  
-            class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
263  
-            class_name
264  
-          end
265  
-        end
266  
-
267  
-        def derive_primary_key_name
268  
-          if macro == :belongs_to
269  
-            "#{name}_id"
270  
-          elsif options[:as]
271  
-            "#{options[:as]}_id"
272  
-          else
273  
-            active_record.name.foreign_key
274  
-          end
  287
+          options[:source_type] || source_reflection.class_name
275 288
         end
276 289
     end
277 290
   end
4  activerecord/test/cases/reflection_test.rb
@@ -170,6 +170,10 @@ def test_reflection_should_not_raise_error_when_compared_to_other_object
170 170
     assert_nothing_raised { Firm.reflections[:clients] == Object.new }
171 171
   end
172 172
 
  173
+  def test_has_many_through_reflection
  174
+    assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
  175
+  end
  176
+
173 177
   private
174 178
     def assert_reflection(klass, association, options)
175 179
       assert reflection = klass.reflect_on_association(association)

0 notes on commit 25ca21a

Please sign in to comment.
Something went wrong with that request. Please try again.