activerecord 3.2.22.5 → 4.0.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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
2
 
3
3
  module ActiveRecord
4
4
  module Inheritance
@@ -6,14 +6,29 @@ module ActiveRecord
6
6
 
7
7
  included do
8
8
  # Determine whether to store the full constant name including namespace when using STI
9
- class_attribute :store_full_sti_class
9
+ class_attribute :store_full_sti_class, instance_writer: false
10
10
  self.store_full_sti_class = true
11
11
  end
12
12
 
13
13
  module ClassMethods
14
+ # Determines if one of the attributes passed in is the inheritance column,
15
+ # and if the inheritance column is attr accessible, it initializes an
16
+ # instance of the given subclass instead of the base class
17
+ def new(*args, &block)
18
+ if (attrs = args.first).is_a?(Hash)
19
+ if subclass = subclass_from_attrs(attrs)
20
+ return subclass.new(*args, &block)
21
+ end
22
+ end
23
+ # Delegate to the original .new
24
+ super
25
+ end
26
+
14
27
  # True if this isn't a concrete subclass needing a STI type condition.
15
28
  def descends_from_active_record?
16
- if superclass.abstract_class?
29
+ if self == Base
30
+ false
31
+ elsif superclass.abstract_class?
17
32
  superclass.descends_from_active_record?
18
33
  else
19
34
  superclass == Base || !columns_hash.include?(inheritance_column)
@@ -33,17 +48,41 @@ module ActiveRecord
33
48
  @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
34
49
  end
35
50
 
36
- # Returns the base AR subclass that this class descends from. If A
37
- # extends AR::Base, A.base_class will return A. If B descends from A
51
+ # Returns the class descending directly from ActiveRecord::Base, or
52
+ # an abstract class, if any, in the inheritance hierarchy.
53
+ #
54
+ # If A extends AR::Base, A.base_class will return A. If B descends from A
38
55
  # through some arbitrarily deep hierarchy, B.base_class will return A.
39
56
  #
40
57
  # If B < A and C < B and if A is an abstract_class then both B.base_class
41
58
  # and C.base_class would return B as the answer since A is an abstract_class.
42
59
  def base_class
43
- class_of_active_record_descendant(self)
60
+ unless self < Base
61
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
62
+ end
63
+
64
+ if superclass == Base || superclass.abstract_class?
65
+ self
66
+ else
67
+ superclass.base_class
68
+ end
44
69
  end
45
70
 
46
71
  # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
72
+ # If you are using inheritance with ActiveRecord and don't want child classes
73
+ # to utilize the implied STI table name of the parent class, this will need to be true.
74
+ # For example, given the following:
75
+ #
76
+ # class SuperClass < ActiveRecord::Base
77
+ # self.abstract_class = true
78
+ # end
79
+ # class Child < SuperClass
80
+ # self.table_name = 'the_table_i_really_want'
81
+ # end
82
+ #
83
+ #
84
+ # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
85
+ #
47
86
  attr_accessor :abstract_class
48
87
 
49
88
  # Returns whether this class is an abstract class or not.
@@ -55,36 +94,8 @@ module ActiveRecord
55
94
  store_full_sti_class ? name : name.demodulize
56
95
  end
57
96
 
58
- # Finder methods must instantiate through this method to work with the
59
- # single-table inheritance model that makes it possible to create
60
- # objects of different types from the same table.
61
- def instantiate(record)
62
- sti_class = find_sti_class(record[inheritance_column])
63
- record_id = sti_class.primary_key && record[sti_class.primary_key]
64
-
65
- if ActiveRecord::IdentityMap.enabled? && record_id
66
- instance = use_identity_map(sti_class, record_id, record)
67
- else
68
- instance = sti_class.allocate.init_with('attributes' => record)
69
- end
70
-
71
- instance
72
- end
73
-
74
97
  protected
75
98
 
76
- # Returns the class descending directly from ActiveRecord::Base or an
77
- # abstract class, if any, in the inheritance hierarchy.
78
- def class_of_active_record_descendant(klass)
79
- if klass == Base || klass.superclass == Base || klass.superclass.abstract_class?
80
- klass
81
- elsif klass.superclass.nil?
82
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
83
- else
84
- class_of_active_record_descendant(klass.superclass)
85
- end
86
- end
87
-
88
99
  # Returns the class type of the record using the current module as a prefix. So descendants of
