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.
- data/CHANGELOG +6023 -0
- data/README.rdoc +222 -0
- data/examples/associations.png +0 -0
- data/examples/performance.rb +162 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +124 -0
- data/lib/active_record/aggregations.rb +277 -0
- data/lib/active_record/association_preload.rb +403 -0
- data/lib/active_record/associations.rb +2254 -0
- data/lib/active_record/associations/association_collection.rb +562 -0
- data/lib/active_record/associations/association_proxy.rb +295 -0
- data/lib/active_record/associations/belongs_to_association.rb +91 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
- data/lib/active_record/associations/has_many_association.rb +128 -0
- data/lib/active_record/associations/has_many_through_association.rb +116 -0
- data/lib/active_record/associations/has_one_association.rb +143 -0
- data/lib/active_record/associations/has_one_through_association.rb +40 -0
- data/lib/active_record/associations/through_association_scope.rb +154 -0
- data/lib/active_record/attribute_methods.rb +60 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +95 -0
- data/lib/active_record/attribute_methods/primary_key.rb +50 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +369 -0
- data/lib/active_record/base.rb +1867 -0
- data/lib/active_record/callbacks.rb +288 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
- data/lib/active_record/counter_cache.rb +115 -0
- data/lib/active_record/dynamic_finder_match.rb +53 -0
- data/lib/active_record/dynamic_scope_match.rb +32 -0
- data/lib/active_record/errors.rb +172 -0
- data/lib/active_record/fixtures.rb +1008 -0
- data/lib/active_record/locale/en.yml +40 -0
- data/lib/active_record/locking/optimistic.rb +172 -0
- data/lib/active_record/locking/pessimistic.rb +55 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +617 -0
- data/lib/active_record/named_scope.rb +138 -0
- data/lib/active_record/nested_attributes.rb +417 -0
- data/lib/active_record/observer.rb +140 -0
- data/lib/active_record/persistence.rb +291 -0
- data/lib/active_record/query_cache.rb +36 -0
- data/lib/active_record/railtie.rb +91 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +512 -0
- data/lib/active_record/reflection.rb +403 -0
- data/lib/active_record/relation.rb +393 -0
- data/lib/active_record/relation/batches.rb +89 -0
- data/lib/active_record/relation/calculations.rb +286 -0
- data/lib/active_record/relation/finder_methods.rb +355 -0
- data/lib/active_record/relation/predicate_builder.rb +41 -0
- data/lib/active_record/relation/query_methods.rb +261 -0
- data/lib/active_record/relation/spawn_methods.rb +112 -0
- data/lib/active_record/schema.rb +59 -0
- data/lib/active_record/schema_dumper.rb +195 -0
- data/lib/active_record/serialization.rb +60 -0
- data/lib/active_record/serializers/xml_serializer.rb +244 -0
- data/lib/active_record/session_store.rb +340 -0
- data/lib/active_record/test_case.rb +67 -0
- data/lib/active_record/timestamp.rb +88 -0
- data/lib/active_record/transactions.rb +356 -0
- data/lib/active_record/validations.rb +84 -0
- data/lib/active_record/validations/associated.rb +48 -0
- data/lib/active_record/validations/uniqueness.rb +185 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/rails/generators/active_record.rb +27 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
- 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
|