Skip to content
This repository
Browse code

Added :include option to to_json (closes #9677) [chuyeow]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7663 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit bbdb4e55f6eceb40c2047c614f5b47cef253dfb0 1 parent 078bd05
David Heinemeier Hansson authored September 28, 2007
2  activerecord/CHANGELOG
@@ -10,7 +10,7 @@
10 10
 
11 11
 * Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe #3998 [robbyrussell/tarmo]
12 12
 
13  
-* Added ActiveRecord::Base#to_json/from_json (currently does not support :include like to_xml) [DHH]
  13
+* Added ActiveRecord::Base#to_json/from_json [DHH/chuyeow]
14 14
 
15 15
 * Added ActiveRecord::Base#from_xml [DHH]. Example:
16 16
 
51  activerecord/lib/active_record/serialization.rb
@@ -2,7 +2,7 @@ module ActiveRecord #:nodoc:
2 2
   module Serialization
3 3
     class Serializer #:nodoc:
4 4
       attr_reader :options
5  
-    
  5
+
6 6
       def initialize(record, options = {})
7 7
         @record, @options = record, options.dup
8 8
       end
@@ -23,31 +23,70 @@ def serializable_attribute_names
23 23
           options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
24 24
           attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
25 25
         end
26  
-      
  26
+
27 27
         attribute_names
28 28
       end
29 29
 
30 30
       def serializable_method_names
31 31
         Array(options[:methods]).inject([]) do |method_attributes, name|
32  
-          method_attributes << :name if @record.respond_to?(name.to_s)
  32
+          method_attributes << name if @record.respond_to?(name.to_s)
33 33
           method_attributes
34 34
         end
35 35
       end
36  
-      
  36
+
37 37
       def serializable_names
38 38
         serializable_attribute_names + serializable_method_names
39 39
       end
40 40
 
  41
+      # Add associations specified via the :includes option.
  42
+      # Expects a block that takes as arguments:
  43
+      #   +association+ - name of the association
  44
+      #   +records+     - the association record(s) to be serialized
  45
+      #   +opts+        - options for the association records
  46
+      def add_includes(&block)
  47
+        if include_associations = options.delete(:include)
  48
+          base_only_or_except = { :except => options[:except],
  49
+                                  :only => options[:only] }
  50
+
  51
+          include_has_options = include_associations.is_a?(Hash)
  52
+          associations = include_has_options ? include_associations.keys : Array(include_associations)
  53
+
  54
+          for association in associations
  55
+            records = case @record.class.reflect_on_association(association).macro
  56
+            when :has_many, :has_and_belongs_to_many
  57
+              @record.send(association).to_a
  58
+            when :has_one, :belongs_to
  59
+              @record.send(association)
  60
+            end
  61
+
  62
+            unless records.nil?
  63
+              association_options = include_has_options ? include_associations[association] : base_only_or_except
  64
+              opts = options.merge(association_options)
  65
+              yield(association, records, opts)
  66
+            end
  67
+          end
  68
+
  69
+          options[:include] = include_associations
  70
+        end
  71
+      end
  72
+
41 73
       def serializable_record
42 74
         returning(serializable_record = {}) do
43 75
           serializable_names.each { |name| serializable_record[name] = @record.send(name) }
  76
+          add_includes do |association, records, opts|
  77
+            if records.is_a?(Enumerable)
  78
+              serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
  79
+            else
  80
+              serializable_record[association] = self.class.new(records, opts).serializable_record
  81
+            end
  82
+          end
44 83
         end
45 84
       end
46 85
 
47 86
       def serialize
48 87
         # overwrite to implement
49  
-      end        
50  
-    
  88
+      end
  89
+
51 90
       def to_s(&block)
52 91
         serialize(&block)
53 92
       end
94  activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -141,7 +141,7 @@ def builder
141 141
           builder.instruct!
142 142
           options[:skip_instruct] = true
143 143
         end
144  
-        
  144
+
145 145
         builder
146 146
       end
147 147
     end
@@ -150,7 +150,7 @@ def root
150 150
       root = (options[:root] || @record.class.to_s.underscore).to_s
151 151
       dasherize? ? root.dasherize : root
152 152
     end
153  
-    
  153
+
154 154
     def dasherize?
155 155
       !options.has_key?(:dasherize) || options[:dasherize]
156 156
     end
@@ -179,47 +179,6 @@ def add_attributes
179 179
       end
180 180
     end
181 181
 
182  
-    def add_includes
183  
-      if include_associations = options.delete(:include)
184  
-        root_only_or_except = { :except => options[:except],
185  
-                                :only => options[:only] }
186  
-
187  
-        include_has_options = include_associations.is_a?(Hash)
188  
-
189  
-        for association in include_has_options ? include_associations.keys : Array(include_associations)
190  
-          association_options = include_has_options ? include_associations[association] : root_only_or_except
191  
-
192  
-          opts = options.merge(association_options)
193  
-
194  
-          case @record.class.reflect_on_association(association).macro
195  
-          when :has_many, :has_and_belongs_to_many
196  
-            records = @record.send(association).to_a
197  
-            tag = association.to_s
198  
-            tag = tag.dasherize if dasherize?
199  
-            if records.empty?
200  
-              builder.tag!(tag, :type => :array)
201  
-            else
202  
-              builder.tag!(tag, :type => :array) do
203  
-                association_name = association.to_s.singularize
204  
-                records.each do |record| 
205  
-                  record.to_xml opts.merge(
206  
-                    :root => association_name, 
207  
-                    :type => (record.class.to_s.underscore == association_name ? nil : record.class.name)
208  
-                  )
209  
-                end
210  
-              end
211  
-            end
212  
-          when :has_one, :belongs_to
213  
-            if record = @record.send(association)
214  
-              record.to_xml(opts.merge(:root => association))
215  
-            end
216  
-          end
217  
-        end
218  
-
219  
-        options[:include] = include_associations
220  
-      end
221  
-    end
222  
-
223 182
     def add_procs
224 183
       if procs = options.delete(:procs)
225 184
         [ *procs ].each do |proc|
@@ -228,7 +187,6 @@ def add_procs
228 187
       end
229 188
     end
230 189
 
231  
-
232 190
     def add_tag(attribute)
233 191
       builder.tag!(
234 192
         dasherize? ? attribute.name.dasherize : attribute.name, 
@@ -237,30 +195,54 @@ def add_tag(attribute)
237 195
       )
238 196
     end
239 197
 
  198
+    def add_associations(association, records, opts)
  199
+      if records.is_a?(Enumerable)
  200
+        tag = association.to_s
  201
+        tag = tag.dasherize if dasherize?
  202
+        if records.empty?
  203
+          builder.tag!(tag, :type => :array)
  204
+        else
  205
+          builder.tag!(tag, :type => :array) do
  206
+            association_name = association.to_s.singularize
  207
+            records.each do |record| 
  208
+              record.to_xml opts.merge(
  209
+                :root => association_name, 
  210
+                :type => (record.class.to_s.underscore == association_name ? nil : record.class.name)
  211
+              )
  212
+            end
  213
+          end
  214
+        end
  215
+      else
  216
+        if record = @record.send(association)
  217
+          record.to_xml(opts.merge(:root => association))
  218
+        end
  219
+      end
  220
+    end
  221
+
240 222
     def serialize
241 223
       args = [root]
242 224
       if options[:namespace]
243 225
         args << {:xmlns=>options[:namespace]}
244 226
       end
245  
-      
  227
+
246 228
       if options[:type]
247 229
         args << {:type=>options[:type]}
248 230
       end
249  
-        
  231
+
250 232
       builder.tag!(*args) do
251 233
         add_attributes
252  
-        add_includes
  234
+        add_includes { |association, records, opts| add_associations(association, records, opts) }
253 235
         add_procs
254 236
         yield builder if block_given?
255 237
       end
256  
-    end        
  238
+    end
257 239
 
258 240
     class Attribute #:nodoc:
259 241
       attr_reader :name, :value, :type
260  
-    
  242
+
261 243
       def initialize(name, record)
262 244
         @name, @record = name, record
263  
-      
  245
+
264 246
         @type  = compute_type
265 247
         @value = compute_value
266 248
       end
@@ -277,21 +259,21 @@ def initialize(name, record)
277 259
       def needs_encoding?
278 260
         ![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
279 261
       end
280  
-    
  262
+
281 263
       def decorations(include_types = true)
282 264
         decorations = {}
283 265
 
284 266
         if type == :binary
285 267
           decorations[:encoding] = 'base64'
286 268
         end
287  
-      
  269
+
288 270
         if include_types && type != :string
289 271
           decorations[:type] = type
290 272
         end
291  
-      
  273
+
292 274
         decorations
293 275
       end
294  
-    
  276
+
295 277
       protected
296 278
         def compute_type
297 279
           type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
@@ -305,10 +287,10 @@ def compute_type
305 287
               type
306 288
           end
307 289
         end
308  
-    
  290
+
309 291
         def compute_value
310 292
           value = @record.send(name)
311  
-        
  293
+
312 294
           if formatter = Hash::XML_FORMATTING[type.to_s]
313 295
             value ? formatter.call(value) : nil
314 296
           else

0 notes on commit bbdb4e5

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