activerecord 4.1.15 → 4.2.11.3

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # ==== Parameters
12
12
  #
13
13
  # * +id+ - The id of the object you wish to reset a counter on.
14
- # * +counters+ - One or more association counters to reset
14
+ # * +counters+ - One or more association counters to reset. Association name or counter name can be given.
15
15
  #
16
16
  # ==== Examples
17
17
  #
@@ -19,9 +19,14 @@ module ActiveRecord
19
19
  # Post.reset_counters(1, :comments)
20
20
  def reset_counters(id, *counters)
21
21
  object = find(id)
22
- counters.each do |association|
23
- has_many_association = _reflect_on_association(association.to_sym)
24
- raise ArgumentError, "'#{self.name}' has no association called '#{association}'" unless has_many_association
22
+ counters.each do |counter_association|
23
+ has_many_association = _reflect_on_association(counter_association)
24
+ unless has_many_association
25
+ has_many = reflect_on_all_associations(:has_many)
26
+ has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
27
+ counter_association = has_many_association.plural_name if has_many_association
28
+ end
29
+ raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
25
30
 
26
31
  if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
27
32
  has_many_association = has_many_association.through_reflection
@@ -29,11 +34,11 @@ module ActiveRecord
29
34
 
30
35
  foreign_key = has_many_association.foreign_key.to_s
31
36
  child_class = has_many_association.klass
32
- reflection = child_class._reflections.values.find { |e| :belongs_to == e.macro && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
37
+ reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
33
38
  counter_name = reflection.counter_cache_column
34
39
 
35
40
  stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
36
- arel_table[counter_name] => object.send(association).count(:all)
41
+ arel_table[counter_name] => object.send(counter_association).count(:all)
37
42
  }, primary_key)
38
43
  connection.update stmt
39
44
  end
@@ -117,5 +122,54 @@ module ActiveRecord
117
122
  update_counters(id, counter_name => -1)
118
123
  end
119
124
  end
125
+
126
+ protected
127
+
128
+ def actually_destroyed?
129
+ @_actually_destroyed
130
+ end
131
+
132
+ def clear_destroy_state
133
+ @_actually_destroyed = nil
134
+ end
135
+
136
+ private
137
+
138
+ def _create_record(*)
139
+ id = super
140
+
141
+ each_counter_cached_associations do |association|
142
+ if send(association.reflection.name)
143
+ association.increment_counters
144
+ @_after_create_counter_called = true
145
+ end
146
+ end
147
+
148
+ id
149
+ end
150
+
151
+ def destroy_row
152
+ affected_rows = super
153
+
154
+ if affected_rows > 0
155
+ each_counter_cached_associations do |association|
156
+ foreign_key = association.reflection.foreign_key.to_sym
157
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
158
+ if send(association.reflection.name)
159
+ association.decrement_counters
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ affected_rows
166
+ end
167
+
168
+ def each_counter_cached_associations
169
+ _reflections.each do |name, reflection|
170
+ yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
171
+ end
172
+ end
173
+
120
174
  end
121
175
  end
@@ -18,17 +18,18 @@ module ActiveRecord
18
18
  # conversation.archived? # => true
19
19
  # conversation.status # => "archived"
20
20
  #
21
- # # conversation.update! status: 1
21
+ # # conversation.status = 1
22
22
  # conversation.status = "archived"
23
23
  #
24
- # # conversation.update! status: nil
25
24
  # conversation.status = nil
26
25
  # conversation.status.nil? # => true
27
26
  # conversation.status # => nil
28
27
  #
29
28
  # Scopes based on the allowed values of the enum field will be provided
30
- # as well. With the above example, it will create an +active+ and +archived+
31
- # scope.
29
+ # as well. With the above example:
30
+ #
31
+ # Conversation.active
32
+ # Conversation.archived
32
33
  #
33
34
  # You can set the default value from the database declaration, like:
34
35
  #
@@ -138,19 +139,16 @@ module ActiveRecord
138
139
  @_enum_methods_module ||= begin
139
140
  mod = Module.new do
140
141
  private
141
- def save_changed_attribute(attr_name, value)
142
+ def save_changed_attribute(attr_name, old)
142
143
  if (mapping = self.class.defined_enums[attr_name.to_s])
144
+ value = _read_attribute(attr_name)
143
145
  if attribute_changed?(attr_name)
144
- old = changed_attributes[attr_name]
145
-
146
146
  if mapping[old] == value
