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,205 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ module ActiveRecord
4
+ # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
5
+ # It's like a simple key/value store baked into your record when you don't care about being able to
6
+ # query that store outside the context of a single record.
7
+ #
8
+ # You can then declare accessors to this store that are then accessible just like any other attribute
9
+ # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
10
+ # already built around just accessing attributes on the model.
11
+ #
12
+ # Make sure that you declare the database column used for the serialized store as a text, so there's
13
+ # plenty of room.
14
+ #
15
+ # You can set custom coder to encode/decode your serialized attributes to/from different formats.
16
+ # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
17
+ #
18
+ # NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
19
+ # the serialization provided by +store+. Simply use +store_accessor+ instead to generate
20
+ # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
21
+ # using a symbol.
22
+ #
23
+ # Examples:
24
+ #
25
+ # class User < ActiveRecord::Base
26
+ # store :settings, accessors: [ :color, :homepage ], coder: JSON
27
+ # end
28
+ #
29
+ # u = User.new(color: 'black', homepage: '37signals.com')
30
+ # u.color # Accessor stored attribute
31
+ # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
32
+ #
33
+ # # There is no difference between strings and symbols for accessing custom attributes
34
+ # u.settings[:country] # => 'Denmark'
35
+ # u.settings['country'] # => 'Denmark'
36
+ #
37
+ # # Add additional accessors to an existing store through store_accessor
38
+ # class SuperUser < User
39
+ # store_accessor :settings, :privileges, :servants
40
+ # end
41
+ #
42
+ # The stored attribute names can be retrieved using +stored_attributes+.
43
+ #
44
+ # User.stored_attributes[:settings] # [:color, :homepage]
45
+ #
46
+ # == Overwriting default accessors
47
+ #
48
+ # All stored values are automatically available through accessors on the Active Record
49
+ # object, but sometimes you want to specialize this behavior. This can be done by overwriting
50
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
51
+ # to actually change things.
52
+ #
53
+ # class Song < ActiveRecord::Base
54
+ # # Uses a stored integer to hold the volume adjustment of the song
55
+ # store :settings, accessors: [:volume_adjustment]
56
+ #
57
+ # def volume_adjustment=(decibels)
58
+ # super(decibels.to_i)
59
+ # end
60
+ #
61
+ # def volume_adjustment
62
+ # super.to_i
63
+ # end
64
+ # end
65
+ module Store
66
+ extend ActiveSupport::Concern
67
+
68
+ included do
69
+ class << self
70
+ attr_accessor :local_stored_attributes
71
+ end
72
+ end
73
+
74
+ module ClassMethods
75
+ def store(store_attribute, options = {})
76
+ serialize store_attribute, IndifferentCoder.new(options[:coder])
77
+ store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
78
+ end
79
+
80
+ def store_accessor(store_attribute, *keys)
81
+ keys = keys.flatten
82
+
83
+ _store_accessors_module.module_eval do
84
+ keys.each do |key|
85
+ define_method("#{key}=") do |value|
86
+ write_store_attribute(store_attribute, key, value)
87
+ end
88
+
89
+ define_method(key) do
90
+ read_store_attribute(store_attribute, key)
91
+ end
92
+ end
93
+ end
94
+
95
+ # assign new store attribute and create new hash to ensure that each class in the hierarchy
96
+ # has its own hash of stored attributes.
97
+ self.local_stored_attributes ||= {}
98
+ self.local_stored_attributes[store_attribute] ||= []
99
+ self.local_stored_attributes[store_attribute] |= keys
100
+ end
101
+
102
+ def _store_accessors_module # :nodoc:
103
+ @_store_accessors_module ||= begin
104
+ mod = Module.new
105
+ include mod
106
+ mod
107
+ end
108
+ end
109
+
110
+ def stored_attributes
111
+ parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
112
+ if self.local_stored_attributes
113
+ parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
114
+ end
115
+ parent
116
+ end
117
+ end
118
+
119
+ protected
120
+ def read_store_attribute(store_attribute, key)
121
+ accessor = store_accessor_for(store_attribute)
122
+ accessor.read(self, store_attribute, key)
123
+ end
124
+
125
+ def write_store_attribute(store_attribute, key, value)
126
+ accessor = store_accessor_for(store_attribute)
127
+ accessor.write(self, store_attribute, key, value)
128
+ end
129
+
130
+ private
131
+ def store_accessor_for(store_attribute)
132
+ type_for_attribute(store_attribute.to_s).accessor
133
+ end
134
+
135
+ class HashAccessor # :nodoc:
136
+ def self.read(object, attribute, key)
137
+ prepare(object, attribute)
138
+ object.public_send(attribute)[key]
139
+ end
140
+
141
+ def self.write(object, attribute, key, value)
142
+ prepare(object, attribute)
143
+ if value != read(object, attribute, key)
144
+ object.public_send :"#{attribute}_will_change!"
145
+ object.public_send(attribute)[key] = value
146
+ end
147
+ end
148
+
149
+ def self.prepare(object, attribute)
150
+ object.public_send :"#{attribute}=", {} unless object.send(attribute)
151
+ end
152
+ end
153
+
154
+ class StringKeyedHashAccessor < HashAccessor # :nodoc:
155
+ def self.read(object, attribute, key)
156
+ super object, attribute, key.to_s
157
+ end
158
+
159
+ def self.write(object, attribute, key, value)
160
+ super object, attribute, key.to_s, value
161
+ end
162
+ end
163
+
164
+ class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
165
+ def self.prepare(object, store_attribute)
166
+ attribute = object.send(store_attribute)
167
+ unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
168
+ attribute = IndifferentCoder.as_indifferent_hash(attribute)
169
+ object.send :"#{store_attribute}=", attribute
170
+ end
171
+ attribute
172
+ end
173
+ end
174
+
175
+ class IndifferentCoder # :nodoc:
176
+ def initialize(coder_or_class_name)
177
+ @coder =
178
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
179
+ coder_or_class_name
180
+ else
181
+ ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
182
+ end
183
+ end
184
+
185
+ def dump(obj)
186
+ @coder.dump self.class.as_indifferent_hash(obj)
187
+ end
188
+
189
+ def load(yaml)
190
+ self.class.as_indifferent_hash(@coder.load(yaml || ''))
191
+ end
192
+
193
+ def self.as_indifferent_hash(obj)
194
+ case obj
195
+ when ActiveSupport::HashWithIndifferentAccess
196
+ obj
197
+ when Hash
198
+ obj.with_indifferent_access
199
+ else
200
+ ActiveSupport::HashWithIndifferentAccess.new
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,296 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class DatabaseAlreadyExists < StandardError; end # :nodoc:
6
+ class DatabaseNotSupported < StandardError; end # :nodoc:
7
+
8
+ # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
9
+ # logic behind common tasks used to manage database and migrations.
10
+ #
11
+ # The tasks defined here are used with Rake tasks provided by Active Record.
12
+ #
13
+ # In order to use DatabaseTasks, a few config values need to be set. All the needed
14
+ # config values are set by Rails already, so it's necessary to do it only if you
15
+ # want to change the defaults or when you want to use Active Record outside of Rails
16
+ # (in such case after configuring the database tasks, you can also use the rake tasks
17
+ # defined in Active Record).
18
+ #
19
+ # The possible config values are:
20
+ #
21
+ # * +env+: current environment (like Rails.env).
22
+ # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
23
+ # * +db_dir+: your +db+ directory.
24
+ # * +fixtures_path+: a path to fixtures directory.
25
+ # * +migrations_paths+: a list of paths to directories with migrations.
26
+ # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
27
+ # * +root+: a path to the root of the application.
28
+ #
29
+ # Example usage of +DatabaseTasks+ outside Rails could look as such:
30
+ #
31
+ # include ActiveRecord::Tasks
32
+ # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
33
+ # DatabaseTasks.db_dir = 'db'
34
+ # # other settings...
35
+ #
36
+ # DatabaseTasks.create_current('production')
37
+ module DatabaseTasks
38
+ extend self
39
+
40
+ attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
41
+ attr_accessor :database_configuration
42
+
43
+ LOCAL_HOSTS = ['127.0.0.1', 'localhost']
44
+
45
+ def register_task(pattern, task)
46
+ @tasks ||= {}
47
+ @tasks[pattern] = task
48
+ end
49
+
50
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
51
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
52
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
53
+
54
+ def db_dir
55
+ @db_dir ||= Rails.application.config.paths["db"].first
56
+ end
57
+
58
+ def migrations_paths
59
+ @migrations_paths ||= Rails.application.paths['db/migrate'].to_a
60
+ end
61
+
62
+ def fixtures_path
63
+ @fixtures_path ||= if ENV['FIXTURES_PATH']
64
+ File.join(root, ENV['FIXTURES_PATH'])
65
+ else
66
+ File.join(root, 'test', 'fixtures')
67
+ end
68
+ end
69
+
70
+ def root
71
+ @root ||= Rails.root
72
+ end
73
+
74
+ def env
75
+ @env ||= Rails.env
76
+ end
77
+
78
+ def seed_loader
79
+ @seed_loader ||= Rails.application
80
+ end
81
+
82
+ def current_config(options = {})
83
+ options.reverse_merge! :env => env
84
+ if options.has_key?(:config)
85
+ @current_config = options[:config]
86
+ else
87
+ @current_config ||= ActiveRecord::Base.configurations[options[:env]]
88
+ end
89
+ end
90
+
91
+ def create(*arguments)
92
+ configuration = arguments.first
93
+ class_for_adapter(configuration['adapter']).new(*arguments).create
94
+ rescue DatabaseAlreadyExists
95
+ $stderr.puts "#{configuration['database']} already exists"
96
+ rescue Exception => error
97
+ $stderr.puts error, *(error.backtrace)
98
+ $stderr.puts "Couldn't create database for #{configuration.inspect}"
99
+ end
100
+
101
+ def create_all
102
+ each_local_configuration { |configuration| create configuration }
103
+ end
104
+
105
+ def create_current(environment = env)
106
+ each_current_configuration(environment) { |configuration|
107
+ create configuration
108
+ }
109
+ ActiveRecord::Base.establish_connection(environment.to_sym)
110
+ end
111
+
112
+ def drop(*arguments)
113
+ configuration = arguments.first
114
+ class_for_adapter(configuration['adapter']).new(*arguments).drop
115
+ rescue ActiveRecord::NoDatabaseError
116
+ $stderr.puts "Database '#{configuration['database']}' does not exist"
117
+ rescue Exception => error
118
+ $stderr.puts error, *(error.backtrace)
119
+ $stderr.puts "Couldn't drop #{configuration['database']}"
120
+ end
121
+
122
+ def drop_all
123
+ each_local_configuration { |configuration| drop configuration }
124
+ end
125
+
126
+ def drop_current(environment = env)
127
+ each_current_configuration(environment) { |configuration|
128
+ drop configuration
129
+ }
130
+ end
131
+
132
+ def migrate
133
+ verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
134
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
135
+ scope = ENV['SCOPE']
136
+ verbose_was, Migration.verbose = Migration.verbose, verbose
137
+ Migrator.migrate(Migrator.migrations_paths, version) do |migration|
138
+ scope.blank? || scope == migration.scope
139
+ end
140
+ ensure
141
+ Migration.verbose = verbose_was
142
+ end
143
+
144
+ def charset_current(environment = env)
145
+ charset ActiveRecord::Base.configurations[environment]
146
+ end
147
+
148
+ def charset(*arguments)
149
+ configuration = arguments.first
150
+ class_for_adapter(configuration['adapter']).new(*arguments).charset
151
+ end
152
+
153
+ def collation_current(environment = env)
154
+ collation ActiveRecord::Base.configurations[environment]
155
+ end
156
+
157
+ def collation(*arguments)
158
+ configuration = arguments.first
159
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
160
+ end
161
+
162
+ def purge(configuration)
163
+ class_for_adapter(configuration['adapter']).new(configuration).purge
164
+ end
165
+
166
+ def purge_all
167
+ each_local_configuration { |configuration|
168
+ purge configuration
169
+ }
170
+ end
171
+
172
+ def purge_current(environment = env)
173
+ each_current_configuration(environment) { |configuration|
174
+ purge configuration
175
+ }
176
+ ActiveRecord::Base.establish_connection(environment.to_sym)
177
+ end
178
+
179
+ def structure_dump(*arguments)
180
+ configuration = arguments.first
181
+ filename = arguments.delete_at 1
182
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
183
+ end
184
+
185
+ def structure_load(*arguments)
186
+ configuration = arguments.first
187
+ filename = arguments.delete_at 1
188
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
189
+ end
190
+
191
+ def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
192
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
193
+ This method will act on a specific connection in the future.
194
+ To act on the current connection, use `load_schema_current` instead.
195
+ MSG
196
+
197
+ load_schema_current(format, file)
198
+ end
199
+
200
+ def schema_file(format = ActiveSupport::Base.schema_format)
201
+ case format
202
+ when :ruby
203
+ File.join(db_dir, "schema.rb")
204
+ when :sql
205
+ File.join(db_dir, "structure.sql")
206
+ end
207
+ end
208
+
209
+ # This method is the successor of +load_schema+. We should rename it
210
+ # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
211
+ def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
212
+ file ||= schema_file(format)
213
+
214
+ case format
215
+ when :ruby
216
+ check_schema_file(file)
217
+ ActiveRecord::Base.establish_connection(configuration)
218
+ load(file)
219
+ when :sql
220
+ check_schema_file(file)
221
+ structure_load(configuration, file)
222
+ else
223
+ raise ArgumentError, "unknown format #{format.inspect}"
224
+ end
225
+ end
226
+
227
+ def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
228
+ if File.exist?(file || schema_file(format))
229
+ load_schema_current(format, file, environment)
230
+ end
231
+ end
232
+
233
+ def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
234
+ each_current_configuration(environment) { |configuration|
235
+ load_schema_for configuration, format, file
236
+ }
237
+ ActiveRecord::Base.establish_connection(environment.to_sym)
238
+ end
239
+
240
+ def check_schema_file(filename)
241
+ unless File.exist?(filename)
242
+ message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
243
+ message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
244
+ Kernel.abort message
245
+ end
246
+ end
247
+
248
+ def load_seed
249
+ if seed_loader
250
+ seed_loader.load_seed
251
+ else
252
+ raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
253
+ "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
254
+ "Seed loader should respond to load_seed method"
255
+ end
256
+ end
257
+
258
+ private
259
+
260
+ def class_for_adapter(adapter)
261
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
262
+ unless key
263
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
264
+ end
265
+ @tasks[key]
266
+ end
267
+
268
+ def each_current_configuration(environment)
269
+ environments = [environment]
270
+ # add test environment only if no RAILS_ENV was specified.
271
+ environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
272
+
273
+ configurations = ActiveRecord::Base.configurations.values_at(*environments)
274
+ configurations.compact.each do |configuration|
275
+ yield configuration unless configuration['database'].blank?
276
+ end
277
+ end
278
+
279
+ def each_local_configuration
280
+ ActiveRecord::Base.configurations.each_value do |configuration|
281
+ next unless configuration['database']
282
+
283
+ if local_database?(configuration)
284
+ yield configuration
285
+ else
286
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
287
+ end
288
+ end
289
+ end
290
+
291
+ def local_database?(configuration)
292
+ configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
293
+ end
294
+ end
295
+ end
296
+ end