activerecord 4.1.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  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,12 +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
- belongs_to = child_class.reflect_on_all_associations(:belongs_to)
33
- reflection = belongs_to.find { |e| 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? }
34
38
  counter_name = reflection.counter_cache_column
35
39
 
36
40
  stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
37
- arel_table[counter_name] => object.send(association).count
41
+ arel_table[counter_name] => object.send(counter_association).count(:all)
38
42
  }, primary_key)
39
43
  connection.update stmt
40
44
  end
@@ -118,5 +122,54 @@ module ActiveRecord
118
122
  update_counters(id, counter_name => -1)
119
123
  end
120
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) if reflection.belongs_to? && reflection.counter_cache_column
171
+ end
172
+ end
173
+
121
174
  end
122
175
  end
@@ -27,8 +27,10 @@ module ActiveRecord
27
27
  # conversation.status # => nil
28
28
  #
29
29
  # 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.
30
+ # as well. With the above example:
31
+ #
32
+ # Conversation.active
33
+ # Conversation.archived
32
34
  #
33
35
  # You can set the default value from the database declaration, like:
34
36
  #
@@ -67,12 +69,12 @@ module ActiveRecord
67
69
  #
68
70
  # Where conditions on an enum attribute must use the ordinal value of an enum.
69
71
  module Enum
70
- def self.extended(base)
72
+ def self.extended(base) # :nodoc:
71
73
  base.class_attribute(:defined_enums)
72
74
  base.defined_enums = {}
73
75
  end
74
76
 
75
- def inherited(base)
77
+ def inherited(base) # :nodoc:
76
78
  base.defined_enums = defined_enums.deep_dup
77
79
  super
78
80
  end
@@ -138,19 +140,16 @@ module ActiveRecord
138
140
  @_enum_methods_module ||= begin
139
141
  mod = Module.new do
140
142
  private
141
- def save_changed_attribute(attr_name, value)
143
+ def save_changed_attribute(attr_name, old)
142
144
  if (mapping = self.class.defined_enums[attr_name.to_s])
145
+ value = _read_attribute(attr_name)
143
146
  if attribute_changed?(attr_name)
144
- old = changed_attributes[attr_name]
145
-
146
147
  if mapping[old] == value
147
- changed_attributes.delete(attr_name)
148
+ clear_attribute_changes([attr_name])
148
149
  end
149
150
  else
150
- old = clone_attribute_value(:read_attribute, attr_name)
151
-
152
151
  if old != value
153
- changed_attributes[attr_name] = mapping.key old
152
+ set_attribute_was(attr_name, mapping.key(old))
154
153
  end
155
154
  end
156
155
  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(record)
75
+ @record = record
76
+ super()
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
@@ -225,6 +240,13 @@ module ActiveRecord
225
240
  class ImmutableRelation < ActiveRecordError
226
241
  end
227
242
 
243
+ # TransactionIsolationError will be raised under the following conditions:
244
+ #
245
+ # * The adapter does not support setting the isolation level
246
+ # * You are joining an existing open transaction
247
+ # * You are creating a nested (savepoint) transaction
248
+ #
249
+ # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
228
250
  class TransactionIsolationError < ActiveRecordError
229
251
  end
230
252
  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
@@ -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.
@@ -124,7 +126,7 @@ module ActiveRecord
124
126
  # that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
125
127
  #
126
128
  # - define a helper method in `test_helper.rb`
127
- # class FixtureFileHelpers
129
+ # module FixtureFileHelpers
128
130
  # def file_sha(path)
129
131
  # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
130
132
  # end
@@ -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
 
@@ -549,9 +551,13 @@ module ActiveRecord
549
551
  end
550
552
 
551
553
  # Returns a consistent, platform-independent identifier for +label+.
552
- # Identifiers are positive integers less than 2^32.
553
- def self.identify(label)
554
- Zlib.crc32(label.to_s) % MAX_ID
554
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
555
+ def self.identify(label, column_type = :integer)
556
+ if column_type == :uuid
557
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
558
+ else
559
+ Zlib.crc32(label.to_s) % MAX_ID
560
+ end
555
561
  end
556
562
 
557
563
  # Superclass for the evaluation contexts used by ERB fixtures.
@@ -559,6 +565,10 @@ module ActiveRecord
559
565
  @context_class ||= Class.new
560
566
  end
561
567
 
568
+ def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
569
+ all_loaded_fixtures.update(fixtures_map)
570
+ end
571
+
562
572
  attr_reader :table_name, :name, :fixtures, :model_class, :config
563
573
 
564
574
  def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
@@ -567,10 +577,6 @@ module ActiveRecord
567
577
  @config = config
568
578
  @model_class = nil
569
579
 
570
- if class_name.is_a?(String)
571
- 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.")
572
- end
573
-
574
580
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
575
581
  @model_class = class_name
576
582
  else
@@ -627,12 +633,12 @@ module ActiveRecord
627
633
 
628
634
  # interpolate the fixture label
629
635
  row.each do |key, value|
630
- row[key] = label if "$LABEL" == value
636
+ row[key] = value.gsub("$LABEL", label) if value.is_a?(String)
631
637
  end
632
638
 
633
639
  # generate a primary key if necessary
634
640
  if has_primary_key_column? && !row.include?(primary_key_name)