89
100
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
90
101
  def compute_type(type_name)
@@ -114,39 +125,33 @@ module ActiveRecord
114
125
 
115
126
  private
116
127
 
117
- def use_identity_map(sti_class, record_id, record)
118
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
119
- record_id = record_id.to_i
120
- end
121
-
122
- if instance = IdentityMap.get(sti_class, record_id)
123
- instance.reinit_with('attributes' => record)
128
+ # Called by +instantiate+ to decide which class to use for a new
129
+ # record instance. For single-table inheritance, we check the record
130
+ # for a +type+ column and return the corresponding class.
131
+ def discriminate_class_for_record(record)
132
+ if using_single_table_inheritance?(record)
133
+ find_sti_class(record[inheritance_column])
124
134
  else
125
- instance = sti_class.allocate.init_with('attributes' => record)
126
- IdentityMap.add(instance)
135
+ super
127
136
  end
137
+ end
128
138
 
129
- instance
139
+ def using_single_table_inheritance?(record)
140
+ record[inheritance_column].present? && columns_hash.include?(inheritance_column)
130
141
  end
131
142
 
132
143
  def find_sti_class(type_name)
133
- if type_name.blank? || !columns_hash.include?(inheritance_column)
134
- self
144
+ if store_full_sti_class
145
+ ActiveSupport::Dependencies.constantize(type_name)
135
146
  else
136
- begin
137
- if store_full_sti_class
138
- ActiveSupport::Dependencies.constantize(type_name)
139
- else
140
- compute_type(type_name)
141
- end
142
- rescue NameError
143
- raise SubclassNotFound,
144
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
145
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
146
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
147
- "or overwrite #{name}.inheritance_column to use another column for that information."
148
- end
147
+ compute_type(type_name)
149
148
  end
149
+ rescue NameError
150
+ raise SubclassNotFound,
151
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
152
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
153
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
154
+ "or overwrite #{name}.inheritance_column to use another column for that information."
150
155
  end
151
156
 
152
157
  def type_condition(table = arel_table)
@@ -155,6 +160,19 @@ module ActiveRecord
155
160
 
156
161
  sti_column.in(sti_names)
157
162
  end
163
+
164
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
165
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
166
+ # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
167
+ # this will ignore the inheritance column and return nil
168
+ def subclass_from_attrs(attrs)
169
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
170
+ return nil if subclass_name.blank? || subclass_name == self.name
171
+ unless subclass = subclasses.detect { |sub| sub.name == subclass_name }
172
+ raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
173
+ end
174
+ subclass
175
+ end
158
176
  end
159
177
 
160
178
  private
@@ -5,10 +5,12 @@ module ActiveRecord
5
5
  included do
6
6
  ##
7
7
  # :singleton-method:
8
- # Indicates the format used to generate the timestamp format in the cache key.
9
- # This is +:number+, by default.
8
+ # Indicates the format used to generate the timestamp in the cache key.
9
+ # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
10
+ #
11
+ # This is +:nsec+, by default.
10
12
  class_attribute :cache_timestamp_format, :instance_writer => false
11
- self.cache_timestamp_format = :number
13
+ self.cache_timestamp_format = :nsec
12
14
  end
13
15
 
14
16
  # Returns a String, which Action Pack uses for constructing an URL to this
@@ -40,8 +42,6 @@ module ActiveRecord
40
42
 
41
43
  # Returns a cache key that can be used to identify this record.
42
44
  #
43
- # ==== Examples
44
- #
45
45
  # Product.new.cache_key # => "products/new"
46
46
  # Product.find(5).cache_key # => "products/5" (updated_at not available)
47
47
  # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
@@ -4,12 +4,19 @@ en:
4
4
  #created_at: "Created at"
5
5
  #updated_at: "Updated at"
6
6
 
7
+ # Default error messages
8
+ errors:
9
+ messages:
10
+ taken: "has already been taken"
11
+
7
12
  # Active Record models configuration
8
13
  activerecord:
9
14
  errors:
10
15
  messages:
11
- taken: "has already been taken"
12
16
  record_invalid: "Validation failed: %{errors}"
17
+ restrict_dependent_destroy:
18
+ one: "Cannot delete record because a dependent %{record} exists"
19
+ many: "Cannot delete record because dependent %{record} exist"
13
20
  # Append your own errors here or at the model/attributes scope.
