activerecord 1.14.4 → 1.15.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 +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module Observing # :nodoc:
|
5
|
-
def self.
|
6
|
-
|
7
|
-
base.extend(ClassMethods)
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
@@ -13,18 +13,44 @@ module ActiveRecord
|
|
13
13
|
# # Calls PersonObserver.instance
|
14
14
|
# ActiveRecord::Base.observers = :person_observer
|
15
15
|
#
|
16
|
-
# # Calls Cacher.instance and GarbageCollector.instance
|
16
|
+
# # Calls Cacher.instance and GarbageCollector.instance
|
17
17
|
# ActiveRecord::Base.observers = :cacher, :garbage_collector
|
18
18
|
#
|
19
19
|
# # Same as above, just using explicit class references
|
20
20
|
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
21
|
+
#
|
22
|
+
# Note: Setting this does not instantiate the observers yet. #instantiate_observers is
|
23
|
+
# called during startup, and before each development request.
|
21
24
|
def observers=(*observers)
|
22
|
-
observers =
|
23
|
-
|
24
|
-
|
25
|
+
@observers = observers.flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
# Gets the current observers.
|
29
|
+
def observers
|
30
|
+
@observers ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
# Instantiate the global ActiveRecord observers
|
34
|
+
def instantiate_observers
|
35
|
+
return if @observers.blank?
|
36
|
+
@observers.each do |observer|
|
37
|
+
if observer.respond_to?(:to_sym) # Symbol or String
|
38
|
+
observer.to_s.camelize.constantize.instance
|
39
|
+
elsif observer.respond_to?(:instance)
|
25
40
|
observer.instance
|
41
|
+
else
|
42
|
+
raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
|
43
|
+
end
|
26
44
|
end
|
27
45
|
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
# Notify observers when the observed class is subclassed.
|
49
|
+
def inherited(subclass)
|
50
|
+
super
|
51
|
+
changed
|
52
|
+
notify_observers :observed_class_inherited, subclass
|
53
|
+
end
|
28
54
|
end
|
29
55
|
end
|
30
56
|
|
@@ -85,12 +111,12 @@ module ActiveRecord
|
|
85
111
|
# The observer can implement callback methods for each of the methods described in the Callbacks module.
|
86
112
|
#
|
87
113
|
# == Storing Observers in Rails
|
88
|
-
#
|
114
|
+
#
|
89
115
|
# If you're using Active Record within Rails, observer classes are usually stored in app/models with the
|
90
116
|
# naming convention of app/models/audit_observer.rb.
|
91
117
|
#
|
92
118
|
# == Configuration
|
93
|
-
#
|
119
|
+
#
|
94
120
|
# In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration setting in your
|
95
121
|
# <tt>config/environment.rb</tt> file.
|
96
122
|
#
|
@@ -103,37 +129,50 @@ module ActiveRecord
|
|
103
129
|
|
104
130
|
# Observer subclasses should be reloaded by the dispatcher in Rails
|
105
131
|
# when Dependencies.mechanism = :load.
|
106
|
-
include Reloadable::
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
132
|
+
include Reloadable::Deprecated
|
133
|
+
|
134
|
+
class << self
|
135
|
+
# Attaches the observer to the supplied model classes.
|
136
|
+
def observe(*models)
|
137
|
+
define_method(:observed_classes) { Set.new(models) }
|
138
|
+
end
|
139
|
+
|
140
|
+
# The class observed by default is inferred from the observer's class name:
|
141
|
+
# assert_equal [Person], PersonObserver.observed_class
|
142
|
+
def observed_class
|
143
|
+
name.scan(/(.*)Observer/)[0][0].constantize
|
144
|
+
end
|
111
145
|
end
|
112
146
|
|
147
|
+
# Start observing the declared classes and their subclasses.
|
113
148
|
def initialize
|
114
|
-
observed_classes
|
115
|
-
observed_subclasses_class = observed_classes.collect {|c| c.send(:subclasses) }.flatten!
|
116
|
-
(observed_classes + observed_subclasses_class).each do |klass|
|
117
|
-
klass.add_observer(self)
|
118
|
-
klass.send(:define_method, :after_find) unless klass.respond_to?(:after_find)
|
119
|
-
end
|
149
|
+
Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
|
120
150
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
151
|
+
|
152
|
+
# Send observed_method(object) if the method exists.
|
153
|
+
def update(observed_method, object) #:nodoc:
|
154
|
+
send(observed_method, object) if respond_to?(observed_method)
|
124
155
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
156
|
+
|
157
|
+
# Special method sent by the observed class when it is inherited.
|
158
|
+
# Passes the new subclass.
|
159
|
+
def observed_class_inherited(subclass) #:nodoc:
|
160
|
+
self.class.observe(observed_classes + [subclass])
|
161
|
+
add_observer!(subclass)
|
162
|
+
end
|
163
|
+
|
164
|
+
protected
|
165
|
+
def observed_classes
|
166
|
+
Set.new([self.class.observed_class].flatten)
|
167
|
+
end
|
168
|
+
|
169
|
+
def observed_subclasses
|
170
|
+
observed_classes.sum(&:subclasses)
|
133
171
|
end
|
134
|
-
|
135
|
-
def
|
136
|
-
|
172
|
+
|
173
|
+
def add_observer!(klass)
|
174
|
+
klass.add_observer(self)
|
175
|
+
klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find)
|
137
176
|
end
|
138
177
|
end
|
139
178
|
end
|
@@ -21,33 +21,46 @@ module ActiveRecord
|
|
21
21
|
reflection
|
22
22
|
end
|
23
23
|
|
24
|
+
# Returns a hash containing all AssociationReflection objects for the current class
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# Invoice.reflections
|
28
|
+
# Account.reflections
|
29
|
+
#
|
24
30
|
def reflections
|
25
|
-
read_inheritable_attribute(:reflections)
|
31
|
+
read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
|
26
32
|
end
|
27
|
-
|
33
|
+
|
28
34
|
# Returns an array of AggregateReflection objects for all the aggregations in the class.
|
29
35
|
def reflect_on_all_aggregations
|
30
36
|
reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
|
31
37
|
end
|
32
38
|
|
33
39
|
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
|
40
|
+
#
|
34
41
|
# Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
|
42
|
+
#
|
35
43
|
def reflect_on_aggregation(aggregation)
|
36
44
|
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
|
37
45
|
end
|
38
46
|
|
39
47
|
# Returns an array of AssociationReflection objects for all the aggregations in the class. If you only want to reflect on a
|
40
|
-
# certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
|
41
|
-
#
|
42
|
-
#
|
48
|
+
# certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
|
49
|
+
# Example:
|
50
|
+
#
|
51
|
+
# Account.reflect_on_all_associations # returns an array of all associations
|
52
|
+
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
53
|
+
#
|
43
54
|
def reflect_on_all_associations(macro = nil)
|
44
55
|
association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
|
45
56
|
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
|
46
57
|
end
|
47
58
|
|
48
59
|
# Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
|
60
|
+
#
|
49
61
|
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
50
62
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
63
|
+
#
|
51
64
|
def reflect_on_association(association)
|
52
65
|
reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
|
53
66
|
end
|
@@ -147,6 +160,7 @@ module ActiveRecord
|
|
147
160
|
# Gets an array of possible :through source reflection names
|
148
161
|
#
|
149
162
|
# [singularized, pluralized]
|
163
|
+
#
|
150
164
|
def source_reflection_names
|
151
165
|
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
|
152
166
|
end
|
@@ -166,7 +180,7 @@ module ActiveRecord
|
|
166
180
|
def check_validity!
|
167
181
|
if options[:through]
|
168
182
|
if through_reflection.nil?
|
169
|
-
raise HasManyThroughAssociationNotFoundError.new(self)
|
183
|
+
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
170
184
|
end
|
171
185
|
|
172
186
|
if source_reflection.nil?
|
@@ -174,7 +188,7 @@ module ActiveRecord
|
|
174
188
|
end
|
175
189
|
|
176
190
|
if source_reflection.options[:polymorphic]
|
177
|
-
raise HasManyThroughAssociationPolymorphicError.new(
|
191
|
+
raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
|
178
192
|
end
|
179
193
|
|
180
194
|
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
# This class is used to dump the database schema for some connection to some
|
3
6
|
# output format (i.e., ActiveRecord::Schema).
|
@@ -82,13 +85,27 @@ HEADER
|
|
82
85
|
tbl.print ", :force => true"
|
83
86
|
tbl.puts " do |t|"
|
84
87
|
|
85
|
-
columns.
|
88
|
+
column_specs = columns.map do |column|
|
86
89
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
87
90
|
next if column.name == pk
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
91
|
+
spec = {}
|
92
|
+
spec[:name] = column.name.inspect
|
93
|
+
spec[:type] = column.type.inspect
|
94
|
+
spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
|
95
|
+
spec[:precision] = column.precision.inspect if !column.precision.nil?
|
96
|
+
spec[:scale] = column.scale.inspect if !column.scale.nil?
|
97
|
+
spec[:null] = 'false' if !column.null
|
98
|
+
spec[:default] = default_string(column.default) if !column.default.nil?
|
99
|
+
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
|
100
|
+
spec
|
101
|
+
end.compact
|
102
|
+
keys = [:name, :type, :limit, :precision, :scale, :default, :null] & column_specs.map{ |spec| spec.keys }.inject([]){ |a,b| a | b }
|
103
|
+
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
|
104
|
+
format_string = lengths.map{ |len| "%-#{len}s" }.join("")
|
105
|
+
column_specs.each do |colspec|
|
106
|
+
values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
|
107
|
+
tbl.print " t.column "
|
108
|
+
tbl.print((format_string % values).gsub(/,\s*$/, ''))
|
92
109
|
tbl.puts
|
93
110
|
end
|
94
111
|
|
@@ -108,6 +125,17 @@ HEADER
|
|
108
125
|
stream
|
109
126
|
end
|
110
127
|
|
128
|
+
def default_string(value)
|
129
|
+
case value
|
130
|
+
when BigDecimal
|
131
|
+
value.to_s
|
132
|
+
when Date, DateTime, Time
|
133
|
+
"'" + value.to_s(:db) + "'"
|
134
|
+
else
|
135
|
+
value.inspect
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
111
139
|
def indexes(table, stream)
|
112
140
|
indexes = @connection.indexes(table)
|
113
141
|
indexes.each do |index|
|
@@ -1,26 +1,35 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# Active
|
3
|
-
#
|
4
|
-
# automatically included, so you don't need to do that manually.
|
2
|
+
# Active Record automatically timestamps create and update if the table has fields
|
3
|
+
# created_at/created_on or updated_at/updated_on.
|
5
4
|
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# Timestamping can be turned off by setting
|
6
|
+
# <tt>ActiveRecord::Base.record_timestamps = false</tt>
|
7
|
+
#
|
8
|
+
# Keep in mind that, via inheritance, you can turn off timestamps on a per
|
9
|
+
# model basis by setting <tt>record_timestamps</tt> to false in the desired
|
10
|
+
# models.
|
11
|
+
#
|
12
|
+
# class Feed < ActiveRecord::Base
|
13
|
+
# self.record_timestamps = false
|
14
|
+
# # ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Timestamps are in the local timezone by default but can use UTC by setting
|
18
|
+
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
|
8
19
|
module Timestamp
|
9
|
-
def self.
|
20
|
+
def self.included(base) #:nodoc:
|
10
21
|
super
|
11
22
|
|
12
|
-
base.
|
13
|
-
|
14
|
-
alias_method :create, :create_with_timestamps
|
23
|
+
base.alias_method_chain :create, :timestamps
|
24
|
+
base.alias_method_chain :update, :timestamps
|
15
25
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
26
|
+
base.cattr_accessor :record_timestamps
|
27
|
+
base.record_timestamps = true
|
19
28
|
end
|
20
29
|
|
21
30
|
def create_with_timestamps #:nodoc:
|
22
31
|
if record_timestamps
|
23
|
-
|
32
|
+
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
24
33
|
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
|
25
34
|
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
|
26
35
|
|
@@ -32,31 +41,11 @@ module ActiveRecord
|
|
32
41
|
|
33
42
|
def update_with_timestamps #:nodoc:
|
34
43
|
if record_timestamps
|
35
|
-
|
44
|
+
t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
|
36
45
|
write_attribute('updated_at', t) if respond_to?(:updated_at)
|
37
46
|
write_attribute('updated_on', t) if respond_to?(:updated_on)
|
38
47
|
end
|
39
48
|
update_without_timestamps
|
40
49
|
end
|
41
50
|
end
|
42
|
-
|
43
|
-
class Base
|
44
|
-
# Records the creation date and possibly time in created_on (date only) or created_at (date and time) and the update date and possibly
|
45
|
-
# time in updated_on and updated_at. This only happens if the object responds to either of these messages, which they will do automatically
|
46
|
-
# if the table has columns of either of these names. This feature is turned on by default.
|
47
|
-
@@record_timestamps = true
|
48
|
-
cattr_accessor :record_timestamps
|
49
|
-
|
50
|
-
# deprecated: use ActiveRecord::Base.default_timezone instead.
|
51
|
-
@@timestamps_gmt = false
|
52
|
-
def self.timestamps_gmt=( gmt ) #:nodoc:
|
53
|
-
warn "timestamps_gmt= is deprecated. use default_timezone= instead"
|
54
|
-
self.default_timezone = ( gmt ? :utc : :local )
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.timestamps_gmt #:nodoc:
|
58
|
-
warn "timestamps_gmt is deprecated. use default_timezone instead"
|
59
|
-
self.default_timezone == :utc
|
60
|
-
end
|
61
|
-
end
|
62
51
|
end
|
@@ -4,21 +4,16 @@ require 'thread'
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Transactions # :nodoc:
|
7
|
-
TRANSACTION_MUTEX = Mutex.new
|
8
|
-
|
9
7
|
class TransactionError < ActiveRecordError # :nodoc:
|
10
8
|
end
|
11
9
|
|
12
|
-
def self.
|
13
|
-
super
|
10
|
+
def self.included(base)
|
14
11
|
base.extend(ClassMethods)
|
15
12
|
|
16
13
|
base.class_eval do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
alias_method :save_without_transactions, :save
|
21
|
-
alias_method :save, :save_with_transactions
|
14
|
+
[:destroy, :save, :save!].each do |method|
|
15
|
+
alias_method_chain method, :transactions
|
16
|
+
end
|
22
17
|
end
|
23
18
|
end
|
24
19
|
|
@@ -60,7 +55,7 @@ module ActiveRecord
|
|
60
55
|
# will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction
|
61
56
|
# depend on or you can raise exceptions in the callbacks to rollback.
|
62
57
|
#
|
63
|
-
# == Object-level transactions
|
58
|
+
# == Object-level transactions (deprecated)
|
64
59
|
#
|
65
60
|
# You can enable object-level transactions for Active Record objects, though. You do this by naming each of the Active Records
|
66
61
|
# that you want to enable object-level transactions for, like this:
|
@@ -70,8 +65,14 @@ module ActiveRecord
|
|
70
65
|
# mary.deposit(100)
|
71
66
|
# end
|
72
67
|
#
|
73
|
-
# If the transaction fails, David and Mary will be returned to their
|
74
|
-
#
|
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.
|
75
76
|
#
|
76
77
|
# == Exception handling
|
77
78
|
#
|
@@ -82,11 +83,14 @@ module ActiveRecord
|
|
82
83
|
module ClassMethods
|
83
84
|
def transaction(*objects, &block)
|
84
85
|
previous_handler = trap('TERM') { raise TransactionError, "Transaction aborted" }
|
85
|
-
|
86
|
-
|
86
|
+
increment_open_transactions
|
87
|
+
|
87
88
|
begin
|
88
|
-
objects.
|
89
|
-
|
89
|
+
unless objects.empty?
|
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
|
90
94
|
|
91
95
|
result = connection.transaction(Thread.current['start_db_transaction'], &block)
|
92
96
|
|
@@ -96,22 +100,21 @@ module ActiveRecord
|
|
96
100
|
objects.each { |o| o.abort_transaction }
|
97
101
|
raise
|
98
102
|
ensure
|
99
|
-
|
103
|
+
decrement_open_transactions
|
100
104
|
trap('TERM', previous_handler)
|
101
105
|
end
|
102
106
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def increment_open_transactions #:nodoc:
|
110
|
+
open = Thread.current['open_transactions'] ||= 0
|
111
|
+
Thread.current['start_db_transaction'] = open.zero?
|
112
|
+
Thread.current['open_transactions'] = open + 1
|
113
|
+
end
|
114
|
+
|
115
|
+
def decrement_open_transactions #:nodoc:
|
116
|
+
Thread.current['open_transactions'] -= 1
|
117
|
+
end
|
115
118
|
end
|
116
119
|
|
117
120
|
def transaction(*objects, &block)
|
@@ -121,9 +124,13 @@ module ActiveRecord
|
|
121
124
|
def destroy_with_transactions #:nodoc:
|
122
125
|
transaction { destroy_without_transactions }
|
123
126
|
end
|
124
|
-
|
127
|
+
|
125
128
|
def save_with_transactions(perform_validation = true) #:nodoc:
|
126
129
|
transaction { save_without_transactions(perform_validation) }
|
127
130
|
end
|
131
|
+
|
132
|
+
def save_with_transactions! #:nodoc:
|
133
|
+
transaction { save_without_transactions! }
|
134
|
+
end
|
128
135
|
end
|
129
136
|
end
|