activerecord 3.0.20 → 3.1.0.beta1

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 (122) hide show
  1. data/CHANGELOG +220 -91
  2. data/README.rdoc +3 -3
  3. data/examples/performance.rb +88 -109
  4. data/lib/active_record.rb +6 -2
  5. data/lib/active_record/aggregations.rb +22 -45
  6. data/lib/active_record/associations.rb +264 -991
  7. data/lib/active_record/associations/alias_tracker.rb +85 -0
  8. data/lib/active_record/associations/association.rb +231 -0
  9. data/lib/active_record/associations/association_scope.rb +120 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +40 -60
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
  12. data/lib/active_record/associations/builder/association.rb +53 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
  16. data/lib/active_record/associations/builder/has_many.rb +65 -0
  17. data/lib/active_record/associations/builder/has_one.rb +63 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +524 -0
  20. data/lib/active_record/associations/collection_proxy.rb +125 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
  22. data/lib/active_record/associations/has_many_association.rb +50 -79
  23. data/lib/active_record/associations/has_many_through_association.rb +98 -67
  24. data/lib/active_record/associations/has_one_association.rb +45 -115
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency.rb +215 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +150 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_helper.rb +56 -0
  31. data/lib/active_record/associations/preloader.rb +177 -0
  32. data/lib/active_record/associations/preloader/association.rb +126 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/singular_association.rb +55 -0
  43. data/lib/active_record/associations/through_association.rb +80 -0
  44. data/lib/active_record/attribute_methods.rb +19 -5
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
  46. data/lib/active_record/attribute_methods/dirty.rb +8 -2
  47. data/lib/active_record/attribute_methods/primary_key.rb +33 -13
  48. data/lib/active_record/attribute_methods/read.rb +17 -17
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
  50. data/lib/active_record/attribute_methods/write.rb +2 -1
  51. data/lib/active_record/autosave_association.rb +66 -45
  52. data/lib/active_record/base.rb +445 -273
  53. data/lib/active_record/callbacks.rb +24 -33
  54. data/lib/active_record/coders/yaml_column.rb +41 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
  64. data/lib/active_record/connection_adapters/column.rb +268 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
  68. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
  69. data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
  70. data/lib/active_record/counter_cache.rb +7 -4
  71. data/lib/active_record/fixtures.rb +174 -192
  72. data/lib/active_record/identity_map.rb +131 -0
  73. data/lib/active_record/locking/optimistic.rb +20 -14
  74. data/lib/active_record/locking/pessimistic.rb +4 -4
  75. data/lib/active_record/log_subscriber.rb +24 -4
  76. data/lib/active_record/migration.rb +265 -144
  77. data/lib/active_record/migration/command_recorder.rb +103 -0
  78. data/lib/active_record/named_scope.rb +68 -25
  79. data/lib/active_record/nested_attributes.rb +58 -15
  80. data/lib/active_record/observer.rb +3 -7
  81. data/lib/active_record/persistence.rb +58 -38
  82. data/lib/active_record/query_cache.rb +25 -3
  83. data/lib/active_record/railtie.rb +21 -12
  84. data/lib/active_record/railties/console_sandbox.rb +6 -0
  85. data/lib/active_record/railties/databases.rake +147 -116
  86. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  87. data/lib/active_record/reflection.rb +176 -44
  88. data/lib/active_record/relation.rb +125 -49
  89. data/lib/active_record/relation/batches.rb +7 -5
  90. data/lib/active_record/relation/calculations.rb +50 -18
  91. data/lib/active_record/relation/finder_methods.rb +47 -26
  92. data/lib/active_record/relation/predicate_builder.rb +24 -21
  93. data/lib/active_record/relation/query_methods.rb +117 -101
  94. data/lib/active_record/relation/spawn_methods.rb +27 -20
  95. data/lib/active_record/result.rb +34 -0
  96. data/lib/active_record/schema.rb +5 -6
  97. data/lib/active_record/schema_dumper.rb +11 -13
  98. data/lib/active_record/serialization.rb +2 -2
  99. data/lib/active_record/serializers/xml_serializer.rb +10 -10
  100. data/lib/active_record/session_store.rb +8 -2
  101. data/lib/active_record/test_case.rb +9 -20
  102. data/lib/active_record/timestamp.rb +21 -9
  103. data/lib/active_record/transactions.rb +16 -15
  104. data/lib/active_record/validations.rb +21 -22
  105. data/lib/active_record/validations/associated.rb +3 -1
  106. data/lib/active_record/validations/uniqueness.rb +48 -58
  107. data/lib/active_record/version.rb +3 -3
  108. data/lib/rails/generators/active_record.rb +6 -0
  109. data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
  110. data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
  111. data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
  112. data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
  113. data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
  114. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  115. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
  116. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
  117. metadata +106 -77
  118. checksums.yaml +0 -7
  119. data/lib/active_record/association_preload.rb +0 -431
  120. data/lib/active_record/associations/association_collection.rb +0 -572
  121. data/lib/active_record/associations/association_proxy.rb +0 -304
  122. data/lib/active_record/associations/through_association_scope.rb +0 -160