14
21
 
15
22
  # You can define own errors for models or model attributes.
@@ -40,16 +40,18 @@ module ActiveRecord
40
40
  # This locking mechanism will function inside a single Ruby process. To make it work across all
41
41
  # web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
42
42
  #
43
- # You must ensure that your database schema defaults the +lock_version+ column to 0.
44
- #
45
43
  # This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
46
- # To override the name of the +lock_version+ column, invoke the <tt>set_locking_column</tt> method.
47
- # This method uses the same syntax as <tt>set_table_name</tt>
44
+ # To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
45
+ #
46
+ # class Person < ActiveRecord::Base
47
+ # self.locking_column = :lock_person
48
+ # end
49
+ #
48
50
  module Optimistic
49
51
  extend ActiveSupport::Concern
50
52
 
51
53
  included do
52
- cattr_accessor :lock_optimistically, :instance_writer => false
54
+ class_attribute :lock_optimistically, instance_writer: false
53
55
  self.lock_optimistically = true
54
56
  end
55
57
 
@@ -64,7 +66,7 @@ module ActiveRecord
64
66
  send(lock_col + '=', previous_lock_value + 1)
65
67
  end
66
68
 
67
- def update(attribute_names = @attributes.keys) #:nodoc:
69
+ def update_record(attribute_names = @attributes.keys) #:nodoc:
68
70
  return super unless locking_enabled?
69
71
  return 0 if attribute_names.empty?
70
72
 
@@ -80,9 +82,9 @@ module ActiveRecord
80
82
 
81
83
  stmt = relation.where(
82
84
  relation.table[self.class.primary_key].eq(id).and(
83
- relation.table[lock_col].eq(quote_value(previous_lock_value, self.class.columns_hash[lock_col]))
85
+ relation.table[lock_col].eq(self.class.quote_value(previous_lock_value))
84
86
  )
85
- ).arel.compile_update(arel_attributes_values(false, false, attribute_names))
87
+ ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
86
88
 
87
89
  affected_rows = connection.update stmt
88
90
 
@@ -99,26 +101,29 @@ module ActiveRecord
99
101
  end
100
102
  end
101
103
 
102
- def destroy #:nodoc:
103
- return super unless locking_enabled?
104
+ def destroy_row
105
+ affected_rows = super
106
+
107
+ if locking_enabled? && affected_rows != 1
108
+ raise ActiveRecord::StaleObjectError.new(self, "destroy")
109
+ end
104
110
 
105
- destroy_associations
111
+ affected_rows
112
+ end
106
113
 
107
- if persisted?
108
- table = self.class.arel_table
109
- lock_col = self.class.locking_column
110
- predicate = table[self.class.primary_key].eq(id).
111
- and(table[lock_col].eq(send(lock_col).to_i))
114
+ def relation_for_destroy
115
+ relation = super
112
116
 
113
- affected_rows = self.class.unscoped.where(predicate).delete_all
117
+ if locking_enabled?
118
+ column_name = self.class.locking_column
119
+ column = self.class.columns_hash[column_name]
120
+ substitute = connection.substitute_at(column, relation.bind_values.length)
114
121
 
115
- unless affected_rows == 1
116
- raise ActiveRecord::StaleObjectError.new(self, "destroy")
117
- end
122
+ relation = relation.where(self.class.arel_table[column_name].eq(substitute))
123
+ relation.bind_values << [column, self[column_name].to_i]
118
124
  end
119
125
 
120
- @destroyed = true
121
- freeze
126
+ relation
122
127
  end
123
128
 
124
129
  module ClassMethods
@@ -131,14 +136,9 @@ module ActiveRecord
131
136
  lock_optimistically && columns_hash[locking_column]
132
137
  end
133
138
 
134
- def locking_column=(value)
135
- @original_locking_column = @locking_column if defined?(@locking_column)
136
- @locking_column = value.to_s
137
- end
138
-
139
139
  # Set the column to use for optimistic locking. Defaults to +lock_version+.
140
- def set_locking_column(value = nil, &block)
141
- deprecated_property_setter :locking_column, value, block
140
+ def locking_column=(value)
141
+ @locking_column = value.to_s
142
142
  end
143
143
 
