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,313 @@
1
+ module ActiveRecord
2
+ # = Active Record Callbacks
3
+ #
4
+ # Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
5
+ # before or after an alteration of the object state. This can be used to make sure that associated and
6
+ # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
7
+ # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
8
+ # the <tt>Base#save</tt> call for a new record:
9
+ #
10
+ # * (-) <tt>save</tt>
11
+ # * (-) <tt>valid</tt>
12
+ # * (1) <tt>before_validation</tt>
13
+ # * (-) <tt>validate</tt>
14
+ # * (2) <tt>after_validation</tt>
15
+ # * (3) <tt>before_save</tt>
16
+ # * (4) <tt>before_create</tt>
17
+ # * (-) <tt>create</tt>
18
+ # * (5) <tt>after_create</tt>
19
+ # * (6) <tt>after_save</tt>
20
+ # * (7) <tt>after_commit</tt>
21
+ #
22
+ # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
23
+ # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
24
+ # <tt>after_rollback</tt>.
25
+ #
26
+ # Additionally, an <tt>after_touch</tt> callback is triggered whenever an
27
+ # object is touched.
28
+ #
29
+ # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
30
+ # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
31
+ # are instantiated as well.
32
+ #
33
+ # There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
34
+ # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
35
+ # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
36
+ #
37
+ # Examples:
38
+ # class CreditCard < ActiveRecord::Base
39
+ # # Strip everything but digits, so the user can specify "555 234 34" or
40
+ # # "5552-3434" and both will mean "55523434"
41
+ # before_validation(on: :create) do
42
+ # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
43
+ # end
44
+ # end
45
+ #
46
+ # class Subscription < ActiveRecord::Base
47
+ # before_create :record_signup
48
+ #
49
+ # private
50
+ # def record_signup
51
+ # self.signed_up_on = Date.today
52
+ # end
53
+ # end
54
+ #
55
+ # class Firm < ActiveRecord::Base
56
+ # # Destroys the associated clients and people when the firm is destroyed
57
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
58
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
59
+ # end
60
+ #
61
+ # == Inheritable callback queues
62
+ #
63
+ # Besides the overwritable callback methods, it's also possible to register callbacks through the
64
+ # use of the callback macros. Their main advantage is that the macros add behavior into a callback
65
+ # queue that is kept intact down through an inheritance hierarchy.
66
+ #
67
+ # class Topic < ActiveRecord::Base
68
+ # before_destroy :destroy_author
69
+ # end
70
+ #
71
+ # class Reply < Topic
72
+ # before_destroy :destroy_readers
73
+ # end
74
+ #
75
+ # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
76
+ # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
77
+ # where the +before_destroy+ method is overridden:
78
+ #
79
+ # class Topic < ActiveRecord::Base
80
+ # def before_destroy() destroy_author end
81
+ # end
82
+ #
83
+ # class Reply < Topic
84
+ # def before_destroy() destroy_readers end
85
+ # end
86
+ #
87
+ # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
88
+ # So, use the callback macros when you want to ensure that a certain callback is called for the entire
89
+ # hierarchy, and use the regular overwritable methods when you want to leave it up to each descendant
90
+ # to decide whether they want to call +super+ and trigger the inherited callbacks.
91
+ #
92
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
93
+ # callbacks before specifying the associations. Otherwise, you might trigger the loading of a
94
+ # child before the parent has registered the callbacks and they won't be inherited.
95
+ #
96
+ # == Types of callbacks
97
+ #
98
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
99
+ # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
100
+ # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
101
+ # creating mix-ins), and inline eval methods are deprecated.
102
+ #
103
+ # The method reference callbacks work by specifying a protected or private method available in the object, like this:
104
+ #
105
+ # class Topic < ActiveRecord::Base
106
+ # before_destroy :delete_parents
107
+ #
108
+ # private
109
+ # def delete_parents
110
+ # self.class.delete_all "parent_id = #{id}"
111
+ # end
112
+ # end
113
+ #
114
+ # The callback objects have methods named after the callback called with the record as the only parameter, such as:
115
+ #
116
+ # class BankAccount < ActiveRecord::Base
117
+ # before_save EncryptionWrapper.new
118
+ # after_save EncryptionWrapper.new
119
+ # after_initialize EncryptionWrapper.new
120
+ # end
121
+ #
122
+ # class EncryptionWrapper
123
+ # def before_save(record)
124
+ # record.credit_card_number = encrypt(record.credit_card_number)
125
+ # end
126
+ #
127
+ # def after_save(record)
128
+ # record.credit_card_number = decrypt(record.credit_card_number)
129
+ # end
130
+ #
131
+ # alias_method :after_initialize, :after_save
132
+ #
133
+ # private
134
+ # def encrypt(value)
135
+ # # Secrecy is committed
136
+ # end
137
+ #
138
+ # def decrypt(value)
139
+ # # Secrecy is unveiled
140
+ # end
141
+ # end
142
+ #
143
+ # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
144
+ # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
145
+ # initialization data such as the name of the attribute to work with:
146
+ #
147
+ # class BankAccount < ActiveRecord::Base
148
+ # before_save EncryptionWrapper.new("credit_card_number")
149
+ # after_save EncryptionWrapper.new("credit_card_number")
150
+ # after_initialize EncryptionWrapper.new("credit_card_number")
151
+ # end
152
+ #
153
+ # class EncryptionWrapper
154
+ # def initialize(attribute)
155
+ # @attribute = attribute
156
+ # end
157
+ #
158
+ # def before_save(record)
159
+ # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
160
+ # end
161
+ #
162
+ # def after_save(record)
163
+ # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
164
+ # end
165
+ #
166
+ # alias_method :after_initialize, :after_save
167
+ #
168
+ # private
169
+ # def encrypt(value)
170
+ # # Secrecy is committed
171
+ # end
172
+ #
173
+ # def decrypt(value)
174
+ # # Secrecy is unveiled
175
+ # end
176
+ # end
177
+ #
178
+ # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
179
+ # pass a "method string", which will then be evaluated within the binding of the callback. Example:
180
+ #
181
+ # class Topic < ActiveRecord::Base
182
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"'
183
+ # end
184
+ #
185
+ # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
186
+ # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
187
+ #
188
+ # class Topic < ActiveRecord::Base
189
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
190
+ # 'puts "Evaluated after parents are destroyed"'
191
+ # end
192
+ #
193
+ # == <tt>before_validation*</tt> returning statements
194
+ #
195
+ # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
196
+ # aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
197
+ # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
198
+ #
199
+ # == Canceling callbacks
200
+ #
201
+ # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
202
+ # cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
203
+ # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
204
+ # methods on the model, which are called last.
205
+ #
206
+ # == Ordering callbacks
207
+ #
208
+ # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
209
+ # callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
210
+ #
211
+ # Let's look at the code below:
212
+ #
213
+ # class Topic < ActiveRecord::Base
214
+ # has_many :children, dependent: destroy
215
+ #
216
+ # before_destroy :log_children
217
+ #
218
+ # private
219
+ # def log_children
220
+ # # Child processing
221
+ # end
222
+ # end
223
+ #
224
+ # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
225
+ # because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
226
+ #
227
+ # class Topic < ActiveRecord::Base
228
+ # has_many :children, dependent: destroy
229
+ #
230
+ # before_destroy :log_children, prepend: true
231
+ #
232
+ # private
233
+ # def log_children
234
+ # # Child processing
235
+ # end
236
+ # end
237
+ #
238
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
239
+ #
240
+ # == Transactions
241
+ #
242
+ # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
243
+ # within a transaction. That includes <tt>after_*</tt> hooks. If everything
244
+ # goes fine a COMMIT is executed once the chain has been completed.
245
+ #
246
+ # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
247
+ # can also trigger a ROLLBACK raising an exception in any of the callbacks,
248
+ # including <tt>after_*</tt> hooks. Note, however, that in that case the client
249
+ # needs to be aware of it because an ordinary +save+ will raise such exception
250
+ # instead of quietly returning +false+.
251
+ #
252
+ # == Debugging callbacks
253
+ #
254
+ # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
255
+ # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
256
+ # defines what part of the chain the callback runs in.
257
+ #
258
+ # To find all callbacks in the before_save callback chain:
259
+ #
260
+ # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
261
+ #
262
+ # Returns an array of callback objects that form the before_save chain.
263
+ #
264
+ # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
265
+ #
266
+ # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
267
+ #
268
+ # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
269
+ #
270
+ module Callbacks
271
+ extend ActiveSupport::Concern
272
+
273
+ CALLBACKS = [
274
+ :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
275
+ :before_save, :around_save, :after_save, :before_create, :around_create,
276
+ :after_create, :before_update, :around_update, :after_update,
277
+ :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
278
+ ]
279
+
280
+ module ClassMethods
281
+ include ActiveModel::Callbacks
282
+ end
283
+
284
+ included do
285
+ include ActiveModel::Validations::Callbacks
286
+
287
+ define_model_callbacks :initialize, :find, :touch, :only => :after
288
+ define_model_callbacks :save, :create, :update, :destroy
289
+ end
290
+
291
+ def destroy #:nodoc:
292
+ _run_destroy_callbacks { super }
293
+ end
294
+
295
+ def touch(*) #:nodoc:
296
+ _run_touch_callbacks { super }
297
+ end
298
+
299
+ private
300
+
301
+ def create_or_update #:nodoc:
302
+ _run_save_callbacks { super }
303
+ end
304
+
305
+ def _create_record #:nodoc:
306
+ _run_create_callbacks { super }
307
+ end
308
+
309
+ def _update_record(*) #:nodoc:
310
+ _run_update_callbacks { super }
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module Coders # :nodoc:
3
+ class JSON # :nodoc:
4
+ def self.dump(obj)
5
+ ActiveSupport::JSON.encode(obj)
6
+ end
7
+
8
+ def self.load(json)
9
+ ActiveSupport::JSON.decode(json) unless json.nil?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ require 'yaml'
2
+
3
+ module ActiveRecord
4
+ module Coders # :nodoc:
5
+ class YAMLColumn # :nodoc:
6
+
7
+ attr_accessor :object_class
8
+
9
+ def initialize(object_class = Object)
10
+ @object_class = object_class
11
+ end
12
+
13
+ def dump(obj)
14
+ return if obj.nil?
15
+
16
+ unless obj.is_a?(object_class)
17
+ raise SerializationTypeMismatch,
18
+ "Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
19
+ end
20
+ YAML.dump obj
21
+ end
22
+
23
+ def load(yaml)
24
+ return object_class.new if object_class != Object && yaml.nil?
25
+ return yaml unless yaml.is_a?(String) && yaml =~ /^---/
26
+ obj = YAML.load(yaml)
27
+
28
+ unless obj.is_a?(object_class) || obj.nil?
29
+ raise SerializationTypeMismatch,
30
+ "Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
31
+ end
32
+ obj ||= object_class.new if object_class != Object
33
+
34
+ obj
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,659 @@
1
+ require 'thread'
2
+ require 'thread_safe'
3
+ require 'monitor'
4
+ require 'set'
5
+ require 'active_support/core_ext/string/filters'
6
+
7
+ module ActiveRecord
8
+ # Raised when a connection could not be obtained within the connection
9
+ # acquisition timeout period: because max connections in pool
10
+ # are in use.
11
+ class ConnectionTimeoutError < ConnectionNotEstablished
12
+ end
13
+
14
+ module ConnectionAdapters
15
+ # Connection pool base class for managing Active Record database
16
+ # connections.
17
+ #
18
+ # == Introduction
19
+ #
20
+ # A connection pool synchronizes thread access to a limited number of
21
+ # database connections. The basic idea is that each thread checks out a
22
+ # database connection from the pool, uses that connection, and checks the
23
+ # connection back in. ConnectionPool is completely thread-safe, and will
24
+ # ensure that a connection cannot be used by two threads at the same time,
25
+ # as long as ConnectionPool's contract is correctly followed. It will also
26
+ # handle cases in which there are more threads than connections: if all
27
+ # connections have been checked out, and a thread tries to checkout a
28
+ # connection anyway, then ConnectionPool will wait until some other thread
29
+ # has checked in a connection.
30
+ #
31
+ # == Obtaining (checking out) a connection
32
+ #
33
+ # Connections can be obtained and used from a connection pool in several
34
+ # ways:
35
+ #
36
+ # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
37
+ # earlier (pre-connection-pooling). Eventually, when you're done with
38
+ # the connection(s) and wish it to be returned to the pool, you call
39
+ # ActiveRecord::Base.clear_active_connections!. This will be the
40
+ # default behavior for Active Record when used in conjunction with
41
+ # Action Pack's request handling cycle.
42
+ # 2. Manually check out a connection from the pool with
43
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
44
+ # returning this connection to the pool when finished by calling
45
+ # ActiveRecord::Base.connection_pool.checkin(connection).
46
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
47
+ # obtains a connection, yields it as the sole argument to the block,
48
+ # and returns it to the pool after the block completes.
49
+ #
50
+ # Connections in the pool are actually AbstractAdapter objects (or objects
51
+ # compatible with AbstractAdapter's interface).
52
+ #
53
+ # == Options
54
+ #
55
+ # There are several connection-pooling-related options that you can add to
56
+ # your database connection configuration:
57
+ #
58
+ # * +pool+: number indicating size of connection pool (default 5)
59
+ # * +checkout_timeout+: number of seconds to block and wait for a connection
60
+ # before giving up and raising a timeout error (default 5 seconds).
61
+ # * +reaping_frequency+: frequency in seconds to periodically run the
62
+ # Reaper, which attempts to find and recover connections from dead
63
+ # threads, which can occur if a programmer forgets to close a
64
+ # connection at the end of a thread or a thread dies unexpectedly.
65
+ # Regardless of this setting, the Reaper will be invoked before every
66
+ # blocking wait. (Default nil, which means don't schedule the Reaper).
67
+ class ConnectionPool
68
+ # Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
69
+ # with which it shares a Monitor. But could be a generic Queue.
70
+ #
71
+ # The Queue in stdlib's 'thread' could replace this class except
72
+ # stdlib's doesn't support waiting with a timeout.
73
+ class Queue
74
+ def initialize(lock = Monitor.new)
75
+ @lock = lock
76
+ @cond = @lock.new_cond
77
+ @num_waiting = 0
78
+ @queue = []
79
+ end
80
+
81
+ # Test if any threads are currently waiting on the queue.
82
+ def any_waiting?
83
+ synchronize do
84
+ @num_waiting > 0
85
+ end
86
+ end
87
+
88
+ # Returns the number of threads currently waiting on this
89
+ # queue.
90
+ def num_waiting
91
+ synchronize do
92
+ @num_waiting
93
+ end
94
+ end
95
+
96
+ # Add +element+ to the queue. Never blocks.
97
+ def add(element)
98
+ synchronize do
99
+ @queue.push element
100
+ @cond.signal
101
+ end
102
+ end
103
+
104
+ # If +element+ is in the queue, remove and return it, or nil.
105
+ def delete(element)
106
+ synchronize do
107
+ @queue.delete(element)
108
+ end
109
+ end
110
+
111
+ # Remove all elements from the queue.
112
+ def clear
113
+ synchronize do
114
+ @queue.clear
115
+ end
116
+ end
117
+
118
+ # Remove the head of the queue.
119
+ #
120
+ # If +timeout+ is not given, remove and return the head the
121
+ # queue if the number of available elements is strictly
122
+ # greater than the number of threads currently waiting (that
123
+ # is, don't jump ahead in line). Otherwise, return nil.
124
+ #
125
+ # If +timeout+ is given, block if it there is no element
126
+ # available, waiting up to +timeout+ seconds for an element to
127
+ # become available.
128
+ #
129
+ # Raises:
130
+ # - ConnectionTimeoutError if +timeout+ is given and no element
131
+ # becomes available after +timeout+ seconds,
132
+ def poll(timeout = nil)
133
+ synchronize do
134
+ if timeout
135
+ no_wait_poll || wait_poll(timeout)
136
+ else
137
+ no_wait_poll
138
+ end
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def synchronize(&block)
145
+ @lock.synchronize(&block)
146
+ end
147
+
148
+ # Test if the queue currently contains any elements.
149
+ def any?
150
+ !@queue.empty?
151
+ end
152
+
153
+ # A thread can remove an element from the queue without
154
+ # waiting if an only if the number of currently available
155
+ # connections is strictly greater than the number of waiting
156
+ # threads.
157
+ def can_remove_no_wait?
158
+ @queue.size > @num_waiting
159
+ end
160
+
161
+ # Removes and returns the head of the queue if possible, or nil.
162
+ def remove
163
+ @queue.shift
164
+ end
165
+
166
+ # Remove and return the head the queue if the number of
167
+ # available elements is strictly greater than the number of
168
+ # threads currently waiting. Otherwise, return nil.
169
+ def no_wait_poll
170
+ remove if can_remove_no_wait?
171
+ end
172
+
173
+ # Waits on the queue up to +timeout+ seconds, then removes and
174
+ # returns the head of the queue.
175
+ def wait_poll(timeout)
176
+ @num_waiting += 1
177
+
178
+ t0 = Time.now
179
+ elapsed = 0
180
+ loop do
181
+ @cond.wait(timeout - elapsed)
182
+
183
+ return remove if any?
184
+
185
+ elapsed = Time.now - t0
186
+ if elapsed >= timeout
187
+ msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
188
+ [timeout, elapsed]
189
+ raise ConnectionTimeoutError, msg
190
+ end
191
+ end
192
+ ensure
193
+ @num_waiting -= 1
194
+ end
195
+ end
196
+
197
+ # Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
198
+ # A reaper instantiated with a nil frequency will never reap the
199
+ # connection pool.
200
+ #
201
+ # Configure the frequency by setting "reaping_frequency" in your
202
+ # database yaml file.
203
+ class Reaper
204
+ attr_reader :pool, :frequency
205
+
206
+ def initialize(pool, frequency)
207
+ @pool = pool
208
+ @frequency = frequency
209
+ end
210
+
211
+ def run
212
+ return unless frequency
213
+ Thread.new(frequency, pool) { |t, p|
214
+ while true
215
+ sleep t
216
+ p.reap
217
+ end
218
+ }
219
+ end
220
+ end
221
+
222
+ include MonitorMixin
223
+
224
+ attr_accessor :automatic_reconnect, :checkout_timeout
225
+ attr_reader :spec, :connections, :size, :reaper
226
+
227
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
228
+ # object which describes database connection information (e.g. adapter,
229
+ # host name, username, password, etc), as well as the maximum size for
230
+ # this ConnectionPool.
231
+ #
232
+ # The default ConnectionPool maximum size is 5.
233
+ def initialize(spec)
234
+ super()
235
+
236
+ @spec = spec
237
+
238
+ @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
239
+ @reaper = Reaper.new self, spec.config[:reaping_frequency]
240
+ @reaper.run
241
+
242
+ # default max pool size to 5
243
+ @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
244
+
245
+ # The cache of reserved connections mapped to threads
246
+ @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
247
+
248
+ @connections = []
249
+ @automatic_reconnect = true
250
+
251
+ @available = Queue.new self
252
+ end
253
+
254
+ # Retrieve the connection associated with the current thread, or call
255
+ # #checkout to obtain one if necessary.
256
+ #
257
+ # #connection can be called any number of times; the connection is
258
+ # held in a hash keyed by the thread id.
259
+ def connection
260
+ # this is correctly done double-checked locking
261
+ # (ThreadSafe::Cache's lookups have volatile semantics)
262
+ @reserved_connections[current_connection_id] || synchronize do
263
+ @reserved_connections[current_connection_id] ||= checkout
264
+ end
265
+ end
266
+
267
+ # Is there an open connection that is being used for the current thread?
268
+ def active_connection?
269
+ synchronize do
270
+ @reserved_connections.fetch(current_connection_id) {
271
+ return false
272
+ }.in_use?
273
+ end
274
+ end
275
+
276
+ # Signal that the thread is finished with the current connection.
277
+ # #release_connection releases the connection-thread association
278
+ # and returns the connection to the pool.
279
+ def release_connection(with_id = current_connection_id)
280
+ synchronize do
281
+ conn = @reserved_connections.delete(with_id)
282
+ checkin conn if conn
283
+ end
284
+ end
285
+
286
+ # If a connection already exists yield it to the block. If no connection
287
+ # exists checkout a connection, yield it to the block, and checkin the
288
+ # connection when finished.
289
+ def with_connection
290
+ connection_id = current_connection_id
291
+ fresh_connection = true unless active_connection?
292
+ yield connection
293
+ ensure
294
+ release_connection(connection_id) if fresh_connection
295
+ end
296
+
297
+ # Returns true if a connection has already been opened.
298
+ def connected?
299
+ synchronize { @connections.any? }
300
+ end
301
+
302
+ # Disconnects all connections in the pool, and clears the pool.
303
+ def disconnect!
304
+ synchronize do
305
+ @reserved_connections.clear
306
+ @connections.each do |conn|
307
+ checkin conn
308
+ conn.disconnect!
309
+ end
310
+ @connections = []
311
+ @available.clear
312
+ end
313
+ end
314
+
315
+ # Clears the cache which maps classes.
316
+ def clear_reloadable_connections!
317
+ synchronize do
318
+ @reserved_connections.clear
319
+ @connections.each do |conn|
320
+ checkin conn
321
+ conn.disconnect! if conn.requires_reloading?
322
+ end
323
+ @connections.delete_if do |conn|
324
+ conn.requires_reloading?
325
+ end
326
+ @available.clear
327
+ @connections.each do |conn|
328
+ @available.add conn
329
+ end
330
+ end
331
+ end
332
+
333
+ # Check-out a database connection from the pool, indicating that you want
334
+ # to use it. You should call #checkin when you no longer need this.
335
+ #
336
+ # This is done by either returning and leasing existing connection, or by
337
+ # creating a new connection and leasing it.
338
+ #
339
+ # If all connections are leased and the pool is at capacity (meaning the
340
+ # number of currently leased connections is greater than or equal to the
341
+ # size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
342
+ #
343
+ # Returns: an AbstractAdapter object.
344
+ #
345
+ # Raises:
346
+ # - ConnectionTimeoutError: no connection can be obtained from the pool.
347
+ def checkout
348
+ synchronize do
349
+ conn = acquire_connection
350
+ conn.lease
351
+ checkout_and_verify(conn)
352
+ end
353
+ end
354
+
355
+ # Check-in a database connection back into the pool, indicating that you
356
+ # no longer need this connection.
357
+ #
358
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
359
+ # calling +checkout+ on this pool.
360
+ def checkin(conn)
361
+ synchronize do
362
+ owner = conn.owner
363
+
364
+ conn._run_checkin_callbacks do
365
+ conn.expire
366
+ end
367
+
368
+ release owner
369
+
370
+ @available.add conn
371
+ end
372
+ end
373
+
374
+ # Remove a connection from the connection pool. The connection will
375
+ # remain open and active but will no longer be managed by this pool.
376
+ def remove(conn)
377
+ synchronize do
378
+ @connections.delete conn
379
+ @available.delete conn
380
+
381
+ release conn.owner
382
+
383
+ @available.add checkout_new_connection if @available.any_waiting?
384
+ end
385
+ end
386
+
387
+ # Recover lost connections for the pool. A lost connection can occur if
388
+ # a programmer forgets to checkin a connection at the end of a thread
389
+ # or a thread dies unexpectedly.
390
+ def reap
391
+ stale_connections = synchronize do
392
+ @connections.select do |conn|
393
+ conn.in_use? && !conn.owner.alive?
394
+ end
395
+ end
396
+
397
+ stale_connections.each do |conn|
398
+ synchronize do
399
+ if conn.active?
400
+ conn.reset!
401
+ checkin conn
402
+ else
403
+ remove conn
404
+ end
405
+ end
406
+ end
407
+ end
408
+
409
+ private
410
+
411
+ # Acquire a connection by one of 1) immediately removing one
412
+ # from the queue of available connections, 2) creating a new
413
+ # connection if the pool is not at capacity, 3) waiting on the
414
+ # queue for a connection to become available.
415
+ #
416
+ # Raises:
417
+ # - ConnectionTimeoutError if a connection could not be acquired
418
+ def acquire_connection
419
+ if conn = @available.poll
420
+ conn
421
+ elsif @connections.size < @size
422
+ checkout_new_connection
423
+ else
424
+ reap
425
+ @available.poll(@checkout_timeout)
426
+ end
427
+ end
428
+
429
+ def release(owner)
430
+ thread_id = owner.object_id
431
+
432
+ @reserved_connections.delete thread_id
433
+ end
434
+
435
+ def new_connection
436
+ Base.send(spec.adapter_method, spec.config)
437
+ end
438
+
439
+ def current_connection_id #:nodoc:
440
+ Base.connection_id ||= Thread.current.object_id
441
+ end
442
+
443
+ def checkout_new_connection
444
+ raise ConnectionNotEstablished unless @automatic_reconnect
445
+
446
+ c = new_connection
447
+ c.pool = self
448
+ @connections << c
449
+ c
450
+ end
451
+
452
+ def checkout_and_verify(c)
453
+ c._run_checkout_callbacks do
454
+ c.verify!
455
+ end
456
+ c
457
+ end
458
+ end
459
+
460
+ # ConnectionHandler is a collection of ConnectionPool objects. It is used
461
+ # for keeping separate connection pools for Active Record models that connect
462
+ # to different databases.
463
+ #
464
+ # For example, suppose that you have 5 models, with the following hierarchy:
465
+ #
466
+ # class Author < ActiveRecord::Base
467
+ # end
468
+ #
469
+ # class BankAccount < ActiveRecord::Base
470
+ # end
471
+ #
472
+ # class Book < ActiveRecord::Base
473
+ # establish_connection "library_db"
474
+ # end
475
+ #
476
+ # class ScaryBook < Book
477
+ # end
478
+ #
479
+ # class GoodBook < Book
480
+ # end
481
+ #
482
+ # And a database.yml that looked like this:
483
+ #
484
+ # development:
485
+ # database: my_application
486
+ # host: localhost
487
+ #
488
+ # library_db:
489
+ # database: library
490
+ # host: some.library.org
491
+ #
492
+ # Your primary database in the development environment is "my_application"
493
+ # but the Book model connects to a separate database called "library_db"
494
+ # (this can even be a database on a different machine).
495
+ #
496
+ # Book, ScaryBook and GoodBook will all use the same connection pool to
497
+ # "library_db" while Author, BankAccount, and any other models you create
498
+ # will use the default connection pool to "my_application".
499
+ #
500
+ # The various connection pools are managed by a single instance of
501
+ # ConnectionHandler accessible via ActiveRecord::Base.connection_handler.
502
+ # All Active Record models use this handler to determine the connection pool that they
503
+ # should use.
504
+ class ConnectionHandler
505
+ def initialize
506
+ # These caches are keyed by klass.name, NOT klass. Keying them by klass
507
+ # alone would lead to memory leaks in development mode as all previous
508
+ # instances of the class would stay in memory.
509
+ @owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
510
+ h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
511
+ end
512
+ @class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
513
+ h[k] = ThreadSafe::Cache.new
514
+ end
515
+ end
516
+
517
+ def connection_pool_list
518
+ owner_to_pool.values.compact
519
+ end
520
+
521
+ def connection_pools
522
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
523
+ In the next release, this will return the same as `#connection_pool_list`.
524
+ (An array of pools, rather than a hash mapping specs to pools.)
525
+ MSG
526
+
527
+ Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
528
+ end
529
+
530
+ def establish_connection(owner, spec)
531
+ @class_to_pool.clear
532
+ raise RuntimeError, "Anonymous class is not allowed." unless owner.name
533
+ owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
534
+ end
535
+
536
+ # Returns true if there are any active connections among the connection
537
+ # pools that the ConnectionHandler is managing.
538
+ def active_connections?
539
+ connection_pool_list.any?(&:active_connection?)
540
+ end
541
+
542
+ # Returns any connections in use by the current thread back to the pool,
543
+ # and also returns connections to the pool cached by threads that are no
544
+ # longer alive.
545
+ def clear_active_connections!
546
+ connection_pool_list.each(&:release_connection)
547
+ end
548
+
549
+ # Clears the cache which maps classes.
550
+ def clear_reloadable_connections!
551
+ connection_pool_list.each(&:clear_reloadable_connections!)
552
+ end
553
+
554
+ def clear_all_connections!
555
+ connection_pool_list.each(&:disconnect!)
556
+ end
557
+
558
+ # Locate the connection of the nearest super class. This can be an
559
+ # active or defined connection: if it is the latter, it will be
560
+ # opened and set as the active connection for the class it was defined
561
+ # for (not necessarily the current class).
562
+ def retrieve_connection(klass) #:nodoc:
563
+ pool = retrieve_connection_pool(klass)
564
+ raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
565
+ conn = pool.connection
566
+ raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
567
+ conn
568
+ end
569
+
570
+ # Returns true if a connection that's accessible to this class has
571
+ # already been opened.
572
+ def connected?(klass)
573
+ conn = retrieve_connection_pool(klass)
574
+ conn && conn.connected?
575
+ end
576
+
577
+ # Remove the connection for this class. This will close the active
578
+ # connection and the defined connection (if they exist). The result
579
+ # can be used as an argument for establish_connection, for easily
580
+ # re-establishing the connection.
581
+ def remove_connection(owner)
582
+ if pool = owner_to_pool.delete(owner.name)
583
+ @class_to_pool.clear
584
+ pool.automatic_reconnect = false
585
+ pool.disconnect!
586
+ pool.spec.config
587
+ end
588
+ end
589
+
590
+ # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
591
+ # This makes retrieving the connection pool O(1) once the process is warm.
592
+ # When a connection is established or removed, we invalidate the cache.
593
+ #
594
+ # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
595
+ # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
596
+ # #fetch is significantly slower than #[]. So in the nil case, no caching will
597
+ # take place, but that's ok since the nil case is not the common one that we wish
598
+ # to optimise for.
599
+ def retrieve_connection_pool(klass)
600
+ class_to_pool[klass.name] ||= begin
601
+ until pool = pool_for(klass)
602
+ klass = klass.superclass
603
+ break unless klass <= Base
604
+ end
605
+
606
+ class_to_pool[klass.name] = pool
607
+ end
608
+ end
609
+
610
+ private
611
+
612
+ def owner_to_pool
613
+ @owner_to_pool[Process.pid]
614
+ end
615
+
616
+ def class_to_pool
617
+ @class_to_pool[Process.pid]
618
+ end
619
+
620
+ def pool_for(owner)
621
+ owner_to_pool.fetch(owner.name) {
622
+ if ancestor_pool = pool_from_any_process_for(owner)
623
+ # A connection was established in an ancestor process that must have
624
+ # subsequently forked. We can't reuse the connection, but we can copy
625
+ # the specification and establish a new connection with it.
626
+ establish_connection owner, ancestor_pool.spec
627
+ else
628
+ owner_to_pool[owner.name] = nil
629
+ end
630
+ }
631
+ end
632
+
633
+ def pool_from_any_process_for(owner)
634
+ owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
635
+ owner_to_pool && owner_to_pool[owner.name]
636
+ end
637
+ end
638
+
639
+ class ConnectionManagement
640
+ def initialize(app)
641
+ @app = app
642
+ end
643
+
644
+ def call(env)
645
+ testing = env['rack.test']
646
+
647
+ response = @app.call(env)
648
+ response[2] = ::Rack::BodyProxy.new(response[2]) do
649
+ ActiveRecord::Base.clear_active_connections! unless testing
650
+ end
651
+
652
+ response
653
+ rescue Exception
654
+ ActiveRecord::Base.clear_active_connections! unless testing
655
+ raise
656
+ end
657
+ end
658
+ end
659
+ end