@@ -3,10 +3,13 @@ require 'active_support/core_ext/object/blank'
3
3
  module ActiveRecord
4
4
  module SpawnMethods
5
5
  def merge(r)
6
- merged_relation = clone
7
- return merged_relation unless r
6
+ return self unless r
8
7
  return to_a & r if r.is_a?(Array)
9
8
 
9
+ merged_relation = clone
10
+
11
+ r = r.with_default_scope if r.default_scoped? && r.klass != klass
12
+
10
13
  Relation::ASSOCIATION_METHODS.each do |method|
11
14
  value = r.send(:"#{method}_values")
12
15
 
@@ -19,21 +22,12 @@ module ActiveRecord
19
22
  end
20
23
  end
21
24
 
22
- (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
25
+ (Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
23
26
  value = r.send(:"#{method}_values")
24
27
  merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
25
28
  end
26
29
 
27
- order_value = r.order_values
28
- if order_value.present?
29
- if r.reorder_flag
30
- merged_relation.order_values = order_value
31
- else
32
- merged_relation.order_values = merged_relation.order_values + order_value
33
- end
34
- end
35
-
36
- merged_relation = merged_relation.joins(r.joins_values)
30
+ merged_relation.joins_values += r.joins_values
37
31
 
38
32
  merged_wheres = @where_values + r.where_values
39
33
 
@@ -54,26 +48,31 @@ module ActiveRecord
54
48
 
55
49
  merged_relation.where_values = merged_wheres
56
50
 
57
- Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method|
51
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :create_with]).each do |method|
58
52
  value = r.send(:"#{method}_value")
59
53
  merged_relation.send(:"#{method}_value=", value) unless value.nil?
60
54
  end
61
55
 
62
56
  merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
63
57
 
58
+ merged_relation = merged_relation.create_with(r.create_with_value) if r.create_with_value
59
+
64
60
  # Apply scope extension modules
65
61
  merged_relation.send :apply_modules, r.extensions
66
62
 
67
63
  merged_relation
68
64
  end
69
65
 
70
- def &(r)
71
- ActiveSupport::Deprecation.warn "Using & to merge relations has been deprecated and will be removed in Rails 3.1. Please use the relation's merge method, instead"
72
- merge(r)
73
- end
74
-
66
+ # Removes from the query the condition(s) specified in +skips+.
67
+ #
68
+ # Example:
69
+ #
70
+ # Post.order('id asc').except(:order) # discards the order condition
71
+ # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
72
+ #
75
73
  def except(*skips)
76
74
  result = self.class.new(@klass, table)
75
+ result.default_scoped = default_scoped
77
76
 
78
77
  ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
79
78
  result.send(:"#{method}_values=", send(:"#{method}_values"))
@@ -89,8 +88,16 @@ module ActiveRecord
89
88
  result
90
89
  end
91
90
 
91
+ # Removes any condition from the query other than the one(s) specified in +onlies+.
92
+ #
93
+ # Example:
94
+ #
95
+ # Post.order('id asc').only(:where) # discards the order condition
96
+ # Post.order('id asc').only(:where, :order) # uses the specified order
97
+ #
92
98
  def only(*onlies)
