activerecord 1.15.6 → 2.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 +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- data/test/mixin_nested_set_test.rb +0 -196
@@ -0,0 +1,71 @@
|
|
1
|
+
module ActiveRecord #:nodoc:
|
2
|
+
module Serialization
|
3
|
+
# Returns a JSON string representing the model. Some configuration is
|
4
|
+
# available through +options+.
|
5
|
+
#
|
6
|
+
# Without any +options+, the returned JSON string will include all
|
7
|
+
# the model's attributes. For example:
|
8
|
+
#
|
9
|
+
# konata = User.find(1)
|
10
|
+
# konata.to_json
|
11
|
+
#
|
12
|
+
# {"id": 1, "name": "Konata Izumi", "age": 16,
|
13
|
+
# "created_at": "2006/08/01", "awesome": true}
|
14
|
+
#
|
15
|
+
# The :only and :except options can be used to limit the attributes
|
16
|
+
# included, and work similar to the #attributes method. For example:
|
17
|
+
#
|
18
|
+
# konata.to_json(:only => [ :id, :name ])
|
19
|
+
#
|
20
|
+
# {"id": 1, "name": "Konata Izumi"}
|
21
|
+
#
|
22
|
+
# konata.to_json(:except => [ :id, :created_at, :age ])
|
23
|
+
#
|
24
|
+
# {"name": "Konata Izumi", "awesome": true}
|
25
|
+
#
|
26
|
+
# To include any methods on the model, use :methods.
|
27
|
+
#
|
28
|
+
# konata.to_json(:methods => :permalink)
|
29
|
+
#
|
30
|
+
# {"id": 1, "name": "Konata Izumi", "age": 16,
|
31
|
+
# "created_at": "2006/08/01", "awesome": true,
|
32
|
+
# "permalink": "1-konata-izumi"}
|
33
|
+
#
|
34
|
+
# To include associations, use :include.
|
35
|
+
#
|
36
|
+
# konata.to_json(:include => :posts)
|
37
|
+
#
|
38
|
+
# {"id": 1, "name": "Konata Izumi", "age": 16,
|
39
|
+
# "created_at": "2006/08/01", "awesome": true,
|
40
|
+
# "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
|
41
|
+
# {"id": 2, author_id: 1, "title": "So I was thinking"}]}
|
42
|
+
#
|
43
|
+
# 2nd level and higher order associations work as well:
|
44
|
+
#
|
45
|
+
# konata.to_json(:include => { :posts => {
|
46
|
+
# :include => { :comments => {
|
47
|
+
# :only => :body } },
|
48
|
+
# :only => :title } })
|
49
|
+
#
|
50
|
+
# {"id": 1, "name": "Konata Izumi", "age": 16,
|
51
|
+
# "created_at": "2006/08/01", "awesome": true,
|
52
|
+
# "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
|
53
|
+
# "title": "Welcome to the weblog"},
|
54
|
+
# {"comments": [{"body": "Don't think too hard"}],
|
55
|
+
# "title": "So I was thinking"}]}
|
56
|
+
def to_json(options = {})
|
57
|
+
JsonSerializer.new(self, options).to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def from_json(json)
|
61
|
+
self.attributes = ActiveSupport::JSON.decode(json)
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
66
|
+
def serialize
|
67
|
+
serializable_record.to_json
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ActiveRecord #:nodoc:
|
2
|
-
module
|
2
|
+
module Serialization
|
3
3
|
# Builds an XML document to represent the model. Some configuration is
|
4
|
-
#
|
4
|
+
# available through +options+, however more complicated cases should
|
5
5
|
# override ActiveRecord's to_xml.
|
6
6
|
#
|
7
|
-
# By default the generated XML document will include the processing
|
7
|
+
# By default the generated XML document will include the processing
|
8
8
|
# instruction and all object's attributes. For example:
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# <?xml version="1.0" encoding="UTF-8"?>
|
11
11
|
# <topic>
|
12
12
|
# <title>The First Topic</title>
|
@@ -27,7 +27,7 @@ module ActiveRecord #:nodoc:
|
|
27
27
|
# :except options are the same as for the #attributes method.
|
28
28
|
# The default is to dasherize all column names, to disable this,
|
29
29
|
# set :dasherize to false. To not have the column type included
|
30
|
-
# in the XML output, set :skip_types to
|
30
|
+
# in the XML output, set :skip_types to true.
|
31
31
|
#
|
32
32
|
# For instance:
|
33
33
|
#
|
@@ -42,7 +42,7 @@ module ActiveRecord #:nodoc:
|
|
42
42
|
# <parent-id></parent-id>
|
43
43
|
# <last-read type="date">2004-04-15</last-read>
|
44
44
|
# </topic>
|
45
|
-
#
|
45
|
+
#
|
46
46
|
# To include first level associations use :include
|
47
47
|
#
|
48
48
|
# firm.to_xml :include => [ :account, :clients ]
|
@@ -52,7 +52,7 @@ module ActiveRecord #:nodoc:
|
|
52
52
|
# <id type="integer">1</id>
|
53
53
|
# <rating type="integer">1</rating>
|
54
54
|
# <name>37signals</name>
|
55
|
-
# <clients>
|
55
|
+
# <clients type="array">
|
56
56
|
# <client>
|
57
57
|
# <rating type="integer">1</rating>
|
58
58
|
# <name>Summit</name>
|
@@ -90,7 +90,24 @@ module ActiveRecord #:nodoc:
|
|
90
90
|
# <abc>def</abc>
|
91
91
|
# </firm>
|
92
92
|
#
|
93
|
-
#
|
93
|
+
# Alternatively, you can also just yield the builder object as part of the to_xml call:
|
94
|
+
#
|
95
|
+
# firm.to_xml do |xml|
|
96
|
+
# xml.creator do
|
97
|
+
# xml.first_name "David"
|
98
|
+
# xml.last_name "Heinemeier Hansson"
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# <firm>
|
103
|
+
# # ... normal attributes as shown above ...
|
104
|
+
# <creator>
|
105
|
+
# <first_name>David</first_name>
|
106
|
+
# <last_name>Heinemeier Hansson</last_name>
|
107
|
+
# </creator>
|
108
|
+
# </firm>
|
109
|
+
#
|
110
|
+
# You can override the to_xml method in your ActiveRecord::Base
|
94
111
|
# subclasses if you need to. The general form of doing this is
|
95
112
|
#
|
96
113
|
# class IHaveMyOwnXML < ActiveRecord::Base
|
@@ -103,18 +120,18 @@ module ActiveRecord #:nodoc:
|
|
103
120
|
# end
|
104
121
|
# end
|
105
122
|
# end
|
106
|
-
def to_xml(options = {})
|
107
|
-
XmlSerializer.new(self, options)
|
123
|
+
def to_xml(options = {}, &block)
|
124
|
+
serializer = XmlSerializer.new(self, options)
|
125
|
+
block_given? ? serializer.to_s(&block) : serializer.to_s
|
108
126
|
end
|
109
|
-
end
|
110
127
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
def initialize(record, options = {})
|
115
|
-
@record, @options = record, options.dup
|
128
|
+
def from_xml(xml)
|
129
|
+
self.attributes = Hash.from_xml(xml).values.first
|
130
|
+
self
|
116
131
|
end
|
117
|
-
|
132
|
+
end
|
133
|
+
|
134
|
+
class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
118
135
|
def builder
|
119
136
|
@builder ||= begin
|
120
137
|
options[:indent] ||= 2
|
@@ -124,7 +141,7 @@ module ActiveRecord #:nodoc:
|
|
124
141
|
builder.instruct!
|
125
142
|
options[:skip_instruct] = true
|
126
143
|
end
|
127
|
-
|
144
|
+
|
128
145
|
builder
|
129
146
|
end
|
130
147
|
end
|
@@ -133,7 +150,7 @@ module ActiveRecord #:nodoc:
|
|
133
150
|
root = (options[:root] || @record.class.to_s.underscore).to_s
|
134
151
|
dasherize? ? root.dasherize : root
|
135
152
|
end
|
136
|
-
|
153
|
+
|
137
154
|
def dasherize?
|
138
155
|
!options.has_key?(:dasherize) || options[:dasherize]
|
139
156
|
end
|
@@ -146,64 +163,22 @@ module ActiveRecord #:nodoc:
|
|
146
163
|
# level model can have both :except and :only set. So if
|
147
164
|
# :only is set, always delete :except.
|
148
165
|
def serializable_attributes
|
149
|
-
|
150
|
-
|
151
|
-
if options[:only]
|
152
|
-
options.delete(:except)
|
153
|
-
attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
|
154
|
-
else
|
155
|
-
options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
|
156
|
-
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
|
157
|
-
end
|
158
|
-
|
159
|
-
attribute_names.collect { |name| Attribute.new(name, @record) }
|
166
|
+
serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
|
160
167
|
end
|
161
168
|
|
162
169
|
def serializable_method_attributes
|
163
|
-
Array(options[:methods]).
|
170
|
+
Array(options[:methods]).inject([]) do |method_attributes, name|
|
171
|
+
method_attributes << MethodAttribute.new(name.to_s, @record) if @record.respond_to?(name.to_s)
|
172
|
+
method_attributes
|
173
|
+
end
|
164
174
|
end
|
165
175
|
|
166
|
-
|
167
176
|
def add_attributes
|
168
177
|
(serializable_attributes + serializable_method_attributes).each do |attribute|
|
169
178
|
add_tag(attribute)
|
170
179
|
end
|
171
180
|
end
|
172
181
|
|
173
|
-
def add_includes
|
174
|
-
if include_associations = options.delete(:include)
|
175
|
-
root_only_or_except = { :except => options[:except],
|
176
|
-
:only => options[:only] }
|
177
|
-
|
178
|
-
include_has_options = include_associations.is_a?(Hash)
|
179
|
-
|
180
|
-
for association in include_has_options ? include_associations.keys : Array(include_associations)
|
181
|
-
association_options = include_has_options ? include_associations[association] : root_only_or_except
|
182
|
-
|
183
|
-
opts = options.merge(association_options)
|
184
|
-
|
185
|
-
case @record.class.reflect_on_association(association).macro
|
186
|
-
when :has_many, :has_and_belongs_to_many
|
187
|
-
records = @record.send(association).to_a
|
188
|
-
unless records.empty?
|
189
|
-
tag = records.first.class.to_s.underscore.pluralize
|
190
|
-
tag = tag.dasherize if dasherize?
|
191
|
-
|
192
|
-
builder.tag!(tag) do
|
193
|
-
records.each { |r| r.to_xml(opts.merge(:root => association.to_s.singularize)) }
|
194
|
-
end
|
195
|
-
end
|
196
|
-
when :has_one, :belongs_to
|
197
|
-
if record = @record.send(association)
|
198
|
-
record.to_xml(opts.merge(:root => association))
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
options[:include] = include_associations
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
182
|
def add_procs
|
208
183
|
if procs = options.delete(:procs)
|
209
184
|
[ *procs ].each do |proc|
|
@@ -212,36 +187,64 @@ module ActiveRecord #:nodoc:
|
|
212
187
|
end
|
213
188
|
end
|
214
189
|
|
215
|
-
|
216
190
|
def add_tag(attribute)
|
217
191
|
builder.tag!(
|
218
|
-
dasherize? ? attribute.name.dasherize : attribute.name,
|
219
|
-
attribute.value.to_s,
|
192
|
+
dasherize? ? attribute.name.dasherize : attribute.name,
|
193
|
+
attribute.value.to_s,
|
220
194
|
attribute.decorations(!options[:skip_types])
|
221
195
|
)
|
222
196
|
end
|
223
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
|
+
|
224
222
|
def serialize
|
225
223
|
args = [root]
|
226
224
|
if options[:namespace]
|
227
225
|
args << {:xmlns=>options[:namespace]}
|
228
226
|
end
|
229
|
-
|
227
|
+
|
228
|
+
if options[:type]
|
229
|
+
args << {:type=>options[:type]}
|
230
|
+
end
|
231
|
+
|
230
232
|
builder.tag!(*args) do
|
231
233
|
add_attributes
|
232
|
-
|
234
|
+
procs = options.delete(:procs)
|
235
|
+
add_includes { |association, records, opts| add_associations(association, records, opts) }
|
236
|
+
options[:procs] = procs
|
233
237
|
add_procs
|
238
|
+
yield builder if block_given?
|
234
239
|
end
|
235
|
-
end
|
236
|
-
|
237
|
-
alias_method :to_s, :serialize
|
240
|
+
end
|
238
241
|
|
239
242
|
class Attribute #:nodoc:
|
240
243
|
attr_reader :name, :value, :type
|
241
|
-
|
244
|
+
|
242
245
|
def initialize(name, record)
|
243
246
|
@name, @record = name, record
|
244
|
-
|
247
|
+
|
245
248
|
@type = compute_type
|
246
249
|
@value = compute_value
|
247
250
|
end
|
@@ -258,24 +261,28 @@ module ActiveRecord #:nodoc:
|
|
258
261
|
def needs_encoding?
|
259
262
|
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
|
260
263
|
end
|
261
|
-
|
264
|
+
|
262
265
|
def decorations(include_types = true)
|
263
266
|
decorations = {}
|
264
267
|
|
265
268
|
if type == :binary
|
266
269
|
decorations[:encoding] = 'base64'
|
267
270
|
end
|
268
|
-
|
271
|
+
|
269
272
|
if include_types && type != :string
|
270
273
|
decorations[:type] = type
|
271
274
|
end
|
272
|
-
|
275
|
+
|
276
|
+
if value.nil?
|
277
|
+
decorations[:nil] = true
|
278
|
+
end
|
279
|
+
|
273
280
|
decorations
|
274
281
|
end
|
275
|
-
|
282
|
+
|
276
283
|
protected
|
277
284
|
def compute_type
|
278
|
-
type = @record.class.columns_hash[name].type
|
285
|
+
type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
|
279
286
|
|
280
287
|
case type
|
281
288
|
when :text
|
@@ -286,10 +293,10 @@ module ActiveRecord #:nodoc:
|
|
286
293
|
type
|
287
294
|
end
|
288
295
|
end
|
289
|
-
|
296
|
+
|
290
297
|
def compute_value
|
291
298
|
value = @record.send(name)
|
292
|
-
|
299
|
+
|
293
300
|
if formatter = Hash::XML_FORMATTING[type.to_s]
|
294
301
|
value ? formatter.call(value) : nil
|
295
302
|
else
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# Active Record automatically timestamps create and update if the table has fields
|
3
|
-
# created_at/created_on or updated_at/updated_on.
|
2
|
+
# Active Record automatically timestamps create and update operations if the table has fields
|
3
|
+
# named created_at/created_on or updated_at/updated_on.
|
4
4
|
#
|
5
5
|
# Timestamping can be turned off by setting
|
6
6
|
# <tt>ActiveRecord::Base.record_timestamps = false</tt>
|
@@ -9,34 +9,33 @@ module ActiveRecord
|
|
9
9
|
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
10
10
|
module Timestamp
|
11
11
|
def self.included(base) #:nodoc:
|
12
|
-
super
|
13
|
-
|
14
12
|
base.alias_method_chain :create, :timestamps
|
15
13
|
base.alias_method_chain :update, :timestamps
|
16
14
|
|
17
|
-
base.
|
15
|
+
base.class_inheritable_accessor :record_timestamps, :instance_writer => false
|
18
16
|
base.record_timestamps = true
|
19
17
|
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
private
|
20
|
+
def create_with_timestamps #:nodoc:
|
21
|
+
if record_timestamps
|
22
|
+
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
23
|
+
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
|
24
|
+
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
|
26
25
|
|
27
|
-
|
28
|
-
|
26
|
+
write_attribute('updated_at', t) if respond_to?(:updated_at)
|
27
|
+
write_attribute('updated_on', t) if respond_to?(:updated_on)
|
28
|
+
end
|
29
|
+
create_without_timestamps
|
29
30
|
end
|
30
|
-
create_without_timestamps
|
31
|
-
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
def update_with_timestamps #:nodoc:
|
33
|
+
if record_timestamps
|
34
|
+
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
35
|
+
write_attribute('updated_at', t) if respond_to?(:updated_at)
|
36
|
+
write_attribute('updated_on', t) if respond_to?(:updated_on)
|
37
|
+
end
|
38
|
+
update_without_timestamps
|
38
39
|
end
|
39
|
-
update_without_timestamps
|
40
|
-
end
|
41
40
|
end
|
42
41
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_record/vendor/simple.rb'
|
2
|
-
Transaction::Simple.send(:remove_method, :transaction)
|
3
1
|
require 'thread'
|
4
2
|
|
5
3
|
module ActiveRecord
|
@@ -32,6 +30,19 @@ module ActiveRecord
|
|
32
30
|
# Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though,
|
33
31
|
# that the objects by default will _not_ have their instance data returned to their pre-transactional state.
|
34
32
|
#
|
33
|
+
# == Different ActiveRecord classes in a single transaction
|
34
|
+
#
|
35
|
+
# Though the transaction class method is called on some ActiveRecord class,
|
36
|
+
# the objects within the transaction block need not all be instances of
|
37
|
+
# that class.
|
38
|
+
# In this example a <tt>Balance</tt> record is transactionally saved even
|
39
|
+
# though <tt>transaction</tt> is called on the <tt>Account</tt> class:
|
40
|
+
#
|
41
|
+
# Account.transaction do
|
42
|
+
# balance.save!
|
43
|
+
# account.save!
|
44
|
+
# end
|
45
|
+
#
|
35
46
|
# == Transactions are not distributed across database connections
|
36
47
|
#
|
37
48
|
# A transaction acts on a single database connection. If you have
|
@@ -53,52 +64,20 @@ module ActiveRecord
|
|
53
64
|
#
|
54
65
|
# Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks
|
55
66
|
# will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# == Object-level transactions (deprecated)
|
59
|
-
#
|
60
|
-
# You can enable object-level transactions for Active Record objects, though. You do this by naming each of the Active Records
|
61
|
-
# that you want to enable object-level transactions for, like this:
|
62
|
-
#
|
63
|
-
# Account.transaction(david, mary) do
|
64
|
-
# david.withdrawal(100)
|
65
|
-
# mary.deposit(100)
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# If the transaction fails, David and Mary will be returned to their
|
69
|
-
# pre-transactional state. No money will have changed hands in neither
|
70
|
-
# object nor database.
|
71
|
-
#
|
72
|
-
# However, useful state such as validation errors are also rolled back,
|
73
|
-
# limiting the usefulness of this feature. As such it is deprecated in
|
74
|
-
# Rails 1.2 and will be removed in the next release. Install the
|
75
|
-
# object_transactions plugin if you wish to continue using it.
|
67
|
+
# depends on or you can raise exceptions in the callbacks to rollback.
|
76
68
|
#
|
77
69
|
# == Exception handling
|
78
70
|
#
|
79
71
|
# Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you
|
80
|
-
# should be ready to catch those in your application code.
|
81
|
-
#
|
82
|
-
# Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler.
|
72
|
+
# should be ready to catch those in your application code. One exception is the ActiveRecord::Rollback exception, which will
|
73
|
+
# trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
|
83
74
|
module ClassMethods
|
84
|
-
def transaction(
|
75
|
+
def transaction(&block)
|
85
76
|
previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
|
86
77
|
increment_open_transactions
|
87
78
|
|
88
79
|
begin
|
89
|
-
|
90
|
-
ActiveSupport::Deprecation.warn "Object transactions are deprecated and will be removed from Rails 2.0. See http://www.rubyonrails.org/deprecation for details.", caller
|
91
|
-
objects.each { |o| o.extend(Transaction::Simple) }
|
92
|
-
objects.each { |o| o.start_transaction }
|
93
|
-
end
|
94
|
-
|
95
|
-
result = connection.transaction(Thread.current['start_db_transaction'], &block)
|
96
|
-
|
97
|
-
objects.each { |o| o.commit_transaction }
|
98
|
-
return result
|
99
|
-
rescue Exception => object_transaction_rollback
|
100
|
-
objects.each { |o| o.abort_transaction }
|
101
|
-
raise
|
80
|
+
connection.transaction(Thread.current['start_db_transaction'], &block)
|
102
81
|
ensure
|
103
82
|
decrement_open_transactions
|
104
83
|
trap('TERM', previous_handler)
|
@@ -117,8 +96,8 @@ module ActiveRecord
|
|
117
96
|
end
|
118
97
|
end
|
119
98
|
|
120
|
-
def transaction(
|
121
|
-
self.class.transaction(
|
99
|
+
def transaction(&block)
|
100
|
+
self.class.transaction(&block)
|
122
101
|
end
|
123
102
|
|
124
103
|
def destroy_with_transactions #:nodoc:
|
@@ -126,11 +105,28 @@ module ActiveRecord
|
|
126
105
|
end
|
127
106
|
|
128
107
|
def save_with_transactions(perform_validation = true) #:nodoc:
|
129
|
-
transaction { save_without_transactions(perform_validation) }
|
108
|
+
rollback_active_record_state! { transaction { save_without_transactions(perform_validation) } }
|
130
109
|
end
|
131
110
|
|
132
111
|
def save_with_transactions! #:nodoc:
|
133
|
-
transaction { save_without_transactions! }
|
112
|
+
rollback_active_record_state! { transaction { save_without_transactions! } }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Reset id and @new_record if the transaction rolls back.
|
116
|
+
def rollback_active_record_state!
|
117
|
+
id_present = has_attribute?(self.class.primary_key)
|
118
|
+
previous_id = id
|
119
|
+
previous_new_record = @new_record
|
120
|
+
yield
|
121
|
+
rescue Exception
|
122
|
+
@new_record = previous_new_record
|
123
|
+
if id_present
|
124
|
+
self.id = previous_id
|
125
|
+
else
|
126
|
+
@attributes.delete(self.class.primary_key)
|
127
|
+
@attributes_cache.delete(self.class.primary_key)
|
128
|
+
end
|
129
|
+
raise
|
134
130
|
end
|
135
131
|
end
|
136
132
|
end
|