635
- row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
641
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
636
642
  end
637
643
 
638
644
  # If STI is used, find the correct subclass for association reflection
@@ -643,19 +649,20 @@ module ActiveRecord
643
649
  model_class
644
650
  end
645
651
 
646
- reflection_class.reflect_on_all_associations.each do |association|
652
+ reflection_class._reflections.each_value do |association|
647
653
  case association.macro
648
654
  when :belongs_to
649
655
  # Do not replace association name with association foreign key if they are named the same
650
656
  fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
651
657
 
652
658
  if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
653
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
659
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
654
660
  # support polymorphic belongs_to as "label (Type)"
655
661
  row[association.foreign_type] = $1
656
662
  end
657
663
 
658
- row[fk_name] = ActiveRecord::FixtureSet.identify(value)
664
+ fk_type = association.active_record.columns_hash[fk_name].type
665
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
659
666
  end
660
667
  when :has_many
661
668
  if association.options[:through]
@@ -682,6 +689,10 @@ module ActiveRecord
682
689
  def name
683
690
  @association.name
684
691
  end
692
+
693
+ def primary_key_type
694
+ @association.klass.column_types[@association.klass.primary_key].type
695
+ end
685
696
  end
686
697
 
687
698
  class HasManyThroughProxy < ReflectionProxy # :nodoc:
@@ -699,17 +710,22 @@ module ActiveRecord
699
710
  @primary_key_name ||= model_class && model_class.primary_key
700
711
  end
701
712
 
713
+ def primary_key_type
714
+ @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
715
+ end
716
+
702
717
  def add_join_records(rows, row, association)
703
718
  # This is the case when the join table has no fixtures file
704
719
  if (targets = row.delete(association.name.to_s))
705
- table_name = association.join_table
706
- lhs_key = association.lhs_key
707
- rhs_key = association.rhs_key
720
+ table_name = association.join_table
721
+ column_type = association.primary_key_type
722
+ lhs_key = association.lhs_key
723
+ rhs_key = association.rhs_key
708
724
 
709
725
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
710
726
  rows[table_name].concat targets.map { |target|
711
727
  { lhs_key => row[primary_key_name],
712
- rhs_key => ActiveRecord::FixtureSet.identify(target) }
728
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
713
729
  }
714
730
  end
715
731
  end
@@ -790,7 +806,9 @@ module ActiveRecord
790
806
 
791
807
  def find
792
808
  if model_class
793
- model_class.find(fixture[model_class.primary_key])
809
+ model_class.unscoped do
810
+ model_class.find(fixture[model_class.primary_key])
811
+ end
794
812
  else
795
813
  raise FixtureClassNotFound, "No class attached to find."
796
814
  end
@@ -854,34 +872,9 @@ module ActiveRecord
854
872
  end
855
873
 
856
874
  self.fixture_table_names |= fixture_set_names
857
- require_fixture_classes(fixture_set_names, self.config)
858
875
  setup_fixture_accessors(fixture_set_names)
859
876
  end
860
877
 
861
- def try_to_load_dependency(file_name)
862
- require_dependency file_name
863
- rescue LoadError => e
864
- # Let's hope the developer has included it
865
- # Let's warn in case this is a subdependency, otherwise
866
- # subdependency error messages are totally cryptic
867
- if ActiveRecord::Base.logger
868
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
869
- end
870
- end
871
-
872
- def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
873
- if fixture_set_names
874
- fixture_set_names = fixture_set_names.map { |n| n.to_s }
875
- else
876
- fixture_set_names = fixture_table_names
877
- end
878
-
879
- fixture_set_names.each do |file_name|
880
- file_name = file_name.singularize if config.pluralize_table_names
881
- try_to_load_dependency(file_name)
882
- end
883
- end
884
-
885
878
  def setup_fixture_accessors(fixture_set_names = nil)
886
879
  fixture_set_names = Array(fixture_set_names || fixture_table_names)
887
880
  methods = Module.new do
@@ -958,7 +951,7 @@ module ActiveRecord
958
951
  end
959
952
 
960
953
  # Instantiate fixtures for every test if requested.
961
- instantiate_fixtures(config) if use_instantiated_fixtures
954
+ instantiate_fixtures if use_instantiated_fixtures
962
955
  end
963
956
 
964
957
  def teardown_fixtures
@@ -985,16 +978,9 @@ module ActiveRecord
985
978
  Hash[fixtures.map { |f| [f.name, f] }]
986
979
  end
987
980
 
988
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
989
- @@required_fixture_classes = false
990
-
991
- def instantiate_fixtures(config)
981
+ def instantiate_fixtures
992
982
  if pre_loaded_fixtures
993
983
  raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
994
- unless @@required_fixture_classes
995
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
996
- @@required_fixture_classes = true
997
- end
998
984
  ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
999
985
  else
1000
986
  raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
@@ -1,12 +1,12 @@
1
1
  module ActiveRecord
2
- # Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
2
+ # Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
3
3
  def self.gem_version
4
4
  Gem::Version.new VERSION::STRING
5
5
  end
6
6
 
7
7
  module VERSION
8
8
  MAJOR = 4
9
- MINOR = 1
9
+ MINOR = 2
10
10
  TINY = 0
11
11
  PRE = nil
12
12