93
99
  result = self.class.new(@klass, table)
100
+ result.default_scoped = default_scoped
94
101
 
95
102
  ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
96
103
  result.send(:"#{method}_values=", send(:"#{method}_values"))
@@ -115,7 +122,7 @@ module ActiveRecord
115
122
 
116
123
  options.assert_valid_keys(VALID_FIND_OPTIONS)
117
124
  finders = options.dup
118
- finders.delete_if { |key, value| value.nil? }
125
+ finders.delete_if { |key, value| value.nil? && key != :limit }
119
126
 
120
127
  ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder|
121
128
  relation = relation.send(finder, finders[finder])
@@ -0,0 +1,34 @@
1
+ module ActiveRecord
2
+ ###
3
+ # This class encapsulates a Result returned from calling +exec_query+ on any
4
+ # database connection adapter. For example:
5
+ #
6
+ # x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
7
+ # x # => #<ActiveRecord::Result:0xdeadbeef>
8
+ class Result
9
+ include Enumerable
10
+
11
+ attr_reader :columns, :rows
12
+
13
+ def initialize(columns, rows)
14
+ @columns = columns
15
+ @rows = rows
16
+ @hash_rows = nil
17
+ end
18
+
19
+ def each
20
+ hash_rows.each { |row| yield row }
21
+ end
22
+
23
+ def to_hash
24
+ hash_rows
25
+ end
26
+
27
+ private
28
+ def hash_rows
29
+ @hash_rows ||= @rows.map { |row|
30
+ Hash[@columns.zip(row)]
31
+ }
32
+ end
33
+ end
34
+ end
@@ -30,10 +30,8 @@ module ActiveRecord
30
30
  # ActiveRecord::Schema is only supported by database adapters that also
31
31
  # support migrations, the two features being very similar.
32
32
  class Schema < Migration
33
- private_class_method :new
34
-
35
- def self.migrations_path
36
- ActiveRecord::Migrator.migrations_path
33
+ def migrations_paths
34
+ ActiveRecord::Migrator.migrations_paths
37
35
  end
38
36
 
39
37
  # Eval the given block. All methods available to the current connection
@@ -48,11 +46,12 @@ module ActiveRecord
48
46
  # ...
49
47
  # end
50
48
  def self.define(info={}, &block)
51
- instance_eval(&block)
49
+ schema = new
50
+ schema.instance_eval(&block)
52
51
 
53
52
  unless info[:version].blank?
54
53
  initialize_schema_migrations_table
55
- assume_migrated_upto_version(info[:version], migrations_path)
54
+ assume_migrated_upto_version(info[:version], schema.migrations_paths)
56
55
  end
57
56
  end
58
57
  end
@@ -40,10 +40,6 @@ module ActiveRecord
40
40
  def header(stream)
41
41
  define_params = @version ? ":version => #{@version}" : ""
42
42
 
43
- if stream.respond_to?(:external_encoding) && stream.external_encoding
44
- stream.puts "# encoding: #{stream.external_encoding.name}"
45
- end
46
-
47
43
  stream.puts <<HEADER
48
44
  # This file is auto-generated from the current state of the database. Instead
49
45
  # of editing this file, please use the migrations feature of Active Record to
@@ -87,7 +83,7 @@ HEADER
87
83
 
88
84
  # first dump primary key column
89
85
  if @connection.respond_to?(:pk_and_sequence_for)
90
- pk, pk_seq = @connection.pk_and_sequence_for(table)
86
+ pk, _ = @connection.pk_and_sequence_for(table)
91
87
  elsif @connection.respond_to?(:primary_key)
92
88
  pk = @connection.primary_key(table)
93
89
  end
@@ -118,9 +114,9 @@ HEADER
118
114
  column.type.to_s
119
115
  end
120
116
  spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
