activerecord 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +356 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. metadata +224 -0
@@ -0,0 +1,195 @@
1
+ require 'stringio'
2
+ require 'active_support/core_ext/big_decimal'
3
+
4
+ module ActiveRecord
5
+ # = Active Record Schema Dumper
6
+ #
7
+ # This class is used to dump the database schema for some connection to some
8
+ # output format (i.e., ActiveRecord::Schema).
9
+ class SchemaDumper #:nodoc:
10
+ private_class_method :new
11
+
12
+ ##
13
+ # :singleton-method:
14
+ # A list of tables which should not be dumped to the schema.
15
+ # Acceptable values are strings as well as regexp.
16
+ # This setting is only used if ActiveRecord::Base.schema_format == :ruby
17
+ cattr_accessor :ignore_tables
18
+ @@ignore_tables = []
19
+
20
+ def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
21
+ new(connection).dump(stream)
22
+ stream
23
+ end
24
+
25
+ def dump(stream)
26
+ header(stream)
27
+ tables(stream)
28
+ trailer(stream)
29
+ stream
30
+ end
31
+
32
+ private
33
+
34
+ def initialize(connection)
35
+ @connection = connection
36
+ @types = @connection.native_database_types
37
+ @version = Migrator::current_version rescue nil
38
+ end
39
+
40
+ def header(stream)
41
+ define_params = @version ? ":version => #{@version}" : ""
42
+
43
+ stream.puts <<HEADER
44
+ # This file is auto-generated from the current state of the database. Instead
45
+ # of editing this file, please use the migrations feature of Active Record to
46
+ # incrementally modify your database, and then regenerate this schema definition.
47
+ #
48
+ # Note that this schema.rb definition is the authoritative source for your
49
+ # database schema. If you need to create the application database on another
50
+ # system, you should be using db:schema:load, not running all the migrations
51
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
52
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
53
+ #
54
+ # It's strongly recommended to check this file into your version control system.
55
+
56
+ ActiveRecord::Schema.define(#{define_params}) do
57
+
58
+ HEADER
59
+ end
60
+
61
+ def trailer(stream)
62
+ stream.puts "end"
63
+ end
64
+
65
+ def tables(stream)
66
+ @connection.tables.sort.each do |tbl|
67
+ next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
68
+ case ignored
69
+ when String; tbl == ignored
70
+ when Regexp; tbl =~ ignored
71
+ else
72
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
73
+ end
74
+ end
75
+ table(tbl, stream)
76
+ end
77
+ end
78
+
79
+ def table(table, stream)
80
+ columns = @connection.columns(table)
81
+ begin
82
+ tbl = StringIO.new
83
+
84
+ # first dump primary key column
85
+ if @connection.respond_to?(:pk_and_sequence_for)
86
+ pk, pk_seq = @connection.pk_and_sequence_for(table)
87
+ elsif @connection.respond_to?(:primary_key)
88
+ pk = @connection.primary_key(table)
89
+ end
90
+
91
+ tbl.print " create_table #{table.inspect}"
92
+ if columns.detect { |c| c.name == pk }
93
+ if pk != 'id'
94
+ tbl.print %Q(, :primary_key => "#{pk}")
95
+ end
96
+ else
97
+ tbl.print ", :id => false"
98
+ end
99
+ tbl.print ", :force => true"
100
+ tbl.puts " do |t|"
101
+
102
+ # then dump all non-primary key columns
103
+ column_specs = columns.map do |column|
104
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
105
+ next if column.name == pk
106
+ spec = {}
107
+ spec[:name] = column.name.inspect
108
+
109
+ # AR has an optimisation which handles zero-scale decimals as integers. This
110
+ # code ensures that the dumper still dumps the column as a decimal.
111
+ spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
112
+ 'decimal'
113
+ else
114
+ column.type.to_s
115
+ end
116
+ spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
117
+ spec[:precision] = column.precision.inspect if !column.precision.nil?
118
+ spec[:scale] = column.scale.inspect if !column.scale.nil?
119
+ spec[:null] = 'false' if !column.null
120
+ spec[:default] = default_string(column.default) if column.has_default?
121
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
122
+ spec
123
+ end.compact
124
+
125
+ # find all migration keys used in this table
126
+ keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
127
+
128
+ # figure out the lengths for each column based on above keys
129
+ lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
130
+
131
+ # the string we're going to sprintf our values against, with standardized column widths
132
+ format_string = lengths.map{ |len| "%-#{len}s" }
133
+
134
+ # find the max length for the 'type' column, which is special
135
+ type_length = column_specs.map{ |column| column[:type].length }.max
136
+
137
+ # add column type definition to our format string
138
+ format_string.unshift " t.%-#{type_length}s "
139
+
140
+ format_string *= ''
141
+
142
+ column_specs.each do |colspec|
143
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
144
+ values.unshift colspec[:type]
145
+ tbl.print((format_string % values).gsub(/,\s*$/, ''))
146
+ tbl.puts
147
+ end
148
+
149
+ tbl.puts " end"
150
+ tbl.puts
151
+
152
+ indexes(table, tbl)
153
+
154
+ tbl.rewind
155
+ stream.print tbl.read
156
+ rescue => e
157
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
158
+ stream.puts "# #{e.message}"
159
+ stream.puts
160
+ end
161
+
162
+ stream
163
+ end
164
+
165
+ def default_string(value)
166
+ case value
167
+ when BigDecimal
168
+ value.to_s
169
+ when Date, DateTime, Time
170
+ "'" + value.to_s(:db) + "'"
171
+ else
172
+ value.inspect
173
+ end
174
+ end
175
+
176
+ def indexes(table, stream)
177
+ if (indexes = @connection.indexes(table)).any?
178
+ add_index_statements = indexes.map do |index|
179
+ statement_parts = [ ('add_index ' + index.table.inspect) ]
180
+ statement_parts << index.columns.inspect
181
+ statement_parts << (':name => ' + index.name.inspect)
182
+ statement_parts << ':unique => true' if index.unique
183
+
184
+ index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
185
+ statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
186
+
187
+ ' ' + statement_parts.join(', ')
188
+ end
189
+
190
+ stream.puts add_index_statements.sort.join("\n")
191
+ stream.puts
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,60 @@
1
+ module ActiveRecord #:nodoc:
2
+ # = Active Record Serialization
3
+ module Serialization
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::Serializers::JSON
6
+
7
+ def serializable_hash(options = nil)
8
+ options ||= {}
9
+
10
+ options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
11
+ options[:except] |= Array.wrap(self.class.inheritance_column)
12
+
13
+ hash = super(options)
14
+
15
+ serializable_add_includes(options) do |association, records, opts|
16
+ hash[association] = records.is_a?(Enumerable) ?
17
+ records.map { |r| r.serializable_hash(opts) } :
18
+ records.serializable_hash(opts)
19
+ end
20
+
21
+ hash
22
+ end
23
+
24
+ private
25
+ # Add associations specified via the <tt>:includes</tt> option.
26
+ #
27
+ # Expects a block that takes as arguments:
28
+ # +association+ - name of the association
29
+ # +records+ - the association record(s) to be serialized
30
+ # +opts+ - options for the association records
31
+ def serializable_add_includes(options = {})
32
+ return unless include_associations = options.delete(:include)
33
+
34
+ base_only_or_except = { :except => options[:except],
35
+ :only => options[:only] }
36
+
37
+ include_has_options = include_associations.is_a?(Hash)
38
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
39
+
40
+ for association in associations
41
+ records = case self.class.reflect_on_association(association).macro
42
+ when :has_many, :has_and_belongs_to_many
43
+ send(association).to_a
44
+ when :has_one, :belongs_to
45
+ send(association)
46
+ end
47
+
48
+ unless records.nil?
49
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
50
+ opts = options.merge(association_options)
51
+ yield(association, records, opts)
52
+ end
53
+ end
54
+
55
+ options[:include] = include_associations
56
+ end
57
+ end
58
+ end
59
+
60
+ require 'active_record/serializers/xml_serializer'
@@ -0,0 +1,244 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/hash/conversions'
3
+
4
+ module ActiveRecord #:nodoc:
5
+ module Serialization
6
+ include ActiveModel::Serializers::Xml
7
+
8
+ # Builds an XML document to represent the model. Some configuration is
9
+ # available through +options+. However more complicated cases should
10
+ # override ActiveRecord::Base#to_xml.
11
+ #
12
+ # By default the generated XML document will include the processing
13
+ # instruction and all the object's attributes. For example:
14
+ #
15
+ # <?xml version="1.0" encoding="UTF-8"?>
16
+ # <topic>
17
+ # <title>The First Topic</title>
18
+ # <author-name>David</author-name>
19
+ # <id type="integer">1</id>
20
+ # <approved type="boolean">false</approved>
21
+ # <replies-count type="integer">0</replies-count>
22
+ # <bonus-time type="datetime">2000-01-01T08:28:00+12:00</bonus-time>
23
+ # <written-on type="datetime">2003-07-16T09:28:00+1200</written-on>
24
+ # <content>Have a nice day</content>
25
+ # <author-email-address>david@loudthinking.com</author-email-address>
26
+ # <parent-id></parent-id>
27
+ # <last-read type="date">2004-04-15</last-read>
28
+ # </topic>
29
+ #
30
+ # This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
31
+ # <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
32
+ # The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
33
+ # +attributes+ method. The default is to dasherize all column names, but you
34
+ # can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
35
+ # to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
36
+ # To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
37
+ #
38
+ # For instance:
39
+ #
40
+ # topic.to_xml(:skip_instruct => true, :except => [ :id, :bonus_time, :written_on, :replies_count ])
41
+ #
42
+ # <topic>
43
+ # <title>The First Topic</title>
44
+ # <author-name>David</author-name>
45
+ # <approved type="boolean">false</approved>
46
+ # <content>Have a nice day</content>
47
+ # <author-email-address>david@loudthinking.com</author-email-address>
48
+ # <parent-id></parent-id>
49
+ # <last-read type="date">2004-04-15</last-read>
50
+ # </topic>
51
+ #
52
+ # To include first level associations use <tt>:include</tt>:
53
+ #
54
+ # firm.to_xml :include => [ :account, :clients ]
55
+ #
56
+ # <?xml version="1.0" encoding="UTF-8"?>
57
+ # <firm>
58
+ # <id type="integer">1</id>
59
+ # <rating type="integer">1</rating>
60
+ # <name>37signals</name>
61
+ # <clients type="array">
62
+ # <client>
63
+ # <rating type="integer">1</rating>
64
+ # <name>Summit</name>
65
+ # </client>
66
+ # <client>
67
+ # <rating type="integer">1</rating>
68
+ # <name>Microsoft</name>
69
+ # </client>
70
+ # </clients>
71
+ # <account>
72
+ # <id type="integer">1</id>
73
+ # <credit-limit type="integer">50</credit-limit>
74
+ # </account>
75
+ # </firm>
76
+ #
77
+ # Additionally, the record being serialized will be passed to a Proc's second
78
+ # parameter. This allows for ad hoc additions to the resultant document that
79
+ # incorporate the context of the record being serialized. And by leveraging the
80
+ # closure created by a Proc, to_xml can be used to add elements that normally fall
81
+ # outside of the scope of the model -- for example, generating and appending URLs
82
+ # associated with models.
83
+ #
84
+ # proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
85
+ # firm.to_xml :procs => [ proc ]
86
+ #
87
+ # <firm>
88
+ # # ... normal attributes as shown above ...
89
+ # <name-reverse>slangis73</name-reverse>
90
+ # </firm>
91
+ #
92
+ # To include deeper levels of associations pass a hash like this:
93
+ #
94
+ # firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
95
+ # <?xml version="1.0" encoding="UTF-8"?>
96
+ # <firm>
97
+ # <id type="integer">1</id>
98
+ # <rating type="integer">1</rating>
99
+ # <name>37signals</name>
100
+ # <clients type="array">
101
+ # <client>
102
+ # <rating type="integer">1</rating>
103
+ # <name>Summit</name>
104
+ # <address>
105
+ # ...
106
+ # </address>
107
+ # </client>
108
+ # <client>
109
+ # <rating type="integer">1</rating>
110
+ # <name>Microsoft</name>
111
+ # <address>
112
+ # ...
113
+ # </address>
114
+ # </client>
115
+ # </clients>
116
+ # <account>
117
+ # <id type="integer">1</id>
118
+ # <credit-limit type="integer">50</credit-limit>
119
+ # </account>
120
+ # </firm>
121
+ #
122
+ # To include any methods on the model being called use <tt>:methods</tt>:
123
+ #
124
+ # firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
125
+ #
126
+ # <firm>
127
+ # # ... normal attributes as shown above ...
128
+ # <calculated-earnings>100000000000000000</calculated-earnings>
129
+ # <real-earnings>5</real-earnings>
130
+ # </firm>
131
+ #
132
+ # To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
133
+ # modified version of the options hash that was given to +to_xml+:
134
+ #
135
+ # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
136
+ # firm.to_xml :procs => [ proc ]
137
+ #
138
+ # <firm>
139
+ # # ... normal attributes as shown above ...
140
+ # <abc>def</abc>
141
+ # </firm>
142
+ #
143
+ # Alternatively, you can yield the builder object as part of the +to_xml+ call:
144
+ #
145
+ # firm.to_xml do |xml|
146
+ # xml.creator do
147
+ # xml.first_name "David"
148
+ # xml.last_name "Heinemeier Hansson"
149
+ # end
150
+ # end
151
+ #
152
+ # <firm>
153
+ # # ... normal attributes as shown above ...
154
+ # <creator>
155
+ # <first_name>David</first_name>
156
+ # <last_name>Heinemeier Hansson</last_name>
157
+ # </creator>
158
+ # </firm>
159
+ #
160
+ # As noted above, you may override +to_xml+ in your ActiveRecord::Base
161
+ # subclasses to have complete control about what's generated. The general
162
+ # form of doing this is:
163
+ #
164
+ # class IHaveMyOwnXML < ActiveRecord::Base
165
+ # def to_xml(options = {})
166
+ # options[:indent] ||= 2
167
+ # xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
168
+ # xml.instruct! unless options[:skip_instruct]
169
+ # xml.level_one do
170
+ # xml.tag!(:second_level, 'content')
171
+ # end
172
+ # end
173
+ # end
174
+ def to_xml(options = {}, &block)
175
+ XmlSerializer.new(self, options).serialize(&block)
176
+ end
177
+ end
178
+
179
+ class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
180
+ def initialize(*args)
181
+ super
182
+ options[:except] |= Array.wrap(@serializable.class.inheritance_column)
183
+ end
184
+
185
+ def add_extra_behavior
186
+ add_includes
187
+ end
188
+
189
+ def add_includes
190
+ procs = options.delete(:procs)
191
+ @serializable.send(:serializable_add_includes, options) do |association, records, opts|
192
+ add_associations(association, records, opts)
193
+ end
194
+ options[:procs] = procs
195
+ end
196
+
197
+ # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
198
+ def add_associations(association, records, opts)
199
+ association_name = association.to_s.singularize
200
+ merged_options = options.merge(opts).merge!(:root => association_name, :skip_instruct => true)
201
+
202
+ if records.is_a?(Enumerable)
203
+ tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
204
+ type = options[:skip_types] ? { } : {:type => "array"}
205
+
206
+ if records.empty?
207
+ @builder.tag!(tag, type)
208
+ else
209
+ @builder.tag!(tag, type) do
210
+ records.each do |record|
211
+ if options[:skip_types]
212
+ record_type = {}
213
+ else
214
+ record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
215
+ record_type = {:type => record_class}
216
+ end
217
+
218
+ record.to_xml merged_options.merge(record_type)
219
+ end
220
+ end
221
+ end
222
+ elsif record = @serializable.send(association)
223
+ record.to_xml(merged_options)
224
+ end
225
+ end
226
+
227
+ class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
228
+ def compute_type
229
+ type = @serializable.class.serialized_attributes.has_key?(name) ?
230
+ super : @serializable.class.columns_hash[name].type
231
+
232
+ case type
233
+ when :text
234
+ :string
235
+ when :time
236
+ :datetime
237
+ else
238
+ type
239
+ end
240
+ end
241
+ protected :compute_type
242
+ end
243
+ end
244
+ end