144
144
  # The version column used for optimistic locking. Defaults to +lock_version+.
@@ -147,10 +147,6 @@ module ActiveRecord
147
147
  @locking_column
148
148
  end
149
149
 
150
- def original_locking_column #:nodoc:
151
- deprecated_original_property_getter :locking_column
152
- end
153
-
154
150
  # Quote the column name used for optimistic locking.
155
151
  def quoted_locking_column
156
152
  connection.quote_column_name(locking_column)
@@ -168,16 +164,16 @@ module ActiveRecord
168
164
  super
169
165
  end
170
166
 
171
- # If the locking column has no default value set,
172
- # start the lock version at zero. Note we can't use
173
- # <tt>locking_enabled?</tt> at this point as
174
- # <tt>@attributes</tt> may not have been initialized yet.
175
- def initialize_attributes(attributes, options = {}) #:nodoc:
176
- if attributes.key?(locking_column) && lock_optimistically
177
- attributes[locking_column] ||= 0
178
- end
167
+ def column_defaults
168
+ @column_defaults ||= begin
169
+ defaults = super
170
+
171
+ if defaults.key?(locking_column) && lock_optimistically
172
+ defaults[locking_column] ||= 0
173
+ end
179
174
 
180
- attributes
175
+ defaults
176
+ end
181
177
  end
182
178
  end
183
179
  end
@@ -3,12 +3,12 @@ module ActiveRecord
3
3
  # Locking::Pessimistic provides support for row-level locking using
4
4
  # SELECT ... FOR UPDATE and other lock types.
5
5
  #
6
- # Pass <tt>:lock => true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
6
+ # Pass <tt>lock: true</tt> to <tt>ActiveRecord::Base.find</tt> to obtain an exclusive
7
7
  # lock on the selected rows:
8
8
  # # select * from accounts where id=1 for update
9
- # Account.find(1, :lock => true)
9
+ # Account.find(1, lock: true)
10
10
  #
11
- # Pass <tt>:lock => 'some locking clause'</tt> to give a database-specific locking clause
11
+ # Pass <tt>lock: 'some locking clause'</tt> to give a database-specific locking clause
12
12
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
13
  #
14
14
  # Account.transaction do
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  #
27
27
  # Account.transaction do
28
28
  # # select * from accounts where ...
29
- # accounts = Account.where(...).all
29
+ # accounts = Account.where(...)
30
30
  # account1 = accounts.detect { |account| ... }
31
31
  # account2 = accounts.detect { |account| ... }
32
32
  # # select * from accounts where id=? for update
@@ -1,11 +1,13 @@
1
1
  module ActiveRecord
2
2
  class LogSubscriber < ActiveSupport::LogSubscriber
3
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
4
+
3
5
  def self.runtime=(value)
4
- Thread.current["active_record_sql_runtime"] = value
6
+ Thread.current[:active_record_sql_runtime] = value
5
7
  end
6
8
 
7
9
  def self.runtime
8
- Thread.current["active_record_sql_runtime"] ||= 0
10
+ Thread.current[:active_record_sql_runtime] ||= 0
9
11
  end
10
12
 
11
13
  def self.reset_runtime
@@ -18,25 +20,33 @@ module ActiveRecord
18
20
  @odd_or_even = false
19
21
  end
20
22
 
23
+ def render_bind(column, value)
24
+ if column
25
+ if column.binary?
26
+ value = "<#{value.bytesize} bytes of binary data>"
27
+ end
28
+
29
+ [column.name, value]
30
+ else
31
+ [nil, value]
32
+ end
33
+ end
34
+
21
35
  def sql(event)
22
36
  self.class.runtime += event.duration
23
37
  return unless logger.debug?
24
38
 
25
39
  payload = event.payload
26
40
 
27
- return if 'SCHEMA' == payload[:name]
41
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
28
42
 
29
- name = '%s (%.1fms)' % [payload[:name], event.duration]
43
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
30
44
  sql = payload[:sql].squeeze(' ')
31
45
  binds = nil
32
46
 
33
47
  unless (payload[:binds] || []).empty?
34
48
  binds = " " + payload[:binds].map { |col,v|
35
- if col
36
- [col.name, v]
37
- else
38
- [nil, v]
39
- end
49
+ render_bind(col, v)
40
50
  }.inspect
41
51
  end
42
52