activerecord 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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,252 @@
1
+ module ActiveRecord
2
+
3
+ # = Active Record Errors
4
+ #
5
+ # Generic Active Record exception class.
6
+ class ActiveRecordError < StandardError
7
+ end
8
+
9
+ # Raised when the single-table inheritance mechanism fails to locate the subclass
10
+ # (for example due to improper usage of column that +inheritance_column+ points to).
11
+ class SubclassNotFound < ActiveRecordError #:nodoc:
12
+ end
13
+
14
+ # Raised when an object assigned to an association has an incorrect type.
15
+ #
16
+ # class Ticket < ActiveRecord::Base
17
+ # has_many :patches
18
+ # end
19
+ #
20
+ # class Patch < ActiveRecord::Base
21
+ # belongs_to :ticket
22
+ # end
23
+ #
24
+ # # Comments are not patches, this assignment raises AssociationTypeMismatch.
25
+ # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
26
+ class AssociationTypeMismatch < ActiveRecordError
27
+ end
28
+
29
+ # Raised when unserialized object's type mismatches one specified for serializable field.
30
+ class SerializationTypeMismatch < ActiveRecordError
31
+ end
32
+
33
+ # Raised when adapter not specified on connection (or configuration file
34
+ # +config/database.yml+ misses adapter field).
35
+ class AdapterNotSpecified < ActiveRecordError
36
+ end
37
+
38
+ # Raised when Active Record cannot find database adapter specified in
39
+ # +config/database.yml+ or programmatically.
40
+ class AdapterNotFound < ActiveRecordError
41
+ end
42
+
43
+ # Raised when connection to the database could not been established (for
44
+ # example when +connection=+ is given a nil object).
45
+ class ConnectionNotEstablished < ActiveRecordError
46
+ end
47
+
48
+ # Raised when Active Record cannot find record by given id or set of ids.
49
+ class RecordNotFound < ActiveRecordError
50
+ end
51
+
52
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
53
+ # saved because record is invalid.
54
+ class RecordNotSaved < ActiveRecordError
55
+ attr_reader :record
56
+
57
+ def initialize(message, record = nil)
58
+ @record = record
59
+ super(message)
60
+ end
61
+ end
62
+
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
+ #
71
+ class RecordNotDestroyed < ActiveRecordError
72
+ attr_reader :record
73
+
74
+ def initialize(record)
75
+ @record = record
76
+ super()
77
+ end
78
+ end
79
+
80
+ # Superclass for all database execution errors.
81
+ #
82
+ # Wraps the underlying database error as +original_exception+.
83
+ class StatementInvalid < ActiveRecordError
84
+ attr_reader :original_exception
85
+
86
+ def initialize(message, original_exception = nil)
87
+ super(message)
88
+ @original_exception = original_exception
89
+ end
90
+ end
91
+
92
+ # Defunct wrapper class kept for compatibility.
93
+ # +StatementInvalid+ wraps the original exception now.
94
+ class WrappedDatabaseException < StatementInvalid
95
+ end
96
+
97
+ # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
98
+ class RecordNotUnique < WrappedDatabaseException
99
+ end
100
+
101
+ # Raised when a record cannot be inserted or updated because it references a non-existent record.
102
+ class InvalidForeignKey < WrappedDatabaseException
103
+ end
104
+
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.
108
+ #
109
+ # For example, when there are two placeholders with only one value supplied:
110
+ #
111
+ # Location.where("lat = ? AND lng = ?", 53.7362)
112
+ class PreparedStatementInvalid < ActiveRecordError
113
+ end
114
+
115
+ # Raised when a given database does not exist.
116
+ class NoDatabaseError < StatementInvalid
117
+ end
118
+
119
+ # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
120
+ # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
121
+ # the page before the other.
122
+ #
123
+ # Read more about optimistic locking in ActiveRecord::Locking module
124
+ # documentation.
125
+ class StaleObjectError < ActiveRecordError
126
+ attr_reader :record, :attempted_action
127
+
128
+ def initialize(record, attempted_action)
129
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
130
+ @record = record
131
+ @attempted_action = attempted_action
132
+ end
133
+
134
+ end
135
+
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.
139
+ class ConfigurationError < ActiveRecordError
140
+ end
141
+
142
+ # Raised on attempt to update record that is instantiated as read only.
143
+ class ReadOnlyRecord < ActiveRecordError
144
+ end
145
+
146
+ # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
147
+ # to distinguish a deliberate rollback from other exceptional situations.
148
+ # Normally, raising an exception will cause the +transaction+ method to rollback
149
+ # the database transaction *and* pass on the exception. But if you raise an
150
+ # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
151
+ # without passing on the exception.
152
+ #
153
+ # For example, you could do this in your controller to rollback a transaction:
154
+ #
155
+ # class BooksController < ActionController::Base
156
+ # def create
157
+ # Book.transaction do
158
+ # book = Book.new(params[:book])
159
+ # book.save!
160
+ # if today_is_friday?
161
+ # # The system must fail on Friday so that our support department
162
+ # # won't be out of job. We silently rollback this transaction
163
+ # # without telling the user.
164
+ # raise ActiveRecord::Rollback, "Call tech support!"
165
+ # end
166
+ # end
167
+ # # ActiveRecord::Rollback is the only exception that won't be passed on
168
+ # # by ActiveRecord::Base.transaction, so this line will still be reached
169
+ # # even on Friday.
170
+ # redirect_to root_url
171
+ # end
172
+ # end
173
+ class Rollback < ActiveRecordError
174
+ end
175
+
176
+ # Raised when attribute has a name reserved by Active Record (when attribute
177
+ # has name of one of Active Record instance methods).
178
+ class DangerousAttributeError < ActiveRecordError
179
+ end
180
+
181
+ # Raised when unknown attributes are supplied via mass assignment.
182
+ class UnknownAttributeError < NoMethodError
183
+
184
+ attr_reader :record, :attribute
185
+
186
+ def initialize(record, attribute)
187
+ @record = record
188
+ @attribute = attribute.to_s
189
+ super("unknown attribute '#{attribute}' for #{@record.class}.")
190
+ end
191
+
192
+ end
193
+
194
+ # Raised when an error occurred while doing a mass assignment to an attribute through the
195
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
196
+ # offending attribute.
197
+ class AttributeAssignmentError < ActiveRecordError
198
+ attr_reader :exception, :attribute
199
+
200
+ def initialize(message, exception, attribute)
201
+ super(message)
202
+ @exception = exception
203
+ @attribute = attribute
204
+ end
205
+ end
206
+
207
+ # Raised when there are multiple errors while doing a mass assignment through the +attributes+
208
+ # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
209
+ # objects, each corresponding to the error while assigning to an attribute.
210
+ class MultiparameterAssignmentErrors < ActiveRecordError
211
+ attr_reader :errors
212
+
213
+ def initialize(errors)
214
+ @errors = errors
215
+ end
216
+ end
217
+
218
+ # Raised when a primary key is needed, but not specified in the schema or model.
219
+ class UnknownPrimaryKey < ActiveRecordError
220
+ attr_reader :model
221
+
222
+ def initialize(model)
223
+ super("Unknown primary key for table #{model.table_name} in model #{model}.")
224
+ @model = model
225
+ end
226
+
227
+ end
228
+
229
+ # Raised when a relation cannot be mutated because it's already loaded.
230
+ #
231
+ # class Task < ActiveRecord::Base
232
+ # end
233
+ #
234
+ # relation = Task.all
235
+ # relation.loaded? # => true
236
+ #
237
+ # # Methods which try to mutate a loaded relation fail.
238
+ # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
239
+ # relation.limit!(5) # => ActiveRecord::ImmutableRelation
240
+ class ImmutableRelation < ActiveRecordError
241
+ end
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.
250
+ class TransactionIsolationError < ActiveRecordError
251
+ end
252
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/lazy_load_hooks'
2
+ require 'active_record/explain_registry'
3
+
4
+ module ActiveRecord
5
+ module Explain
6
+ # Executes the block with the collect flag enabled. Queries are collected
7
+ # asynchronously by the subscriber and returned.
8
+ def collecting_queries_for_explain # :nodoc:
9
+ ExplainRegistry.collect = true
10
+ yield
11
+ ExplainRegistry.queries
12
+ ensure
13
+ ExplainRegistry.reset
14
+ end
15
+
16
+ # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
17
+ # Returns a formatted string ready to be logged.
18
+ def exec_explain(queries) # :nodoc:
19
+ str = queries.map do |sql, bind|
20
+ [].tap do |msg|
21
+ msg << "EXPLAIN for: #{sql}"
22
+ unless bind.empty?
23
+ bind_msg = bind.map {|col, val| [col.name, val]}.inspect
24
+ msg.last << " #{bind_msg}"
25
+ end
26
+ msg << connection.explain(sql, bind)
27
+ end.join("\n")
28
+ end.join("\n")
29
+
30
+ # Overriding inspect to be more human readable, especially in the console.
31
+ def str.inspect
32
+ self
33
+ end
34
+
35
+ str
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for EXPLAIN. For example
5
+ #
6
+ # ActiveRecord::ExplainRegistry.queries
7
+ #
8
+ # returns the collected queries local to the current thread.
9
+ #
10
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
11
+ # for further details.
12
+ class ExplainRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :queries, :collect
16
+
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ def collect?
22
+ @collect
23
+ end
24
+
25
+ def reset
26
+ @collect = false
27
+ @queries = []
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'active_support/notifications'
2
+ require 'active_record/explain_registry'
3
+
4
+ module ActiveRecord
5
+ class ExplainSubscriber # :nodoc:
6
+ def start(name, id, payload)
7
+ # unused
8
+ end
9
+
10
+ def finish(name, id, payload)
11
+ if ExplainRegistry.collect? && !ignore_payload?(payload)
12
+ ExplainRegistry.queries << payload.values_at(:sql, :binds)
13
+ end
14
+ end
15
+
16
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
17
+ # our own EXPLAINs now matter how loopingly beautiful that would be.
18
+ #
19
+ # On the other hand, we want to monitor the performance of our real database
20
+ # queries, not the performance of the access to the query cache.
21
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
22
+ EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
23
+ def ignore_payload?(payload)
24
+ payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
25
+ end
26
+
27
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
4
+ module ActiveRecord
5
+ class FixtureSet
6
+ class File # :nodoc:
7
+ include Enumerable
8
+
9
+ ##
10
+ # Open a fixture file named +file+. When called with a block, the block
11
+ # is called with the filehandle and the filehandle is automatically closed
12
+ # when the block finishes.
13
+ def self.open(file)
14
+ x = new file
15
+ block_given? ? yield(x) : x
16
+ end
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ @rows = nil
21
+ end
22
+
23
+ def each(&block)
24
+ rows.each(&block)
25
+ end
26
+
27
+
28
+ private
29
+ def rows
30
+ return @rows if @rows
31
+
32
+ begin
33
+ data = YAML.load(render(IO.read(@file)))
34
+ rescue ArgumentError, Psych::SyntaxError => error
35
+ raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
36
+ end
37
+ @rows = data ? validate(data).to_a : []
38
+ end
39
+
40
+ def render(content)
41
+ context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
42
+ ERB.new(content).result(context.get_binding)
43
+ end
44
+
45
+ # Validate our unmarshalled data.
46
+ def validate(data)
47
+ unless Hash === data || YAML::Omap === data
48
+ raise Fixture::FormatError, 'fixture is not a hash'
49
+ end
50
+
51
+ raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
52
+ data
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,1007 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'zlib'
4
+ require 'active_support/dependencies'
5
+ require 'active_support/core_ext/digest/uuid'
6
+ require 'active_record/fixture_set/file'
7
+ require 'active_record/errors'
8
+
9
+ module ActiveRecord
10
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
11
+ end
12
+
13
+ # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
14
+ #
15
+ # They are stored in YAML files, one file per model, which are placed in the directory
16
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
17
+ # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
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:
22
+ #
23
+ # rubyonrails:
24
+ # id: 1
25
+ # name: Ruby on Rails
26
+ # url: http://www.rubyonrails.org
27
+ #
28
+ # google:
29
+ # id: 2
30
+ # name: Google
31
+ # url: http://www.google.com
32
+ #
33
+ # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
34
+ # is followed by an indented list of key/value pairs in the "key: value" format. Records are
35
+ # separated by a blank line for your viewing pleasure.
36
+ #
37
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
38
+ # See http://yaml.org/type/omap.html
39
+ # for the specification. You will need ordered fixtures when you have foreign key constraints
40
+ # on keys in the same table. This is commonly needed for tree structures. Example:
41
+ #
42
+ # --- !omap
43
+ # - parent:
44
+ # id: 1
45
+ # parent_id: NULL
46
+ # title: Parent
47
+ # - child:
48
+ # id: 2
49
+ # parent_id: 1
50
+ # title: Child
51
+ #
52
+ # = Using Fixtures in Test Cases
53
+ #
54
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There
55
+ # are two ways to use the fixtures, but first let's take a look at a sample unit test:
56
+ #
57
+ # require 'test_helper'
58
+ #
59
+ # class WebSiteTest < ActiveSupport::TestCase
60
+ # test "web_site_count" do
61
+ # assert_equal 2, WebSite.count
62
+ # end
63
+ # end
64
+ #
65
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
66
+ # database, so this test will succeed.
67
+ #
68
+ # The testing environment will automatically load the all fixtures into the database before each
69
+ # test. To ensure consistent data, the environment deletes the fixtures before running the load.
70
+ #
71
+ # In addition to being available in the database, the fixture's data may also be accessed by
72
+ # using a special dynamic method, which has the same name as the model, and accepts the
73
+ # name of the fixture to instantiate:
74
+ #
75
+ # test "find" do
76
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
77
+ # end
78
+ #
79
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
80
+ # following tests:
81
+ #
82
+ # test "find_alt_method_1" do
83
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
84
+ # end
85
+ #
86
+ # test "find_alt_method_2" do
87
+ # assert_equal "Ruby on Rails", @rubyonrails.name
88
+ # end
89
+ #
90
+ # In order to use these methods to access fixtured data within your testcases, you must specify one of the
91
+ # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
92
+ #
93
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
94
+ # self.use_instantiated_fixtures = true
95
+ #
96
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
97
+ # self.use_instantiated_fixtures = :no_instances
98
+ #
99
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
100
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
101
+ # large sets of fixtured data.
102
+ #
103
+ # = Dynamic fixtures with ERB
104
+ #
105
+ # Some times you don't care about the content of the fixtures as much as you care about the volume.
106
+ # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
107
+ # testing, like:
108
+ #
109
+ # <% 1.upto(1000) do |i| %>
110
+ # fix_<%= i %>:
111
+ # id: <%= i %>
112
+ # name: guy_<%= 1 %>
113
+ # <% end %>
114
+ #
115
+ # This will create 1000 very simple fixtures.
116
+ #
117
+ # Using ERB, you can also inject dynamic values into your fixtures with inserts like
118
+ # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
119
+ # This is however a feature to be used with some caution. The point of fixtures are that they're
120
+ # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
121
+ # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
122
+ # in fixtures are to be considered a code smell.
123
+ #
124
+ # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
125
+ # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
126
+ # that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
127
+ #
128
+ # - define a helper method in `test_helper.rb`
129
+ # module FixtureFileHelpers
130
+ # def file_sha(path)
131
+ # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
132
+ # end
133
+ # end
134
+ # ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
135
+ #
136
+ # - use the helper method in a fixture
137
+ # photo:
138
+ # name: kitten.png
139
+ # sha: <%= file_sha 'files/kitten.png' %>
140
+ #
141
+ # = Transactional Fixtures
142
+ #
143
+ # Test cases can use begin+rollback to isolate their changes to the database instead of having to
144
+ # delete+insert for every test case.
145
+ #
146
+ # class FooTest < ActiveSupport::TestCase
147
+ # self.use_transactional_fixtures = true
148
+ #
149
+ # test "godzilla" do
150
+ # assert !Foo.all.empty?
151
+ # Foo.destroy_all
152
+ # assert Foo.all.empty?
153
+ # end
154
+ #
155
+ # test "godzilla aftermath" do
156
+ # assert !Foo.all.empty?
157
+ # end
158
+ # end
159
+ #
160
+ # If you preload your test database with all fixture data (probably in the rake task) and use
161
+ # transactional fixtures, then you may omit all fixtures declarations in your test cases since
162
+ # all the data's already there and every case rolls back its changes.
163
+ #
164
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
165
+ # true. This will provide access to fixture data for every table that has been loaded through
166
+ # fixtures (depending on the value of +use_instantiated_fixtures+).
167
+ #
168
+ # When *not* to use transactional fixtures:
169
+ #
170
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
171
+ # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
172
+ # and rolled back in teardown. Thus, you won't be able to verify
173
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
174
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
175
+ # Use InnoDB, MaxDB, or NDB instead.
176
+ #
177
+ # = Advanced Fixtures
178
+ #
179
+ # Fixtures that don't specify an ID get some extra features:
180
+ #
181
+ # * Stable, autogenerated IDs
182
+ # * Label references for associations (belongs_to, has_one, has_many)
183
+ # * HABTM associations as inline lists
184
+ #
185
+ # There are some more advanced features available even if the id is specified:
186
+ #
187
+ # * Autofilled timestamp columns
188
+ # * Fixture label interpolation
189
+ # * Support for YAML defaults
190
+ #
191
+ # == Stable, Autogenerated IDs
192
+ #
193
+ # Here, have a monkey fixture:
194
+ #
195
+ # george:
196
+ # id: 1
197
+ # name: George the Monkey
198
+ #
199
+ # reginald:
200
+ # id: 2
201
+ # name: Reginald the Pirate
202
+ #
203
+ # Each of these fixtures has two unique identifiers: one for the database
204
+ # and one for the humans. Why don't we generate the primary key instead?
205
+ # Hashing each fixture's label yields a consistent ID:
206
+ #
207
+ # george: # generated id: 503576764
208
+ # name: George the Monkey
209
+ #
210
+ # reginald: # generated id: 324201669
211
+ # name: Reginald the Pirate
212
+ #
213
+ # Active Record looks at the fixture's model class, discovers the correct
214
+ # primary key, and generates it right before inserting the fixture
215
+ # into the database.
216
+ #
217
+ # The generated ID for a given label is constant, so we can discover
218
+ # any fixture's ID without loading anything, as long as we know the label.
219
+ #
220
+ # == Label references for associations (belongs_to, has_one, has_many)
221
+ #
222
+ # Specifying foreign keys in fixtures can be very fragile, not to
223
+ # mention difficult to read. Since Active Record can figure out the ID of
224
+ # any fixture from its label, you can specify FK's by label instead of ID.
225
+ #
226
+ # === belongs_to
227
+ #
228
+ # Let's break out some more monkeys and pirates.
229
+ #
230
+ # ### in pirates.yml
231
+ #
232
+ # reginald:
233
+ # id: 1
234
+ # name: Reginald the Pirate
235
+ # monkey_id: 1
236
+ #
237
+ # ### in monkeys.yml
238
+ #
239
+ # george:
240
+ # id: 1
241
+ # name: George the Monkey
242
+ # pirate_id: 1
243
+ #
244
+ # Add a few more monkeys and pirates and break this into multiple files,
245
+ # and it gets pretty hard to keep track of what's going on. Let's
246
+ # use labels instead of IDs:
247
+ #
248
+ # ### in pirates.yml
249
+ #
250
+ # reginald:
251
+ # name: Reginald the Pirate
252
+ # monkey: george
253
+ #
254
+ # ### in monkeys.yml
255
+ #
256
+ # george:
257
+ # name: George the Monkey
258
+ # pirate: reginald
259
+ #
260
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
261
+ # finds all the +belongs_to+ associations, and allows you to specify
262
+ # a target *label* for the *association* (monkey: george) rather than
263
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
264
+ #
265
+ # ==== Polymorphic belongs_to
266
+ #
267
+ # Supporting polymorphic relationships is a little bit more complicated, since
268
+ # Active Record needs to know what type your association is pointing at. Something
269
+ # like this should look familiar:
270
+ #
271
+ # ### in fruit.rb
272
+ #
273
+ # belongs_to :eater, polymorphic: true
274
+ #
275
+ # ### in fruits.yml
276
+ #
277
+ # apple:
278
+ # id: 1
279
+ # name: apple
280
+ # eater_id: 1
281
+ # eater_type: Monkey
282
+ #
283
+ # Can we do better? You bet!
284
+ #
285
+ # apple:
286
+ # eater: george (Monkey)
287
+ #
288
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
289
+ #
290
+ # === has_and_belongs_to_many
291
+ #
292
+ # Time to give our monkey some fruit.
293
+ #
294
+ # ### in monkeys.yml
295
+ #
296
+ # george:
297
+ # id: 1
298
+ # name: George the Monkey
299
+ #
300
+ # ### in fruits.yml
301
+ #
302
+ # apple:
303
+ # id: 1
304
+ # name: apple
305
+ #
306
+ # orange:
307
+ # id: 2
308
+ # name: orange
309
+ #
310
+ # grape:
311
+ # id: 3
312
+ # name: grape
313
+ #
314
+ # ### in fruits_monkeys.yml
315
+ #
316
+ # apple_george:
317
+ # fruit_id: 1
318
+ # monkey_id: 1
319
+ #
320
+ # orange_george:
321
+ # fruit_id: 2
322
+ # monkey_id: 1
323
+ #
324
+ # grape_george:
325
+ # fruit_id: 3
326
+ # monkey_id: 1
327
+ #
328
+ # Let's make the HABTM fixture go away.
329
+ #
330
+ # ### in monkeys.yml
331
+ #
332
+ # george:
333
+ # id: 1
334
+ # name: George the Monkey
335
+ # fruits: apple, orange, grape
336
+ #
337
+ # ### in fruits.yml
338
+ #
339
+ # apple:
340
+ # name: apple
341
+ #
342
+ # orange:
343
+ # name: orange
344
+ #
345
+ # grape:
346
+ # name: grape
347
+ #
348
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
349
+ # on George's fixture, but we could've just as easily specified a list
350
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
351
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
352
+ # associations.
353
+ #
354
+ # == Autofilled Timestamp Columns
355
+ #
356
+ # If your table/model specifies any of Active Record's
357
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
358
+ # they will automatically be set to <tt>Time.now</tt>.
359
+ #
360
+ # If you've set specific values, they'll be left alone.
361
+ #
362
+ # == Fixture label interpolation
363
+ #
364
+ # The label of the current fixture is always available as a column value:
365
+ #
366
+ # geeksomnia:
367
+ # name: Geeksomnia's Account
368
+ # subdomain: $LABEL
369
+ # email: $LABEL@email.com
370
+ #
371
+ # Also, sometimes (like when porting older join table fixtures) you'll need
372
+ # to be able to get a hold of the identifier for a given label. ERB
373
+ # to the rescue:
374
+ #
375
+ # george_reginald:
376
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
377
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
378
+ #
379
+ # == Support for YAML defaults
380
+ #
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:
384
+ #
385
+ # DEFAULTS: &DEFAULTS
386
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
387
+ #
388
+ # first:
389
+ # name: Smurf
390
+ # <<: *DEFAULTS
391
+ #
392
+ # second:
393
+ # name: Fraggle
394
+ # <<: *DEFAULTS
395
+ #
396
+ # Any fixture labeled "DEFAULTS" is safely ignored.
397
+ class FixtureSet
398
+ #--
399
+ # An instance of FixtureSet is normally stored in a single YAML file and
400
+ # possibly in a folder with the same name.
401
+ #++
402
+
403
+ MAX_ID = 2 ** 30 - 1
404
+
405
+ @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
406
+
407
+ def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
408
+ config.pluralize_table_names ?
409
+ fixture_set_name.singularize.camelize :
410
+ fixture_set_name.camelize
411
+ end
412
+
413
+ def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
414
+ "#{ config.table_name_prefix }"\
415
+ "#{ fixture_set_name.tr('/', '_') }"\
416
+ "#{ config.table_name_suffix }".to_sym
417
+ end
418
+
419
+ def self.reset_cache
420
+ @@all_cached_fixtures.clear
421
+ end
422
+
423
+ def self.cache_for_connection(connection)
424
+ @@all_cached_fixtures[connection]
425
+ end
426
+
427
+ def self.fixture_is_cached?(connection, table_name)
428
+ cache_for_connection(connection)[table_name]
429
+ end
430
+
431
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
432
+ if keys_to_fetch
433
+ cache_for_connection(connection).values_at(*keys_to_fetch)
434
+ else
435
+ cache_for_connection(connection).values
436
+ end
437
+ end
438
+
439
+ def self.cache_fixtures(connection, fixtures_map)
440
+ cache_for_connection(connection).update(fixtures_map)
441
+ end
442
+
443
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true)
444
+ if load_instances
445
+ fixture_set.each do |fixture_name, fixture|
446
+ begin
447
+ object.instance_variable_set "@#{fixture_name}", fixture.find
448
+ rescue FixtureClassNotFound
449
+ nil
450
+ end
451
+ end
452
+ end
453
+ end
454
+
455
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
456
+ all_loaded_fixtures.each_value do |fixture_set|
457
+ instantiate_fixtures(object, fixture_set, load_instances)
458
+ end
459
+ end
460
+
461
+ cattr_accessor :all_loaded_fixtures
462
+ self.all_loaded_fixtures = {}
463
+
464
+ class ClassCache
465
+ def initialize(class_names, config)
466
+ @class_names = class_names.stringify_keys
467
+ @config = config
468
+
469
+ # Remove string values that aren't constants or subclasses of AR
470
+ @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
471
+ end
472
+
473
+ def [](fs_name)
474
+ @class_names.fetch(fs_name) {
475
+ klass = default_fixture_model(fs_name, @config).safe_constantize
476
+ insert_class(@class_names, fs_name, klass)
477
+ }
478
+ end
479
+
480
+ private
481
+
482
+ def insert_class(class_names, name, klass)
483
+ # We only want to deal with AR objects.
484
+ if klass && klass < ActiveRecord::Base
485
+ class_names[name] = klass
486
+ else
487
+ class_names[name] = nil
488
+ end
489
+ end
490
+
491
+ def default_fixture_model(fs_name, config)
492
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
493
+ end
494
+ end
495
+
496
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
497
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
498
+ class_names = ClassCache.new class_names, config
499
+
500
+ # FIXME: Apparently JK uses this.
501
+ connection = block_given? ? yield : ActiveRecord::Base.connection
502
+
503
+ files_to_read = fixture_set_names.reject { |fs_name|
504
+ fixture_is_cached?(connection, fs_name)
505
+ }
506
+
507
+ unless files_to_read.empty?
508
+ connection.disable_referential_integrity do
509
+ fixtures_map = {}
510
+
511
+ fixture_sets = files_to_read.map do |fs_name|
512
+ klass = class_names[fs_name]
513
+ conn = klass ? klass.connection : connection
514
+ fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
515
+ conn,
516
+ fs_name,
517
+ klass,
518
+ ::File.join(fixtures_directory, fs_name))
519
+ end
520
+
521
+ update_all_loaded_fixtures fixtures_map
522
+
523
+ connection.transaction(:requires_new => true) do
524
+ fixture_sets.each do |fs|
525
+ conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
526
+ table_rows = fs.table_rows
527
+
528
+ table_rows.each_key do |table|
529
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
530
+ end
531
+
532
+ table_rows.each do |fixture_set_name, rows|
533
+ rows.each do |row|
534
+ conn.insert_fixture(row, fixture_set_name)
535
+ end
536
+ end
537
+ end
538
+
539
+ # Cap primary key sequences to max(pk).
540
+ if connection.respond_to?(:reset_pk_sequence!)
541
+ fixture_sets.each do |fs|
542
+ connection.reset_pk_sequence!(fs.table_name)
543
+ end
544
+ end
545
+ end
546
+
547
+ cache_fixtures(connection, fixtures_map)
548
+ end
549
+ end
550
+ cached_fixtures(connection, fixture_set_names)
551
+ end
552
+
553
+ # Returns a consistent, platform-independent identifier for +label+.
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
561
+ end
562
+
563
+ # Superclass for the evaluation contexts used by ERB fixtures.
564
+ def self.context_class
565
+ @context_class ||= Class.new
566
+ end
567
+
568
+ def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
569
+ all_loaded_fixtures.update(fixtures_map)
570
+ end
571
+
572
+ attr_reader :table_name, :name, :fixtures, :model_class, :config
573
+
574
+ def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
575
+ @name = name
576
+ @path = path
577
+ @config = config
578
+ @model_class = nil
579
+
580
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
581
+ @model_class = class_name
582
+ else
583
+ @model_class = class_name.safe_constantize if class_name
584
+ end
585
+
586
+ @connection = connection
587
+
588
+ @table_name = ( model_class.respond_to?(:table_name) ?
589
+ model_class.table_name :
590
+ self.class.default_fixture_table_name(name, config) )
591
+
592
+ @fixtures = read_fixture_files path, @model_class
593
+ end
594
+
595
+ def [](x)
596
+ fixtures[x]
597
+ end
598
+
599
+ def []=(k,v)
600
+ fixtures[k] = v
601
+ end
602
+
603
+ def each(&block)
604
+ fixtures.each(&block)
605
+ end
606
+
607
+ def size
608
+ fixtures.size
609
+ end
610
+
611
+ # Returns a hash of rows to be inserted. The key is the table, the value is
612
+ # a list of rows to insert to that table.
613
+ def table_rows
614
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
615
+ now = now.to_s(:db)
616
+
617
+ # allow a standard key to be used for doing defaults in YAML
618
+ fixtures.delete('DEFAULTS')
619
+
620
+ # track any join tables we need to insert later
621
+ rows = Hash.new { |h,table| h[table] = [] }
622
+
623
+ rows[table_name] = fixtures.map do |label, fixture|
624
+ row = fixture.to_hash
625
+
626
+ if model_class
627
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
628
+ if model_class.record_timestamps
629
+ timestamp_column_names.each do |c_name|
630
+ row[c_name] = now unless row.key?(c_name)
631
+ end
632
+ end
633
+
634
+ # interpolate the fixture label
635
+ row.each do |key, value|
636
+ row[key] = value.gsub("$LABEL", label) if value.is_a?(String)
637
+ end
638
+
639
+ # generate a primary key if necessary
640
+ if has_primary_key_column? && !row.include?(primary_key_name)
641
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
642
+ end
643
+
644
+ # If STI is used, find the correct subclass for association reflection
645
+ reflection_class =
646
+ if row.include?(inheritance_column_name)
647
+ row[inheritance_column_name].constantize rescue model_class
648
+ else
649
+ model_class
650
+ end
651
+
652
+ reflection_class._reflections.each_value do |association|
653
+ case association.macro
654
+ when :belongs_to
655
+ # Do not replace association name with association foreign key if they are named the same
656
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
657
+
658
+ if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
659
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
660
+ # support polymorphic belongs_to as "label (Type)"
661
+ row[association.foreign_type] = $1
662
+ end
663
+
664
+ fk_type = association.active_record.columns_hash[fk_name].type
665
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
666
+ end
667
+ when :has_many
668
+ if association.options[:through]
669
+ add_join_records(rows, row, HasManyThroughProxy.new(association))
670
+ end
671
+ end
672
+ end
673
+ end
674
+
675
+ row
676
+ end
677
+ rows
678
+ end
679
+
680
+ class ReflectionProxy # :nodoc:
681
+ def initialize(association)
682
+ @association = association
683
+ end
684
+
685
+ def join_table
686
+ @association.join_table
687
+ end
688
+
689
+ def name
690
+ @association.name
691
+ end
692
+
693
+ def primary_key_type
694
+ @association.klass.column_types[@association.klass.primary_key].type
695
+ end
696
+ end
697
+
698
+ class HasManyThroughProxy < ReflectionProxy # :nodoc:
699
+ def rhs_key
700
+ @association.foreign_key
701
+ end
702
+
703
+ def lhs_key
704
+ @association.through_reflection.foreign_key
705
+ end
706
+ end
707
+
708
+ private
709
+ def primary_key_name
710
+ @primary_key_name ||= model_class && model_class.primary_key
711
+ end
712
+
713
+ def primary_key_type
714
+ @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
715
+ end
716
+
717
+ def add_join_records(rows, row, association)
718
+ # This is the case when the join table has no fixtures file
719
+ if (targets = row.delete(association.name.to_s))
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
724
+
725
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
726
+ rows[table_name].concat targets.map { |target|
727
+ { lhs_key => row[primary_key_name],
728
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
729
+ }
730
+ end
731
+ end
732
+
733
+ def has_primary_key_column?
734
+ @has_primary_key_column ||= primary_key_name &&
735
+ model_class.columns.any? { |c| c.name == primary_key_name }
736
+ end
737
+
738
+ def timestamp_column_names
739
+ @timestamp_column_names ||=
740
+ %w(created_at created_on updated_at updated_on) & column_names
741
+ end
742
+
743
+ def inheritance_column_name
744
+ @inheritance_column_name ||= model_class && model_class.inheritance_column
745
+ end
746
+
747
+ def column_names
748
+ @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
749
+ end
750
+
751
+ def read_fixture_files(path, model_class)
752
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
753
+ ::File.file?(f)
754
+ } + [yaml_file_path(path)]
755
+
756
+ yaml_files.each_with_object({}) do |file, fixtures|
757
+ FixtureSet::File.open(file) do |fh|
758
+ fh.each do |fixture_name, row|
759
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
760
+ end
761
+ end
762
+ end
763
+ end
764
+
765
+ def yaml_file_path(path)
766
+ "#{path}.yml"
767
+ end
768
+
769
+ end
770
+
771
+ #--
772
+ # Deprecate 'Fixtures' in favor of 'FixtureSet'.
773
+ #++
774
+ # :nodoc:
775
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
776
+
777
+ class Fixture #:nodoc:
778
+ include Enumerable
779
+
780
+ class FixtureError < StandardError #:nodoc:
781
+ end
782
+
783
+ class FormatError < FixtureError #:nodoc:
784
+ end
785
+
786
+ attr_reader :model_class, :fixture
787
+
788
+ def initialize(fixture, model_class)
789
+ @fixture = fixture
790
+ @model_class = model_class
791
+ end
792
+
793
+ def class_name
794
+ model_class.name if model_class
795
+ end
796
+
797
+ def each
798
+ fixture.each { |item| yield item }
799
+ end
800
+
801
+ def [](key)
802
+ fixture[key]
803
+ end
804
+
805
+ alias :to_hash :fixture
806
+
807
+ def find
808
+ if model_class
809
+ model_class.unscoped do
810
+ model_class.find(fixture[model_class.primary_key])
811
+ end
812
+ else
813
+ raise FixtureClassNotFound, "No class attached to find."
814
+ end
815
+ end
816
+ end
817
+ end
818
+
819
+ module ActiveRecord
820
+ module TestFixtures
821
+ extend ActiveSupport::Concern
822
+
823
+ def before_setup
824
+ setup_fixtures
825
+ super
826
+ end
827
+
828
+ def after_teardown
829
+ super
830
+ teardown_fixtures
831
+ end
832
+
833
+ included do
834
+ class_attribute :fixture_path, :instance_writer => false
835
+ class_attribute :fixture_table_names
836
+ class_attribute :fixture_class_names
837
+ class_attribute :use_transactional_fixtures
838
+ class_attribute :use_instantiated_fixtures # true, false, or :no_instances
839
+ class_attribute :pre_loaded_fixtures
840
+ class_attribute :config
841
+
842
+ self.fixture_table_names = []
843
+ self.use_transactional_fixtures = true
844
+ self.use_instantiated_fixtures = false
845
+ self.pre_loaded_fixtures = false
846
+ self.config = ActiveRecord::Base
847
+
848
+ self.fixture_class_names = Hash.new do |h, fixture_set_name|
849
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name, self.config)
850
+ end
851
+ end
852
+
853
+ module ClassMethods
854
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
855
+ #
856
+ # Examples:
857
+ #
858
+ # set_fixture_class some_fixture: SomeModel,
859
+ # 'namespaced/fixture' => Another::Model
860
+ #
861
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
862
+ def set_fixture_class(class_names = {})
863
+ self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
864
+ end
865
+
866
+ def fixtures(*fixture_set_names)
867
+ if fixture_set_names.first == :all
868
+ fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
869
+ fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
870
+ else
871
+ fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
872
+ end
873
+
874
+ self.fixture_table_names |= fixture_set_names
875
+ setup_fixture_accessors(fixture_set_names)
876
+ end
877
+
878
+ def setup_fixture_accessors(fixture_set_names = nil)
879
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
880
+ methods = Module.new do
881
+ fixture_set_names.each do |fs_name|
882
+ fs_name = fs_name.to_s
883
+ accessor_name = fs_name.tr('/', '_').to_sym
884
+
885
+ define_method(accessor_name) do |*fixture_names|
886
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
887
+
888
+ @fixture_cache[fs_name] ||= {}
889
+
890
+ instances = fixture_names.map do |f_name|
891
+ f_name = f_name.to_s
892
+ @fixture_cache[fs_name].delete(f_name) if force_reload
893
+
894
+ if @loaded_fixtures[fs_name][f_name]
895
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
896
+ else
897
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
898
+ end
899
+ end
900
+
901
+ instances.size == 1 ? instances.first : instances
902
+ end
903
+ private accessor_name
904
+ end
905
+ end
906
+ include methods
907
+ end
908
+
909
+ def uses_transaction(*methods)
910
+ @uses_transaction = [] unless defined?(@uses_transaction)
911
+ @uses_transaction.concat methods.map { |m| m.to_s }
912
+ end
913
+
914
+ def uses_transaction?(method)
915
+ @uses_transaction = [] unless defined?(@uses_transaction)
916
+ @uses_transaction.include?(method.to_s)
917
+ end
918
+ end
919
+
920
+ def run_in_transaction?
921
+ use_transactional_fixtures &&
922
+ !self.class.uses_transaction?(method_name)
923
+ end
924
+
925
+ def setup_fixtures(config = ActiveRecord::Base)
926
+ if pre_loaded_fixtures && !use_transactional_fixtures
927
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
928
+ end
929
+
930
+ @fixture_cache = {}
931
+ @fixture_connections = []
932
+ @@already_loaded_fixtures ||= {}
933
+
934
+ # Load fixtures once and begin transaction.
935
+ if run_in_transaction?
936
+ if @@already_loaded_fixtures[self.class]
937
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
938
+ else
939
+ @loaded_fixtures = load_fixtures(config)
940
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
941
+ end
942
+ @fixture_connections = enlist_fixture_connections
943
+ @fixture_connections.each do |connection|
944
+ connection.begin_transaction joinable: false
945
+ end
946
+ # Load fixtures for every test.
947
+ else
948
+ ActiveRecord::FixtureSet.reset_cache
949
+ @@already_loaded_fixtures[self.class] = nil
950
+ @loaded_fixtures = load_fixtures(config)
951
+ end
952
+
953
+ # Instantiate fixtures for every test if requested.
954
+ instantiate_fixtures if use_instantiated_fixtures
955
+ end
956
+
957
+ def teardown_fixtures
958
+ # Rollback changes if a transaction is active.
959
+ if run_in_transaction?
960
+ @fixture_connections.each do |connection|
961
+ connection.rollback_transaction if connection.transaction_open?
962
+ end
963
+ @fixture_connections.clear
964
+ else
965
+ ActiveRecord::FixtureSet.reset_cache
966
+ end
967
+
968
+ ActiveRecord::Base.clear_active_connections!
969
+ end
970
+
971
+ def enlist_fixture_connections
972
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
973
+ end
974
+
975
+ private
976
+ def load_fixtures(config)
977
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
978
+ Hash[fixtures.map { |f| [f.name, f] }]
979
+ end
980
+
981
+ def instantiate_fixtures
982
+ if pre_loaded_fixtures
983
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
984
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
985
+ else
986
+ raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
987
+ @loaded_fixtures.each_value do |fixture_set|
988
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
989
+ end
990
+ end
991
+ end
992
+
993
+ def load_instances?
994
+ use_instantiated_fixtures != :no_instances
995
+ end
996
+ end
997
+ end
998
+
999
+ class ActiveRecord::FixtureSet::RenderContext # :nodoc:
1000
+ def self.create_subclass
1001
+ Class.new ActiveRecord::FixtureSet.context_class do
1002
+ def get_binding
1003
+ binding()
1004
+ end
1005
+ end
1006
+ end
1007
+ end