147
- changed_attributes.delete(attr_name)
147
+ clear_attribute_changes([attr_name])
148
148
  end
149
149
  else
150
- old = clone_attribute_value(:read_attribute, attr_name)
151
-
152
150
  if old != value
153
- changed_attributes[attr_name] = mapping.key old
151
+ set_attribute_was(attr_name, mapping.key(old))
154
152
  end
155
153
  end
156
154
  else
@@ -30,17 +30,18 @@ module ActiveRecord
30
30
  class SerializationTypeMismatch < ActiveRecordError
31
31
  end
32
32
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
33
+ # Raised when adapter not specified on connection (or configuration file
34
+ # +config/database.yml+ misses adapter field).
35
35
  class AdapterNotSpecified < ActiveRecordError
36
36
  end
37
37
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
38
+ # Raised when Active Record cannot find database adapter specified in
39
+ # +config/database.yml+ or programmatically.
39
40
  class AdapterNotFound < ActiveRecordError
40
41
  end
41
42
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
43
- # is given a nil object).
43
+ # Raised when connection to the database could not been established (for
44
+ # example when +connection=+ is given a nil object).
44
45
  class ConnectionNotEstablished < ActiveRecordError
45
46
  end
46
47
 
@@ -51,10 +52,29 @@ module ActiveRecord
51
52
  # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
52
53
  # saved because record is invalid.
53
54
  class RecordNotSaved < ActiveRecordError
55
+ attr_reader :record
56
+
57
+ def initialize(message, record = nil)
58
+ @record = record
59
+ super(message)
60
+ end
54
61
  end
55
62
 
56
63
  # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
64
+ #
65
+ # begin
66
+ # complex_operation_that_internally_calls_destroy!
67
+ # rescue ActiveRecord::RecordNotDestroyed => invalid
68
+ # puts invalid.record.errors
69
+ # end
70
+ #
57
71
  class RecordNotDestroyed < ActiveRecordError
72
+ attr_reader :record
73
+
74
+ def initialize(message, record = nil)
75
+ @record = record
76
+ super(message)
77
+ end
58
78
  end
59
79
 
60
80
  # Superclass for all database execution errors.
@@ -82,35 +102,26 @@ module ActiveRecord
82
102
  class InvalidForeignKey < WrappedDatabaseException
83
103
  end
84
104
 
85
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
86
- # when using +find+ method)
87
- # does not match number of expected variables.
105
+ # Raised when number of bind variables in statement given to +:condition+ key
106
+ # (for example, when using +find+ method) does not match number of expected
107
+ # values supplied.
88
108
  #
89
- # For example, in
109
+ # For example, when there are two placeholders with only one value supplied:
90
110
  #
91
111
  # Location.where("lat = ? AND lng = ?", 53.7362)
92
- #
93
- # two placeholders are given but only one variable to fill them.
94
112
  class PreparedStatementInvalid < ActiveRecordError
95
113
  end
96
114
 
97
- # Raised when a given database does not exist
98
- class NoDatabaseError < ActiveRecordError
99
- def initialize(message)
100
- super extend_message(message)
101
- end
102
-
103
- # can be over written to add additional error information.
104
- def extend_message(message)
105
- message
106
- end
115
+ # Raised when a given database does not exist.
116
+ class NoDatabaseError < StatementInvalid
107
117
  end
108
118
 
109
119
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
110
120
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
111
121
  # the page before the other.
112
122
  #
113
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
123
+ # Read more about optimistic locking in ActiveRecord::Locking module
124
+ # documentation.
114
125
  class StaleObjectError < ActiveRecordError
115
126
  attr_reader :record, :attempted_action
116
127
 
@@ -122,8 +133,9 @@ module ActiveRecord
122
133
 
123
134
  end
124
135
 
125
- # Raised when association is being configured improperly or
126
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
136
+ # Raised when association is being configured improperly or user tries to use
137
+ # offset and limit together with +has_many+ or +has_and_belongs_to_many+
138
+ # associations.
127
139
  class ConfigurationError < ActiveRecordError
128
140
  end
129
141
 
@@ -161,7 +173,8 @@ module ActiveRecord
161
173
  class Rollback < ActiveRecordError
162
174
  end
163
175
 
164
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
176
+ # Raised when attribute has a name reserved by Active Record (when attribute
177
+ # has name of one of Active Record instance methods).
165
178
  class DangerousAttributeError < ActiveRecordError
166
179
  end
167
180
 
@@ -173,16 +186,17 @@ module ActiveRecord
173
186
  def initialize(record, attribute)