121
- spec[:precision] = column.precision.inspect if !column.precision.nil?
122
- spec[:scale] = column.scale.inspect if !column.scale.nil?
123
- spec[:null] = 'false' if !column.null
117
+ spec[:precision] = column.precision.inspect if column.precision
118
+ spec[:scale] = column.scale.inspect if column.scale
119
+ spec[:null] = 'false' unless column.null
124
120
  spec[:default] = default_string(column.default) if column.has_default?
125
121
  (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
126
122
  spec
@@ -180,13 +176,15 @@ HEADER
180
176
  def indexes(table, stream)
181
177
  if (indexes = @connection.indexes(table)).any?
182
178
  add_index_statements = indexes.map do |index|
183
- statement_parts = [ ('add_index ' + index.table.inspect) ]
184
- statement_parts << index.columns.inspect
185
- statement_parts << (':name => ' + index.name.inspect)
179
+ statement_parts = [
180
+ ('add_index ' + index.table.inspect),
181
+ index.columns.inspect,
182
+ (':name => ' + index.name.inspect),
183
+ ]
186
184
  statement_parts << ':unique => true' if index.unique
187
185
 
188
- index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
189
- statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
186
+ index_lengths = (index.lengths || []).compact
187
+ statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
190
188
 
191
189
  ' ' + statement_parts.join(', ')
192
190
  end
@@ -22,7 +22,7 @@ module ActiveRecord #:nodoc:
22
22
  end
23
23
 
24
24
  private
25
- # Add associations specified via the <tt>:includes</tt> option.
25
+ # Add associations specified via the <tt>:include</tt> option.
26
26
  #
27
27
  # Expects a block that takes as arguments:
28
28
  # +association+ - name of the association
@@ -45,7 +45,7 @@ module ActiveRecord #:nodoc:
45
45
  send(association)
46
46
  end
47
47
 
48
- unless records.nil?
48
+ if records
49
49
  association_options = include_has_options ? include_associations[association] : base_only_or_except
50
50
  opts = options.merge(association_options)
51
51
  yield(association, records, opts)
@@ -226,17 +226,17 @@ module ActiveRecord #:nodoc:
226
226
 
227
227
  class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
228
228
  def compute_type
229
- type = @serializable.class.serialized_attributes.has_key?(name) ?
230
- super : @serializable.class.columns_hash[name].type
229
+ klass = @serializable.class
230
+ type = if klass.serialized_attributes.key?(name)
231
+ super
232
+ elsif klass.columns_hash.key?(name)
233
+ klass.columns_hash[name].type
234
+ else
235
+ NilClass
236
+ end
231
237
 
232
- case type
233
- when :text
234
- :string
235
- when :time
236
- :datetime
237
- else
238
- type
239
- end
238
+ { :text => :string,
239
+ :time => :datetime }[type] || type
240
240
  end
241
241
  protected :compute_type
242
242
  end
@@ -59,10 +59,12 @@ module ActiveRecord
59
59
  end
60
60
 
61
61
  def drop_table!
62
+ connection_pool.clear_table_cache!(table_name)
62
63
  connection.drop_table table_name
63
64
  end
64
65
 
65
66
  def create_table!
67
+ connection_pool.clear_table_cache!(table_name)
66
68
  connection.create_table(table_name) do |t|
67
69
  t.string session_id_column, :limit => 255
68
70
  t.text data_column_name
@@ -288,6 +290,7 @@ module ActiveRecord
288
290
  self.session_class = Session
289
291
 
290
292
  SESSION_RECORD_KEY = 'rack.session.record'
293
+ ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
291
294
 
292
295
  private
293
296
  def get_session(env, sid)
@@ -299,7 +302,7 @@ module ActiveRecord
299
302
  end
300
303
  end
301
304
 
302
- def set_session(env, sid, session_data)
305
+ def set_session(env, sid, session_data, options)
303
306
  Base.silence do
304
307
  record = get_session_model(env, sid)
305
308
  record.data = session_data
@@ -316,12 +319,15 @@ module ActiveRecord
316
319
  sid
317
320
  end
318
321
 
319
- def destroy(env)
322
+ def destroy_session(env, session_id, options)
320
323
  if sid = current_session_id(env)
321
324
  Base.silence do
322
325
  get_session_model(env, sid).destroy
326
+ env[SESSION_RECORD_KEY] = nil
323
327
  end
324
328
  end
329
+
330
+ generate_sid unless options[:drop]
325
331
  end
326
332
 
327
333
  def get_session_model(env, sid)
@@ -3,11 +3,14 @@ module ActiveRecord
3
3
  #
4
4
  # Defines some test assertions to test against SQL queries.
5
5
  class TestCase < ActiveSupport::TestCase #:nodoc:
6
- # Backport skip to Ruby 1.8. test/unit doesn't support it, so just
7
- # make it a noop.
8
- unless instance_methods.map(&:to_s).include?("skip")
9
- def skip(message)
10
- end
6
+ setup :cleanup_identity_map
7
+
8
+ def setup
9
+ cleanup_identity_map
10
+ end
11
+
12
+ def cleanup_identity_map
13
+ ActiveRecord::IdentityMap.clear
11
14
  end
12
15
 
13
16
  def assert_date_from_db(expected, actual, message = nil)
@@ -23,6 +26,7 @@ module ActiveRecord
23
26
  def assert_sql(*patterns_to_match)
24
27
  $queries_executed = []
25
28
  yield
29
+ $queries_executed
26
30
  ensure
27
31
  failed_patterns = []
28
32
  patterns_to_match.each do |pattern|
@@ -43,21 +47,6 @@ module ActiveRecord
43
47
  assert_queries(0, &block)
44
48
  end
45
49
 
46
- def self.use_concurrent_connections
47
- setup :connection_allow_concurrency_setup
48
- teardown :connection_allow_concurrency_teardown
49
- end
50
-
51
- def connection_allow_concurrency_setup
52
- @connection = ActiveRecord::Base.remove_connection
53
- ActiveRecord::Base.establish_connection(@connection.merge({:allow_concurrency => true}))
54
- end
55
-
56
- def connection_allow_concurrency_teardown
57
- ActiveRecord::Base.clear_all_connections!
58
- ActiveRecord::Base.establish_connection(@connection)
59
- end
60
-
61
50
  def with_kcode(kcode)
62
51
  if RUBY_VERSION < '1.9'
63
52
  orig_kcode, $KCODE = $KCODE, kcode
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Timestamp
3
5
  #
@@ -7,36 +9,38 @@ module ActiveRecord
7
9
  #
8
10
  # Timestamping can be turned off by setting:
9
11
  #
10
- # <tt>ActiveRecord::Base.record_timestamps = false</tt>
12
+ # config.active_record.record_timestamps = false
11
13
  #
12
14
  # Timestamps are in the local timezone by default but you can use UTC by setting:
13
15
  #
14
- # <tt>ActiveRecord::Base.default_timezone = :utc</tt>
16
+ # config.active_record.default_timezone = :utc
15
17
  #
16
18
  # == Time Zone aware attributes
17
19
  #
18
20
  # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
19
21
  #
20
- # ActiveRecord::Base.time_zone_aware_attributes = true
22
+ # config.active_record.time_zone_aware_attributes = true
21
23
  #
22
24
  # This feature can easily be turned off by assigning value <tt>false</tt> .
23
25
  #
24
- # If your attributes are time zone aware and you desire to skip time zone conversion for certain
25
- # attributes then you can do following:
26
+ # If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
27
+ # when reading certain attributes then you can do following:
26
28
  #
27
- # Topic.skip_time_zone_conversion_for_attributes = [:written_on]
29
+ # class Topic < ActiveRecord::Base
30
+ # self.skip_time_zone_conversion_for_attributes = [:written_on]
31
+ # end
28
32
  module Timestamp
29
33
  extend ActiveSupport::Concern
30
34
 
31
35
  included do
32
- class_inheritable_accessor :record_timestamps, :instance_writer => false
36
+ class_attribute :record_timestamps, :instance_writer => false
33
37
  self.record_timestamps = true
34
38
  end
35
39
 
36
40
  private
37
41
 
38
42
  def create #:nodoc:
39
- if record_timestamps
43
+ if self.record_timestamps
40
44
  current_time = current_time_from_proper_timezone
41
45
 
42
46
  all_timestamp_attributes.each do |column|
@@ -61,13 +65,21 @@ module ActiveRecord
61
65
  end
62
66
 
63
67
  def should_record_timestamps?
64
- record_timestamps && (!partial_updates? || changed?)
68
+ self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
69
+ end
70
+
71
+ def timestamp_attributes_for_create_in_model
72
+ timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c.to_s) }
65
73
  end
