activerecord 1.0.0 → 4.0.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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,156 @@
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
+ # Examples:
19
+ #
20
+ # class User < ActiveRecord::Base
21
+ # store :settings, accessors: [ :color, :homepage ], coder: JSON
22
+ # end
23
+ #
24
+ # u = User.new(color: 'black', homepage: '37signals.com')
25
+ # u.color # Accessor stored attribute
26
+ # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
27
+ #
28
+ # # There is no difference between strings and symbols for accessing custom attributes
29
+ # u.settings[:country] # => 'Denmark'
30
+ # u.settings['country'] # => 'Denmark'
31
+ #
32
+ # # Add additional accessors to an existing store through store_accessor
33
+ # class SuperUser < User
34
+ # store_accessor :settings, :privileges, :servants
35
+ # end
36
+ #
37
+ # The stored attribute names can be retrieved using +stored_attributes+.
38
+ #
39
+ # User.stored_attributes[:settings] # [:color, :homepage]
40
+ #
41
+ # == Overwriting default accessors
42
+ #
43
+ # All stored values are automatically available through accessors on the Active Record
44
+ # object, but sometimes you want to specialize this behavior. This can be done by overwriting
45
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
46
+ # to actually change things.
47
+ #
48
+ # class Song < ActiveRecord::Base
49
+ # # Uses a stored integer to hold the volume adjustment of the song
50
+ # store :settings, accessors: [:volume_adjustment]
51
+ #
52
+ # def volume_adjustment=(decibels)
53
+ # super(decibels.to_i)
54
+ # end
55
+ #
56
+ # def volume_adjustment
57
+ # super.to_i
58
+ # end
59
+ # end
60
+ module Store
61
+ extend ActiveSupport::Concern
62
+
63
+ included do
64
+ class_attribute :stored_attributes, instance_accessor: false
65
+ self.stored_attributes = {}
66
+ end
67
+
68
+ module ClassMethods
69
+ def store(store_attribute, options = {})
70
+ serialize store_attribute, IndifferentCoder.new(options[:coder])
71
+ store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
72
+ end
73
+
74
+ def store_accessor(store_attribute, *keys)
75
+ keys = keys.flatten
76
+
77
+ _store_accessors_module.module_eval do
78
+ keys.each do |key|
79
+ define_method("#{key}=") do |value|
80
+ write_store_attribute(store_attribute, key, value)
81
+ end
82
+
83
+ define_method(key) do
84
+ read_store_attribute(store_attribute, key)
85
+ end
86
+ end
87
+ end
88
+
89
+ self.stored_attributes[store_attribute] ||= []
90
+ self.stored_attributes[store_attribute] |= keys
91
+ end
92
+
93
+ def _store_accessors_module
94
+ @_store_accessors_module ||= begin
95
+ mod = Module.new
96
+ include mod
97
+ mod
98
+ end
99
+ end
100
+ end
101
+
102
+ protected
103
+ def read_store_attribute(store_attribute, key)
104
+ attribute = initialize_store_attribute(store_attribute)
105
+ attribute[key]
106
+ end
107
+
108
+ def write_store_attribute(store_attribute, key, value)
109
+ attribute = initialize_store_attribute(store_attribute)
110
+ if value != attribute[key]
111
+ send :"#{store_attribute}_will_change!"
112
+ attribute[key] = value
113
+ end
114
+ end
115
+
116
+ private
117
+ def initialize_store_attribute(store_attribute)
118
+ attribute = send(store_attribute)
119
+ unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
120
+ attribute = IndifferentCoder.as_indifferent_hash(attribute)
121
+ send :"#{store_attribute}=", attribute
122
+ end
123
+ attribute
124
+ end
125
+
126
+ class IndifferentCoder # :nodoc:
127
+ def initialize(coder_or_class_name)
128
+ @coder =
129
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
130
+ coder_or_class_name
131
+ else
132
+ ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
133
+ end
134
+ end
135
+
136
+ def dump(obj)
137
+ @coder.dump self.class.as_indifferent_hash(obj)
138
+ end
139
+
140
+ def load(yaml)
141
+ self.class.as_indifferent_hash @coder.load(yaml)
142
+ end
143
+
144
+ def self.as_indifferent_hash(obj)
145
+ case obj
146
+ when ActiveSupport::HashWithIndifferentAccess
147
+ obj
148
+ when Hash
149
+ obj.with_indifferent_access
150
+ else
151
+ ActiveSupport::HashWithIndifferentAccess.new
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,203 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class DatabaseAlreadyExists < StandardError; end # :nodoc:
4
+ class DatabaseNotSupported < StandardError; end # :nodoc:
5
+
6
+ # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
7
+ # logic behind common tasks used to manage database and migrations.
8
+ #
9
+ # The tasks defined here are used in rake tasks provided by Active Record.
10
+ #
11
+ # In order to use DatabaseTasks, a few config values need to be set. All the needed
12
+ # config values are set by Rails already, so it's necessary to do it only if you
13
+ # want to change the defaults or when you want to use Active Record outside of Rails
14
+ # (in such case after configuring the database tasks, you can also use the rake tasks
15
+ # defined in Active Record).
16
+ #
17
+ #
18
+ # The possible config values are:
19
+ #
20
+ # * +env+: current environment (like Rails.env).
21
+ # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
22
+ # * +db_dir+: your +db+ directory.
23
+ # * +fixtures_path+: a path to fixtures directory.
24
+ # * +migrations_paths+: a list of paths to directories with migrations.
25
+ # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
26
+ #
27
+ # Example usage of +DatabaseTasks+ outside Rails could look as such:
28
+ #
29
+ # include ActiveRecord::Tasks
30
+ # DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
31
+ # DatabaseTasks.db_dir = 'db'
32
+ # # other settings...
33
+ #
34
+ # DatabaseTasks.create_current('production')
35
+ module DatabaseTasks
36
+ extend self
37
+
38
+ attr_writer :current_config
39
+ attr_accessor :database_configuration, :migrations_paths, :seed_loader, :db_dir,
40
+ :fixtures_path, :env
41
+
42
+ LOCAL_HOSTS = ['127.0.0.1', 'localhost']
43
+
44
+ def register_task(pattern, task)
45
+ @tasks ||= {}
46
+ @tasks[pattern] = task
47
+ end
48
+
49
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
50
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
51
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
52
+
53
+ register_task(/firebird/, ActiveRecord::Tasks::FirebirdDatabaseTasks)
54
+ register_task(/sqlserver/, ActiveRecord::Tasks::SqlserverDatabaseTasks)
55
+ register_task(/(oci|oracle)/, ActiveRecord::Tasks::OracleDatabaseTasks)
56
+
57
+ def current_config(options = {})
58
+ options.reverse_merge! :env => env
59
+ if options.has_key?(:config)
60
+ @current_config = options[:config]
61
+ else
62
+ @current_config ||= if ENV['DATABASE_URL']
63
+ database_url_config
64
+ else
65
+ ActiveRecord::Base.configurations[options[:env]]
66
+ end
67
+ end
68
+ end
69
+
70
+ def create(*arguments)
71
+ configuration = arguments.first
72
+ class_for_adapter(configuration['adapter']).new(*arguments).create
73
+ rescue DatabaseAlreadyExists
74
+ $stderr.puts "#{configuration['database']} already exists"
75
+ rescue Exception => error
76
+ $stderr.puts error, *(error.backtrace)
77
+ $stderr.puts "Couldn't create database for #{configuration.inspect}"
78
+ end
79
+
80
+ def create_all
81
+ each_local_configuration { |configuration| create configuration }
82
+ end
83
+
84
+ def create_current(environment = env)
85
+ each_current_configuration(environment) { |configuration|
86
+ create configuration
87
+ }
88
+ ActiveRecord::Base.establish_connection environment
89
+ end
90
+
91
+ def create_database_url
92
+ create database_url_config
93
+ end
94
+
95
+ def drop(*arguments)
96
+ configuration = arguments.first
97
+ class_for_adapter(configuration['adapter']).new(*arguments).drop
98
+ rescue Exception => error
99
+ $stderr.puts error, *(error.backtrace)
100
+ $stderr.puts "Couldn't drop #{configuration['database']}"
101
+ end
102
+
103
+ def drop_all
104
+ each_local_configuration { |configuration| drop configuration }
105
+ end
106
+
107
+ def drop_current(environment = env)
108
+ each_current_configuration(environment) { |configuration|
109
+ drop configuration
110
+ }
111
+ end
112
+
113
+ def drop_database_url
114
+ drop database_url_config
115
+ end
116
+
117
+ def charset_current(environment = env)
118
+ charset ActiveRecord::Base.configurations[environment]
119
+ end
120
+
121
+ def charset(*arguments)
122
+ configuration = arguments.first
123
+ class_for_adapter(configuration['adapter']).new(*arguments).charset
124
+ end
125
+
126
+ def collation_current(environment = env)
127
+ collation ActiveRecord::Base.configurations[environment]
128
+ end
129
+
130
+ def collation(*arguments)
131
+ configuration = arguments.first
132
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
133
+ end
134
+
135
+ def purge(configuration)
136
+ class_for_adapter(configuration['adapter']).new(configuration).purge
137
+ end
138
+
139
+ def structure_dump(*arguments)
140
+ configuration = arguments.first
141
+ filename = arguments.delete_at 1
142
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
143
+ end
144
+
145
+ def structure_load(*arguments)
146
+ configuration = arguments.first
147
+ filename = arguments.delete_at 1
148
+ class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
149
+ end
150
+
151
+ def load_seed
152
+ if seed_loader
153
+ seed_loader.load_seed
154
+ else
155
+ raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
156
+ "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
157
+ "Seed loader should respond to load_seed method"
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ def database_url_config
164
+ @database_url_config ||=
165
+ ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
166
+ end
167
+
168
+ def class_for_adapter(adapter)
169
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
170
+ unless key
171
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
172
+ end
173
+ @tasks[key]
174
+ end
175
+
176
+ def each_current_configuration(environment)
177
+ environments = [environment]
178
+ environments << 'test' if environment == 'development'
179
+
180
+ configurations = ActiveRecord::Base.configurations.values_at(*environments)
181
+ configurations.compact.each do |configuration|
182
+ yield configuration unless configuration['database'].blank?
183
+ end
184
+ end
185
+
186
+ def each_local_configuration
187
+ ActiveRecord::Base.configurations.each_value do |configuration|
188
+ next unless configuration['database']
189
+
190
+ if local_database?(configuration)
191
+ yield configuration
192
+ else
193
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
194
+ end
195
+ end
196
+ end
197
+
198
+ def local_database?(configuration)
199
+ configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class FirebirdDatabaseTasks # :nodoc:
4
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
5
+
6
+ def initialize(configuration)
7
+ ActiveSupport::Deprecation.warn "This database tasks were deprecated, because this tasks should be served by the 3rd party adapter."
8
+ @configuration = configuration
9
+ end
10
+
11
+ def create
12
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
13
+ end
14
+
15
+ def drop
16
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
17
+ end
18
+
19
+ def purge
20
+ establish_connection(:test)
21
+ connection.recreate_database!
22
+ end
23
+
24
+ def charset
25
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
26
+ end
27
+
28
+ def structure_dump(filename)
29
+ set_firebird_env(configuration)
30
+ db_string = firebird_db_string(configuration)
31
+ Kernel.system "isql -a #{db_string} > #{filename}"
32
+ end
33
+
34
+ def structure_load(filename)
35
+ set_firebird_env(configuration)
36
+ db_string = firebird_db_string(configuration)
37
+ Kernel.system "isql -i #{filename} #{db_string}"
38
+ end
39
+
40
+ private
41
+
42
+ def set_firebird_env(config)
43
+ ENV['ISC_USER'] = config['username'].to_s if config['username']
44
+ ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
45
+ end
46
+
47
+ def firebird_db_string(config)
48
+ FireRuby::Database.db_string_for(config.symbolize_keys)
49
+ end
50
+
51
+ def configuration
52
+ @configuration
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,143 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class MySQLDatabaseTasks # :nodoc:
4
+ DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
5
+ DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
6
+ ACCESS_DENIED_ERROR = 1045
7
+
8
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
9
+
10
+ def initialize(configuration)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def create
15
+ establish_connection configuration_without_database
16
+ connection.create_database configuration['database'], creation_options
17
+ establish_connection configuration
18
+ rescue ActiveRecord::StatementInvalid => error
19
+ if /database exists/ === error.message
20
+ raise DatabaseAlreadyExists
21
+ else
22
+ raise
23
+ end
24
+ rescue error_class => error
25
+ if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
26
+ $stdout.print error.error
27
+ establish_connection root_configuration_without_database
28
+ connection.create_database configuration['database'], creation_options
29
+ if configuration['username'] != 'root'
30
+ connection.execute grant_statement.gsub(/\s+/, ' ').strip
31
+ end
32
+ establish_connection configuration
33
+ else
34
+ $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
35
+ $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
36
+ end
37
+ end
38
+
39
+ def drop
40
+ establish_connection configuration
41
+ connection.drop_database configuration['database']
42
+ end
43
+
44
+ def purge
45
+ establish_connection :test
46
+ connection.recreate_database configuration['database'], creation_options
47
+ end
48
+
49
+ def charset
50
+ connection.charset
51
+ end
52
+
53
+ def collation
54
+ connection.collation
55
+ end
56
+
57
+ def structure_dump(filename)
58
+ args = prepare_command_options('mysqldump')
59
+ args.concat(["--result-file", "#{filename}"])
60
+ args.concat(["--no-data"])
61
+ args.concat(["#{configuration['database']}"])
62
+ unless Kernel.system(*args)
63
+ $stderr.puts "Could not dump the database structure. "\
64
+ "Make sure `mysqldump` is in your PATH and check the command output for warnings."
65
+ end
66
+ end
67
+
68
+ def structure_load(filename)
69
+ args = prepare_command_options('mysql')
70
+ args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
71
+ args.concat(["--database", "#{configuration['database']}"])
72
+ Kernel.system(*args)
73
+ end
74
+
75
+ private
76
+
77
+ def configuration
78
+ @configuration
79
+ end
80
+
81
+ def configuration_without_database
82
+ configuration.merge('database' => nil)
83
+ end
84
+
85
+ def creation_options
86
+ Hash.new.tap do |options|
87
+ options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
88
+ options[:collation] = configuration['collation'] if configuration.include? 'collation'
89
+
90
+ # Set default charset only when collation isn't set.
91
+ options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
92
+
93
+ # Set default collation only when charset is also default.
94
+ options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
95
+ end
96
+ end
97
+
98
+ def error_class
99
+ if configuration['adapter'] =~ /jdbc/
100
+ require 'active_record/railties/jdbcmysql_error'
101
+ ArJdbcMySQL::Error
102
+ elsif defined?(Mysql2)
103
+ Mysql2::Error
104
+ elsif defined?(Mysql)
105
+ Mysql::Error
106
+ else
107
+ StandardError
108
+ end
109
+ end
110
+
111
+ def grant_statement
112
+ <<-SQL
113
+ GRANT ALL PRIVILEGES ON #{configuration['database']}.*
114
+ TO '#{configuration['username']}'@'localhost'
115
+ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
116
+ SQL
117
+ end
118
+
119
+ def root_configuration_without_database
120
+ configuration_without_database.merge(
121
+ 'username' => 'root',
122
+ 'password' => root_password
123
+ )
124
+ end
125
+
126
+ def root_password
127
+ $stdout.print "Please provide the root password for your mysql installation\n>"
128
+ $stdin.gets.strip
129
+ end
130
+
131
+ def prepare_command_options(command)
132
+ args = [command]
133
+ args.concat(['--user', configuration['username']]) if configuration['username']
134
+ args << "--password=#{configuration['password']}" if configuration['password']
135
+ args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
136
+ configuration.slice('host', 'port', 'socket').each do |k, v|
137
+ args.concat([ "--#{k}", v ]) if v
138
+ end
139
+ args
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,45 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class OracleDatabaseTasks # :nodoc:
4
+ delegate :connection, :establish_connection, to: ActiveRecord::Base
5
+
6
+ def initialize(configuration)
7
+ ActiveSupport::Deprecation.warn "This database tasks were deprecated, because this tasks should be served by the 3rd party adapter."
8
+ @configuration = configuration
9
+ end
10
+
11
+ def create
12
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
13
+ end
14
+
15
+ def drop
16
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
17
+ end
18
+
19
+ def purge
20
+ establish_connection(:test)
21
+ connection.structure_drop.split(";\n\n").each { |ddl| connection.execute(ddl) }
22
+ end
23
+
24
+ def charset
25
+ $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
26
+ end
27
+
28
+ def structure_dump(filename)
29
+ establish_connection(configuration)
30
+ File.open(filename, "w:utf-8") { |f| f << connection.structure_dump }
31
+ end
32
+
33
+ def structure_load(filename)
34
+ establish_connection(configuration)
35
+ IO.read(filename).split(";\n\n").each { |ddl| connection.execute(ddl) }
36
+ end
37
+
38
+ private
39
+
40
+ def configuration
41
+ @configuration
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,90 @@
1
+ require 'shellwords'
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class PostgreSQLDatabaseTasks # :nodoc:
6
+ DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
7
+
8
+ delegate :connection, :establish_connection, :clear_active_connections!,
9
+ to: ActiveRecord::Base
10
+
11
+ def initialize(configuration)
12
+ @configuration = configuration
13
+ end
14
+
15
+ def create(master_established = false)
16
+ establish_master_connection unless master_established
17
+ connection.create_database configuration['database'],
18
+ configuration.merge('encoding' => encoding)
19
+ establish_connection configuration
20
+ rescue ActiveRecord::StatementInvalid => error
21
+ if /database .* already exists/ === error.message
22
+ raise DatabaseAlreadyExists
23
+ else
24
+ raise
25
+ end
26
+ end
27
+
28
+ def drop
29
+ establish_master_connection
30
+ connection.drop_database configuration['database']
31
+ end
32
+
33
+ def charset
34
+ connection.encoding
35
+ end
36
+
37
+ def collation
38
+ connection.collation
39
+ end
40
+
41
+ def purge
42
+ clear_active_connections!
43
+ drop
44
+ create true
45
+ end
46
+
47
+ def structure_dump(filename)
48
+ set_psql_env
49
+ search_path = configuration['schema_search_path']
50
+ unless search_path.blank?
51
+ search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
52
+ end
53
+
54
+ command = "pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
55
+ raise 'Error dumping database' unless Kernel.system(command)
56
+
57
+ File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
58
+ end
59
+
60
+ def structure_load(filename)
61
+ set_psql_env
62
+ Kernel.system("psql -q -f #{filename} #{configuration['database']}")
63
+ end
64
+
65
+ private
66
+
67
+ def configuration
68
+ @configuration
69
+ end
70
+
71
+ def encoding
72
+ configuration['encoding'] || DEFAULT_ENCODING
73
+ end
74
+
75
+ def establish_master_connection
76
+ establish_connection configuration.merge(
77
+ 'database' => 'postgres',
78
+ 'schema_search_path' => 'public'
79
+ )
80
+ end
81
+
82
+ def set_psql_env
83
+ ENV['PGHOST'] = configuration['host'] if configuration['host']
84
+ ENV['PGPORT'] = configuration['port'].to_s if configuration['port']
85
+ ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
86
+ ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
87
+ end
88
+ end
89
+ end
90
+ end