174
187
  @record = record
175
188
  @attribute = attribute.to_s
176
- super("unknown attribute: #{attribute}")
189
+ super("unknown attribute '#{attribute}' for #{@record.class}.")
177
190
  end
178
191
 
179
192
  end
180
193
 
181
194
  # Raised when an error occurred while doing a mass assignment to an attribute through the
182
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
195
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
183
196
  # offending attribute.
184
197
  class AttributeAssignmentError < ActiveRecordError
185
198
  attr_reader :exception, :attribute
199
+
186
200
  def initialize(message, exception, attribute)
187
201
  super(message)
188
202
  @exception = exception
@@ -195,6 +209,7 @@ module ActiveRecord
195
209
  # objects, each corresponding to the error while assigning to an attribute.
196
210
  class MultiparameterAssignmentErrors < ActiveRecordError
197
211
  attr_reader :errors
212
+
198
213
  def initialize(errors)
199
214
  @errors = errors
200
215
  end
@@ -204,11 +219,12 @@ module ActiveRecord
204
219
  class UnknownPrimaryKey < ActiveRecordError
205
220
  attr_reader :model
206
221
 
207
- def initialize(model)
208
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
222
+ def initialize(model, description = nil)
223
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
+ message += "\n#{description}" if description
225
+ super(message)
209
226
  @model = model
210
227
  end
211
-
212
228
  end
213
229
 
214
230
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -225,6 +241,13 @@ module ActiveRecord
225
241
  class ImmutableRelation < ActiveRecordError
226
242
  end
227
243
 
244
+ # TransactionIsolationError will be raised under the following conditions:
245
+ #
246
+ # * The adapter does not support setting the isolation level
247
+ # * You are joining an existing open transaction
248
+ # * You are creating a nested (savepoint) transaction
249
+ #
250
+ # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
228
251
  class TransactionIsolationError < ActiveRecordError
229
252
  end
230
253
  end
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  end.join("\n")
28
28
  end.join("\n")
29
29
 
30
- # Overriding inspect to be more human readable, specially in the console.
30
+ # Overriding inspect to be more human readable, especially in the console.
31
31
  def str.inspect
32
32
  self
33
33
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # On the other hand, we want to monitor the performance of our real database
20
20
  # queries, not the performance of the access to the query cache.
21
21
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
22
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
22
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
23
23
  def ignore_payload?(payload)
24
24
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
25
25
  end
@@ -2,6 +2,7 @@ require 'erb'
2
2
  require 'yaml'
3
3
  require 'zlib'
4
4
  require 'active_support/dependencies'
5
+ require 'active_support/core_ext/digest/uuid'
5
6
  require 'active_record/fixture_set/file'
6
7
  require 'active_record/errors'
7
8
 
@@ -14,9 +15,10 @@ module ActiveRecord
14
15
  # They are stored in YAML files, one file per model, which are placed in the directory
15
16
  # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
16
17
  # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
17
- # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
18
- # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
19
- # like this:
18
+ # The fixture file ends with the +.yml+ file extension, for example:
19
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
20
+ #
21
+ # The format of a fixture file looks like this:
20
22
  #
21
23
  # rubyonrails:
22
24
  # id: 1
@@ -32,7 +34,7 @@ module ActiveRecord
32
34
  # is followed by an indented list of key/value pairs in the "key: value" format. Records are
33
35
  # separated by a blank line for your viewing pleasure.
34
36
  #
35
- # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
37
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
36
38
  # See http://yaml.org/type/omap.html
37
39
  # for the specification. You will need ordered fixtures when you have foreign key constraints
38
40
  # on keys in the same table. This is commonly needed for tree structures. Example:
@@ -60,8 +62,8 @@ module ActiveRecord
60
62
  # end
61
63
  # end
62
64
  #
63
- # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
64
- # so this test will succeed.
65
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
66
+ # database, so this test will succeed.
65
67
  #
66
68
  # The testing environment will automatically load the all fixtures into the database before each
67
69
  # test. To ensure consistent data, the environment deletes the fixtures before running the load.
@@ -179,6 +181,9 @@ module ActiveRecord
179
181
  # * Stable, autogenerated IDs
180
182
  # * Label references for associations (belongs_to, has_one, has_many)
181
183
  # * HABTM associations as inline lists
184
+ #
185
+ # There are some more advanced features available even if the id is specified:
186
+ #
182
187
  # * Autofilled timestamp columns
183
188
  # * Fixture label interpolation