66
74
 
67
75
  def timestamp_attributes_for_update_in_model
68
76
  timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
69
77
  end
70
78
 
79
+ def all_timestamp_attributes_in_model
80
+ timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
81
+ end
82
+
71
83
  def timestamp_attributes_for_update #:nodoc:
72
84
  [:updated_at, :updated_on]
73
85
  end
@@ -11,6 +11,7 @@ module ActiveRecord
11
11
  included do
12
12
  define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
13
13
  end
14
+
14
15
  # = Active Record Transactions
15
16
  #
16
17
  # Transactions are protective blocks where SQL statements are only permanent
@@ -227,8 +228,8 @@ module ActiveRecord
227
228
  end
228
229
 
229
230
  # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
230
- def transaction(&block)
231
- self.class.transaction(&block)
231
+ def transaction(options = {}, &block)
232
+ self.class.transaction(options, &block)
232
233
  end
233
234
 
234
235
  def destroy #:nodoc:
@@ -250,6 +251,7 @@ module ActiveRecord
250
251
  remember_transaction_record_state
251
252
  yield
252
253
  rescue Exception
254
+ IdentityMap.remove(self) if IdentityMap.enabled?
253
255
  restore_transaction_record_state
254
256
  raise
255
257
  ensure
@@ -258,7 +260,7 @@ module ActiveRecord
258
260
 