184
189
  # * Support for YAML defaults
@@ -361,6 +366,7 @@ module ActiveRecord
361
366
  # geeksomnia:
362
367
  # name: Geeksomnia's Account
363
368
  # subdomain: $LABEL
369
+ # email: $LABEL@email.com
364
370
  #
365
371
  # Also, sometimes (like when porting older join table fixtures) you'll need
366
372
  # to be able to get a hold of the identifier for a given label. ERB
@@ -372,8 +378,9 @@ module ActiveRecord
372
378
  #
373
379
  # == Support for YAML defaults
374
380
  #
375
- # You probably already know how to use YAML to set and reuse defaults in
376
- # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
381
+ # You can set and reuse defaults in your fixtures YAML file.
382
+ # This is the same technique used in the +database.yml+ file to specify
383
+ # defaults:
377
384
  #
378
385
  # DEFAULTS: &DEFAULTS
379
386
  # created_on: <%= 3.weeks.ago.to_s(:db) %>
@@ -389,7 +396,8 @@ module ActiveRecord
389
396
  # Any fixture labeled "DEFAULTS" is safely ignored.
390
397
  class FixtureSet
391
398
  #--
392
- # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
399
+ # An instance of FixtureSet is normally stored in a single YAML file and
400
+ # possibly in a folder with the same name.
393
401
  #++
394
402
 
395
403
  MAX_ID = 2 ** 30 - 1
@@ -459,13 +467,7 @@ module ActiveRecord
459
467
  @config = config
460
468
 
461
469
  # Remove string values that aren't constants or subclasses of AR
462
- @class_names.delete_if { |k,klass|
463
- unless klass.is_a? Class
464
- klass = klass.safe_constantize
465
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
466
- end
467
- !insert_class(@class_names, k, klass)
468
- }
470
+ @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
469
471
  end
470
472
 
471
473
  def [](fs_name)
@@ -516,14 +518,14 @@ module ActiveRecord
516
518
  ::File.join(fixtures_directory, fs_name))
517
519
  end
518
520
 
519
- all_loaded_fixtures.update(fixtures_map)
521
+ update_all_loaded_fixtures fixtures_map
520
522
 
521
523
  connection.transaction(:requires_new => true) do
522
524
  fixture_sets.each do |fs|
523
525
  conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
524
526
  table_rows = fs.table_rows
525
527
 
526
- table_rows.keys.each do |table|
528
+ table_rows.each_key do |table|
527
529
  conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
528
530
  end
529
531
 
@@ -547,9 +549,13 @@ module ActiveRecord
547
549
  end
548
550
 
549
551
  # Returns a consistent, platform-independent identifier for +label+.
550
- # Identifiers are positive integers less than 2^32.
551
- def self.identify(label)
552
- Zlib.crc32(label.to_s) % MAX_ID
552
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
553
+ def self.identify(label, column_type = :integer)
554
+ if column_type == :uuid
555
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
556
+ else
557
+ Zlib.crc32(label.to_s) % MAX_ID
558
+ end
553
559
  end
554
560
 
555
561
  # Superclass for the evaluation contexts used by ERB fixtures.
@@ -557,6 +563,10 @@ module ActiveRecord
557
563
  @context_class ||= Class.new
558
564
  end
559
565
 
566
+ def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
567
+ all_loaded_fixtures.update(fixtures_map)
568
+ end
569
+
560
570
  attr_reader :table_name, :name, :fixtures, :model_class, :config
561
571
 
562
572
  def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
@@ -565,10 +575,6 @@ module ActiveRecord
565
575
  @config = config
566
576
  @model_class = nil
567
577
 
568
- if class_name.is_a?(String)
569
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
570
- end
571
-
572
578
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
573
579
  @model_class = class_name
574
580
  else
@@ -625,12 +631,12 @@ module ActiveRecord
625
631
 
626
632
  # interpolate the fixture label
627
633
  row.each do |key, value|
628
- row[key] = label if "$LABEL" == value
634
+ row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
629
635
  end
630
636
 
631
637
  # generate a primary key if necessary
632
638
  if has_primary_key_column? && !row.include?(primary_key_name)
633
- row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
639
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
634
640
  end
635
641
 
636
642
  # If STI is used, find the correct subclass for association reflection
@@ -641,19 +647,20 @@ module ActiveRecord
641
647
  model_class
642
648
  end
643
649
 
644
- reflection_class._reflections.values.each do |association|
650
+ reflection_class._reflections.each_value do |association|
645
651
  case association.macro