259
261
  # Call the after_commit callbacks
260
262
  def committed! #:nodoc:
261
- _run_commit_callbacks
263
+ run_callbacks :commit
262
264
  ensure
263
265
  clear_transaction_record_state
264
266
  end
@@ -266,8 +268,9 @@ module ActiveRecord
266
268
  # Call the after rollback callbacks. The restore_state argument indicates if the record
267
269
  # state should be rolled back to the beginning or just to the last savepoint.
268
270
  def rolledback!(force_restore_state = false) #:nodoc:
269
- _run_rollback_callbacks
271
+ run_callbacks :rollback
270
272
  ensure
273
+ IdentityMap.remove(self) if IdentityMap.enabled?
271
274
  restore_transaction_record_state(force_restore_state)
272
275
  end
273
276
 
@@ -300,8 +303,8 @@ module ActiveRecord
300
303
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
301
304
  def remember_transaction_record_state #:nodoc
302
305
  @_start_transaction_state ||= {}
306
+ @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
303
307
  unless @_start_transaction_state.include?(:new_record)
304
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
305
308
  @_start_transaction_state[:new_record] = @new_record
306
309
  end
307
310
  unless @_start_transaction_state.include?(:destroyed)
@@ -324,16 +327,14 @@ module ActiveRecord
324
327
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
325
328
  if @_start_transaction_state[:level] < 1
326
329
  restore_state = remove_instance_variable(:@_start_transaction_state)
327
- if restore_state
328
- @attributes = @attributes.dup if @attributes.frozen?
329
- @new_record = restore_state[:new_record]
330
- @destroyed = restore_state[:destroyed]
331
- if restore_state[:id]
332
- self.id = restore_state[:id]
333
- else
334
- @attributes.delete(self.class.primary_key)
335
- @attributes_cache.delete(self.class.primary_key)
336
- end
330
+ @attributes = @attributes.dup if @attributes.frozen?
331
+ @new_record = restore_state[:new_record]
332
+ @destroyed = restore_state[:destroyed]
333
+ if restore_state.has_key?(:id)
334
+ self.id = restore_state[:id]
335
+ else
336
+ @attributes.delete(self.class.primary_key)
337
+ @attributes_cache.delete(self.class.primary_key)
337
338
  end
338
339
  end
339
340
  end