646
652
  when :belongs_to
647
653
  # Do not replace association name with association foreign key if they are named the same
648
654
  fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
649
655
 
650
656
  if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
651
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
657
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
652
658
  # support polymorphic belongs_to as "label (Type)"
653
659
  row[association.foreign_type] = $1
654
660
  end
655
661
 
656
- row[fk_name] = ActiveRecord::FixtureSet.identify(value)
662
+ fk_type = reflection_class.columns_hash[fk_name].type
663
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
657
664
  end
658
665
  when :has_many
659
666
  if association.options[:through]
@@ -680,6 +687,10 @@ module ActiveRecord
680
687
  def name
681
688
  @association.name
682
689
  end
690
+
691
+ def primary_key_type
692
+ @association.klass.column_types[@association.klass.primary_key].type
693
+ end
683
694
  end
684
695
 
685
696
  class HasManyThroughProxy < ReflectionProxy # :nodoc:
@@ -701,17 +712,22 @@ module ActiveRecord
701
712
  @primary_key_name ||= model_class && model_class.primary_key
702
713
  end
703
714
 
715
+ def primary_key_type
716
+ @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
717
+ end
718
+
704
719
  def add_join_records(rows, row, association)
705
720
  # This is the case when the join table has no fixtures file
706
721
  if (targets = row.delete(association.name.to_s))
707
- table_name = association.join_table
708
- lhs_key = association.lhs_key
709
- rhs_key = association.rhs_key
722
+ table_name = association.join_table
723
+ column_type = association.primary_key_type
724
+ lhs_key = association.lhs_key
725
+ rhs_key = association.rhs_key
710
726
 
711
727
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
712
728
  rows[table_name].concat targets.map { |target|
713
729
  { lhs_key => row[primary_key_name],
714
- rhs_key => ActiveRecord::FixtureSet.identify(target) }
730
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
715
731
  }
716
732
  end
717
733
  end
@@ -792,7 +808,9 @@ module ActiveRecord
792
808
 
793
809
  def find
794
810
  if model_class
795
- model_class.unscoped.find(fixture[model_class.primary_key])
811
+ model_class.unscoped do
812
+ model_class.find(fixture[model_class.primary_key])
813
+ end
796
814
  else
797
815
  raise FixtureClassNotFound, "No class attached to find."
798
816
  end
@@ -856,34 +874,9 @@ module ActiveRecord
856
874
  end
857
875
 
858
876
  self.fixture_table_names |= fixture_set_names
859
- require_fixture_classes(fixture_set_names, self.config)
860
877
  setup_fixture_accessors(fixture_set_names)
861
878
  end
862
879
 
863
- def try_to_load_dependency(file_name)
864
- require_dependency file_name
865
- rescue LoadError => e
866
- unless fixture_class_names.key?(file_name.pluralize)
867
- if ActiveRecord::Base.logger
868
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class")
869
- ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
870
- end
871
- end
872
- end
873
-
874
- def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
875
- if fixture_set_names
876
- fixture_set_names = fixture_set_names.map { |n| n.to_s }
877
- else
878
- fixture_set_names = fixture_table_names
879
- end
880
-
881
- fixture_set_names.each do |file_name|
882
- file_name = file_name.singularize if config.pluralize_table_names
883
- try_to_load_dependency(file_name)
884
- end
885
- end
886
-
887
880
  def setup_fixture_accessors(fixture_set_names = nil)
888
881
  fixture_set_names = Array(fixture_set_names || fixture_table_names)
889
882
  methods = Module.new do
@@ -960,7 +953,7 @@ module ActiveRecord
960
953
  end
961
954
 
962
955
  # Instantiate fixtures for every test if requested.
963
- instantiate_fixtures(config) if use_instantiated_fixtures
956
+ instantiate_fixtures if use_instantiated_fixtures
964
957
  end
965
958
 
966
959
  def teardown_fixtures
@@ -987,16 +980,9 @@ module ActiveRecord
987
980
  Hash[fixtures.map { |f| [f.name, f] }]
988
981
  end
989
982
 
990
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
991
- @@required_fixture_classes = false
992
-
993
- def instantiate_fixtures(config)
983
+ def instantiate_fixtures
994
984
  if pre_loaded_fixtures
995
985
  raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
996
- unless @@required_fixture_classes
997
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
998
- @@required_fixture_classes = true
999
- end
1000
986
  ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
1001
987
  else
1002
988
  raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?