activerecord 3.0.20 → 3.1.0.beta1

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 (122) hide show
  1. data/CHANGELOG +220 -91
  2. data/README.rdoc +3 -3
  3. data/examples/performance.rb +88 -109
  4. data/lib/active_record.rb +6 -2
  5. data/lib/active_record/aggregations.rb +22 -45
  6. data/lib/active_record/associations.rb +264 -991
  7. data/lib/active_record/associations/alias_tracker.rb +85 -0
  8. data/lib/active_record/associations/association.rb +231 -0
  9. data/lib/active_record/associations/association_scope.rb +120 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +40 -60
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
  12. data/lib/active_record/associations/builder/association.rb +53 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
  16. data/lib/active_record/associations/builder/has_many.rb +65 -0
  17. data/lib/active_record/associations/builder/has_one.rb +63 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +524 -0
  20. data/lib/active_record/associations/collection_proxy.rb +125 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
  22. data/lib/active_record/associations/has_many_association.rb +50 -79
  23. data/lib/active_record/associations/has_many_through_association.rb +98 -67
  24. data/lib/active_record/associations/has_one_association.rb +45 -115
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency.rb +215 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +150 -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_helper.rb +56 -0
  31. data/lib/active_record/associations/preloader.rb +177 -0
  32. data/lib/active_record/associations/preloader/association.rb +126 -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 +15 -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 +67 -0
  42. data/lib/active_record/associations/singular_association.rb +55 -0
  43. data/lib/active_record/associations/through_association.rb +80 -0
  44. data/lib/active_record/attribute_methods.rb +19 -5
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
  46. data/lib/active_record/attribute_methods/dirty.rb +8 -2
  47. data/lib/active_record/attribute_methods/primary_key.rb +33 -13
  48. data/lib/active_record/attribute_methods/read.rb +17 -17
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
  50. data/lib/active_record/attribute_methods/write.rb +2 -1
  51. data/lib/active_record/autosave_association.rb +66 -45
  52. data/lib/active_record/base.rb +445 -273
  53. data/lib/active_record/callbacks.rb +24 -33
  54. data/lib/active_record/coders/yaml_column.rb +41 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
  64. data/lib/active_record/connection_adapters/column.rb +268 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
  68. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
  69. data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
  70. data/lib/active_record/counter_cache.rb +7 -4
  71. data/lib/active_record/fixtures.rb +174 -192
  72. data/lib/active_record/identity_map.rb +131 -0
  73. data/lib/active_record/locking/optimistic.rb +20 -14
  74. data/lib/active_record/locking/pessimistic.rb +4 -4
  75. data/lib/active_record/log_subscriber.rb +24 -4
  76. data/lib/active_record/migration.rb +265 -144
  77. data/lib/active_record/migration/command_recorder.rb +103 -0
  78. data/lib/active_record/named_scope.rb +68 -25
  79. data/lib/active_record/nested_attributes.rb +58 -15
  80. data/lib/active_record/observer.rb +3 -7
  81. data/lib/active_record/persistence.rb +58 -38
  82. data/lib/active_record/query_cache.rb +25 -3
  83. data/lib/active_record/railtie.rb +21 -12
  84. data/lib/active_record/railties/console_sandbox.rb +6 -0
  85. data/lib/active_record/railties/databases.rake +147 -116
  86. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  87. data/lib/active_record/reflection.rb +176 -44
  88. data/lib/active_record/relation.rb +125 -49
  89. data/lib/active_record/relation/batches.rb +7 -5
  90. data/lib/active_record/relation/calculations.rb +50 -18
  91. data/lib/active_record/relation/finder_methods.rb +47 -26
  92. data/lib/active_record/relation/predicate_builder.rb +24 -21
  93. data/lib/active_record/relation/query_methods.rb +117 -101
  94. data/lib/active_record/relation/spawn_methods.rb +27 -20
  95. data/lib/active_record/result.rb +34 -0
  96. data/lib/active_record/schema.rb +5 -6
  97. data/lib/active_record/schema_dumper.rb +11 -13
  98. data/lib/active_record/serialization.rb +2 -2
  99. data/lib/active_record/serializers/xml_serializer.rb +10 -10
  100. data/lib/active_record/session_store.rb +8 -2
  101. data/lib/active_record/test_case.rb +9 -20
  102. data/lib/active_record/timestamp.rb +21 -9
  103. data/lib/active_record/transactions.rb +16 -15
  104. data/lib/active_record/validations.rb +21 -22
  105. data/lib/active_record/validations/associated.rb +3 -1
  106. data/lib/active_record/validations/uniqueness.rb +48 -58
  107. data/lib/active_record/version.rb +3 -3
  108. data/lib/rails/generators/active_record.rb +6 -0
  109. data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
  110. data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
  111. data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
  112. data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
  113. data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
  114. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  115. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
  116. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
  117. metadata +106 -77
  118. checksums.yaml +0 -7
  119. data/lib/active_record/association_preload.rb +0 -431
  120. data/lib/active_record/associations/association_collection.rb +0 -572
  121. data/lib/active_record/associations/association_proxy.rb +0 -304
  122. data/lib/active_record/associations/through_association_scope.rb +0 -160
@@ -27,10 +27,32 @@ module ActiveRecord
27
27
  @app = app
28
28
  end
29
29
 
30
- def call(env)
31
- ActiveRecord::Base.cache do
32
- @app.call(env)
30
+ class BodyProxy # :nodoc:
31
+ def initialize(original_cache_value, target)
32
+ @original_cache_value = original_cache_value
33
+ @target = target
34
+ end
35
+
36
+ def each(&block)
37
+ @target.each(&block)
38
+ end
39
+
40
+ def close
41
+ @target.close if @target.respond_to?(:close)
42
+ ensure
43
+ ActiveRecord::Base.connection.clear_query_cache
44
+ unless @original_cache_value
45
+ ActiveRecord::Base.connection.disable_query_cache!
46
+ end
33
47
  end
34
48
  end
49
+
50
+ def call(env)
51
+ old = ActiveRecord::Base.connection.query_cache_enabled
52
+ ActiveRecord::Base.connection.enable_query_cache!
53
+
54
+ status, headers, body = @app.call(env)
55
+ [status, headers, BodyProxy.new(old, body)]
56
+ end
35
57
  end
36
58
  end
@@ -13,8 +13,8 @@ module ActiveRecord
13
13
  class Railtie < Rails::Railtie
14
14
  config.active_record = ActiveSupport::OrderedOptions.new
15
15
 
16
- config.generators.orm :active_record, :migration => true,
17
- :timestamps => true
16
+ config.app_generators.orm :active_record, :migration => true,
17
+ :timestamps => true
18
18
 
19
19
  config.app_middleware.insert_after "::ActionDispatch::Callbacks",
20
20
  "ActiveRecord::QueryCache"
@@ -26,10 +26,12 @@ module ActiveRecord
26
26
  load "active_record/railties/databases.rake"
27
27
  end
28
28
 
29
- # When loading console, force ActiveRecord to be loaded to avoid cross
30
- # references when loading a constant for the first time.
31
- console do
32
- ActiveRecord::Base
29
+ # When loading console, force ActiveRecord::Base to be loaded
30
+ # to avoid cross references when loading a constant for the
31
+ # first time. Also, make it output to STDERR.
32
+ console do |sandbox|
33
+ require "active_record/railties/console_sandbox" if sandbox
34
+ ActiveRecord::Base.logger = Logger.new(STDERR)
33
35
  end
34
36
 
35
37
  initializer "active_record.initialize_timezone" do
@@ -43,8 +45,16 @@ module ActiveRecord
43
45
  ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
44
46
  end
45
47
 
48
+ initializer "active_record.identity_map" do |app|
49
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
50
+ "ActiveRecord::IdentityMap::Middleware" if config.active_record.delete(:identity_map)
51
+ end
52
+
46
53
  initializer "active_record.set_configs" do |app|
47
54
  ActiveSupport.on_load(:active_record) do
55
+ if app.config.active_record.delete(:whitelist_attributes)
56
+ attr_accessible(nil)
57
+ end
48
58
  app.config.active_record.each do |k,v|
49
59
  send "#{k}=", v
50
60
  end
@@ -69,11 +79,10 @@ module ActiveRecord
69
79
  end
70
80
 
71
81
  initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
72
- unless app.config.cache_classes
73
- ActiveSupport.on_load(:active_record) do
74
- ActionDispatch::Callbacks.after do
75
- ActiveRecord::Base.clear_reloadable_connections!
76
- end
82
+ ActiveSupport.on_load(:active_record) do
83
+ ActionDispatch::Reloader.to_cleanup do
84
+ ActiveRecord::Base.clear_reloadable_connections!
85
+ ActiveRecord::Base.clear_cache!
77
86
  end
78
87
  end
79
88
  end
@@ -82,7 +91,7 @@ module ActiveRecord
82
91
  ActiveSupport.on_load(:active_record) do
83
92
  instantiate_observers
84
93
 
85
- ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
94
+ ActionDispatch::Reloader.to_prepare do
86
95
  ActiveRecord::Base.instantiate_observers
87
96
  end
88
97
  end
@@ -0,0 +1,6 @@
1
+ ActiveRecord::Base.connection.increment_open_transactions
2
+ ActiveRecord::Base.connection.begin_db_transaction
3
+ at_exit do
4
+ ActiveRecord::Base.connection.rollback_db_transaction
5
+ ActiveRecord::Base.connection.decrement_open_transactions
6
+ end
@@ -1,7 +1,16 @@
1
- namespace :db do
1
+ require 'active_support/core_ext/object/inclusion'
2
+
3
+ db_namespace = namespace :db do
2
4
  task :load_config => :rails_env do
3
5
  require 'active_record'
4
6
  ActiveRecord::Base.configurations = Rails.application.config.database_configuration
7
+ ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
8
+
9
+ if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
10
+ if engine.paths['db/migrate'].existent
11
+ ActiveRecord::Migrator.migrations_paths += engine.paths['db/migrate'].to_a
12
+ end
13
+ end
5
14
  end
6
15
 
7
16
  namespace :create do
@@ -35,12 +44,6 @@ namespace :db do
35
44
  create_database(ActiveRecord::Base.configurations[Rails.env])
36
45
  end
37
46
 
38
- def mysql_creation_options(config)
39
- @charset = ENV['CHARSET'] || 'utf8'
40
- @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
41
- {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
42
- end
43
-
44
47
  def create_database(config)
45
48
  begin
46
49
  if config['adapter'] =~ /sqlite/
@@ -63,7 +66,10 @@ namespace :db do
63
66
  end
64
67
  rescue
65
68
  case config['adapter']
66
- when /^(jdbc)?mysql/
69
+ when /mysql/
70
+ @charset = ENV['CHARSET'] || 'utf8'
71
+ @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
72
+ creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
67
73
  if config['adapter'] =~ /jdbc/
68
74
  #FIXME After Jdbcmysql gives this class
69
75
  require 'active_record/railties/jdbcmysql_error'
@@ -74,7 +80,7 @@ namespace :db do
74
80
  access_denied_error = 1045
75
81
  begin
76
82
  ActiveRecord::Base.establish_connection(config.merge('database' => nil))
77
- ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
83
+ ActiveRecord::Base.connection.create_database(config['database'], creation_options)
78
84
  ActiveRecord::Base.establish_connection(config)
79
85
  rescue error_class => sqlerr
80
86
  if sqlerr.errno == access_denied_error
@@ -85,7 +91,7 @@ namespace :db do
85
91
  "IDENTIFIED BY '#{config['password']}' WITH GRANT OPTION;"
86
92
  ActiveRecord::Base.establish_connection(config.merge(
87
93
  'database' => nil, 'username' => 'root', 'password' => root_password))
88
- ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
94
+ ActiveRecord::Base.connection.create_database(config['database'], creation_options)
89
95
  ActiveRecord::Base.connection.execute grant_statement
90
96
  ActiveRecord::Base.establish_connection(config)
91
97
  else
@@ -94,7 +100,7 @@ namespace :db do
94
100
  $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
95
101
  end
96
102
  end
97
- when /^(jdbc)?postgresql$/
103
+ when /postgresql/
98
104
  @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
99
105
  begin
100
106
  ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
@@ -137,7 +143,7 @@ namespace :db do
137
143
  end
138
144
 
139
145
  def local_database?(config, &block)
140
- if %w( 127.0.0.1 localhost ).include?(config['host']) || config['host'].blank?
146
+ if config['host'].in?(['127.0.0.1', 'localhost']) || config['host'].blank?
141
147
  yield
142
148
  else
143
149
  $stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host."
@@ -146,45 +152,45 @@ namespace :db do
146
152
 
147
153
 
148
154
  desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
149
- task :migrate => :environment do
155
+ task :migrate => [:environment, :load_config] do
150
156
  ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
151
- ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
152
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
157
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
158
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
153
159
  end
154
160
 
155
161
  namespace :migrate do
156
162
  # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
157
- task :redo => :environment do
158
- if ENV["VERSION"]
159
- Rake::Task["db:migrate:down"].invoke
160
- Rake::Task["db:migrate:up"].invoke
163
+ task :redo => [:environment, :load_config] do
164
+ if ENV['VERSION']
165
+ db_namespace['migrate:down'].invoke
166
+ db_namespace['migrate:up'].invoke
161
167
  else
162
- Rake::Task["db:rollback"].invoke
163
- Rake::Task["db:migrate"].invoke
168
+ db_namespace['rollback'].invoke
169
+ db_namespace['migrate'].invoke
164
170
  end
165
171
  end
166
172
 
167
173
  # desc 'Resets your database using your migrations for the current environment'
168
- task :reset => ["db:drop", "db:create", "db:migrate"]
174
+ task :reset => ['db:drop', 'db:create', 'db:migrate']
169
175
 
170
176
  # desc 'Runs the "up" for a given migration VERSION.'
171
- task :up => :environment do
172
- version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
173
- raise "VERSION is required" unless version
174
- ActiveRecord::Migrator.run(:up, "db/migrate/", version)
175
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
177
+ task :up => [:environment, :load_config] do
178
+ version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
179
+ raise 'VERSION is required' unless version
180
+ ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
181
+ db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
176
182
  end
177
183
 
178
184
  # desc 'Runs the "down" for a given migration VERSION.'
179
- task :down => :environment do
180
- version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
181
- raise "VERSION is required" unless version
182
- ActiveRecord::Migrator.run(:down, "db/migrate/", version)
183
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
185
+ task :down => [:environment, :load_config] do
186
+ version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
187
+ raise 'VERSION is required' unless version
188
+ ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
189
+ db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
184
190
  end
185
191
 
186
- desc "Display status of migrations"
187
- task :status => :environment do
192
+ desc 'Display status of migrations'
193
+ task :status => [:environment, :load_config] do
188
194
  config = ActiveRecord::Base.configurations[Rails.env || 'development']
189
195
  ActiveRecord::Base.establish_connection(config)
190
196
  unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
@@ -195,14 +201,14 @@ namespace :db do
195
201
  file_list = []
196
202
  Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
197
203
  # only files matching "20091231235959_some_name.rb" pattern
198
- if match_data = /(\d{14})_(.+)\.rb/.match(file)
204
+ if match_data = /^(\d{14})_(.+)\.rb$/.match(file)
199
205
  status = db_list.delete(match_data[1]) ? 'up' : 'down'
200
206
  file_list << [status, match_data[1], match_data[2]]
201
207
  end
202
208
  end
203
209
  # output
204
210
  puts "\ndatabase: #{config['database']}\n\n"
205
- puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name"
211
+ puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
206
212
  puts "-" * 50
207
213
  file_list.each do |file|
208
214
  puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2].humanize}"
@@ -215,17 +221,17 @@ namespace :db do
215
221
  end
216
222
 
217
223
  desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
218
- task :rollback => :environment do
224
+ task :rollback => [:environment, :load_config] do
219
225
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
220
- ActiveRecord::Migrator.rollback('db/migrate/', step)
221
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
226
+ ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
227
+ db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
222
228
  end
223
229
 
224
230
  # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
225
- task :forward => :environment do
231
+ task :forward => [:environment, :load_config] do
226
232
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
227
- ActiveRecord::Migrator.forward('db/migrate/', step)
228
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
233
+ ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
234
+ db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby
229
235
  end
230
236
 
231
237
  # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
@@ -235,13 +241,13 @@ namespace :db do
235
241
  task :charset => :environment do
236
242
  config = ActiveRecord::Base.configurations[Rails.env || 'development']
237
243
  case config['adapter']
238
- when /^(jdbc)?mysql/
244
+ when /mysql/
239
245
  ActiveRecord::Base.establish_connection(config)
240
246
  puts ActiveRecord::Base.connection.charset
241
- when /^(jdbc)?postgresql$/
247
+ when /postgresql/
242
248
  ActiveRecord::Base.establish_connection(config)
243
249
  puts ActiveRecord::Base.connection.encoding
244
- when /^(jdbc)?sqlite/
250
+ when /sqlite/
245
251
  ActiveRecord::Base.establish_connection(config)
246
252
  puts ActiveRecord::Base.connection.encoding
247
253
  else
@@ -253,7 +259,7 @@ namespace :db do
253
259
  task :collation => :environment do
254
260
  config = ActiveRecord::Base.configurations[Rails.env || 'development']
255
261
  case config['adapter']
256
- when /^(jdbc)?mysql/
262
+ when /mysql/
257
263
  ActiveRecord::Base.establish_connection(config)
258
264
  puts ActiveRecord::Base.connection.collation
259
265
  else
@@ -261,7 +267,7 @@ namespace :db do
261
267
  end
262
268
  end
263
269
 
264
- desc "Retrieves the current schema version number"
270
+ desc 'Retrieves the current schema version number'
265
271
  task :version => :environment do
266
272
  puts "Current version: #{ActiveRecord::Migrator.current_version}"
267
273
  end
@@ -269,7 +275,7 @@ namespace :db do
269
275
  # desc "Raises an error if there are pending migrations"
270
276
  task :abort_if_pending_migrations => :environment do
271
277
  if defined? ActiveRecord
272
- pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
278
+ pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
273
279
 
274
280
  if pending_migrations.any?
275
281
  puts "You have #{pending_migrations.size} pending migrations:"
@@ -286,8 +292,7 @@ namespace :db do
286
292
 
287
293
  desc 'Load the seed data from db/seeds.rb'
288
294
  task :seed => 'db:abort_if_pending_migrations' do
289
- seed_file = File.join(Rails.root, 'db', 'seeds.rb')
290
- load(seed_file) if File.exist?(seed_file)
295
+ Rails.application.load_seed
291
296
  end
292
297
 
293
298
  namespace :fixtures do
@@ -296,11 +301,11 @@ namespace :db do
296
301
  require 'active_record/fixtures'
297
302
 
298
303
  ActiveRecord::Base.establish_connection(Rails.env)
299
- base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
300
- fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
304
+ base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
305
+ fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
301
306
 
302
- (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir["#{fixtures_dir}/**/*.{yml,csv}"]).each do |fixture_file|
303
- Fixtures.create_fixtures(fixtures_dir, fixture_file[(fixtures_dir.size + 1)..-5])
307
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
308
+ Fixtures.create_fixtures(fixtures_dir, fixture_file)
304
309
  end
305
310
  end
306
311
 
@@ -308,8 +313,8 @@ namespace :db do
308
313
  task :identify => :environment do
309
314
  require 'active_record/fixtures'
310
315
 
311
- label, id = ENV["LABEL"], ENV["ID"]
312
- raise "LABEL or ID required" if label.blank? && id.blank?
316
+ label, id = ENV['LABEL'], ENV['ID']
317
+ raise 'LABEL or ID required' if label.blank? && id.blank?
313
318
 
314
319
  puts %Q(The fixture ID for "#{label}" is #{Fixtures.identify(label)}.) if label
315
320
 
@@ -329,18 +334,16 @@ namespace :db do
329
334
  end
330
335
 
331
336
  namespace :schema do
332
- desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
333
- task :dump => :environment do
337
+ desc 'Create a db/schema.rb file that can be portably used against any DB supported by AR'
338
+ task :dump => :load_config do
334
339
  require 'active_record/schema_dumper'
335
- filename = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
336
- File.open(filename, "w:utf-8") do |file|
337
- ActiveRecord::Base.establish_connection(Rails.env)
340
+ File.open(ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb", "w") do |file|
338
341
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
339
342
  end
340
- Rake::Task["db:schema:dump"].reenable
343
+ db_namespace['schema:dump'].reenable
341
344
  end
342
345
 
343
- desc "Load a schema.rb file into the database"
346
+ desc 'Load a schema.rb file into the database'
344
347
  task :load => :environment do
345
348
  file = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
346
349
  if File.exists?(file)
@@ -352,29 +355,29 @@ namespace :db do
352
355
  end
353
356
 
354
357
  namespace :structure do
355
- desc "Dump the database structure to an SQL file"
358
+ desc 'Dump the database structure to an SQL file'
356
359
  task :dump => :environment do
357
360
  abcs = ActiveRecord::Base.configurations
358
- case abcs[Rails.env]["adapter"]
359
- when /^(jdbc)?mysql/, "oci", "oracle"
361
+ case abcs[Rails.env]['adapter']
362
+ when /mysql/, 'oci', 'oracle'
360
363
  ActiveRecord::Base.establish_connection(abcs[Rails.env])
361
364
  File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
362
- when /^(jdbc)?postgresql$/
363
- ENV['PGHOST'] = abcs[Rails.env]["host"] if abcs[Rails.env]["host"]
364
- ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]["port"]
365
- ENV['PGPASSWORD'] = abcs[Rails.env]["password"].to_s if abcs[Rails.env]["password"]
366
- search_path = abcs[Rails.env]["schema_search_path"]
365
+ when /postgresql/
366
+ ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host']
367
+ ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]['port']
368
+ ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password']
369
+ search_path = abcs[Rails.env]['schema_search_path']
367
370
  unless search_path.blank?
368
371
  search_path = search_path.split(",").map{|search_path| "--schema=#{search_path.strip}" }.join(" ")
369
372
  end
370
- `pg_dump -i -U "#{abcs[Rails.env]["username"]}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]["database"]}`
371
- raise "Error dumping database" if $?.exitstatus == 1
372
- when /^(jdbc)?sqlite/
373
- dbfile = abcs[Rails.env]["database"] || abcs[Rails.env]["dbfile"]
373
+ `pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}`
374
+ raise 'Error dumping database' if $?.exitstatus == 1
375
+ when /sqlite/
376
+ dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile']
374
377
  `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql`
375
- when "sqlserver"
376
- `scptxfr /s #{abcs[Rails.env]["host"]} /d #{abcs[Rails.env]["database"]} /I /f db\\#{Rails.env}_structure.sql /q /A /r`
377
- `scptxfr /s #{abcs[Rails.env]["host"]} /d #{abcs[Rails.env]["database"]} /I /F db\ /q /A /r`
378
+ when 'sqlserver'
379
+ `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /f db\\#{Rails.env}_structure.sql /q /A /r`
380
+ `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /F db\ /q /A /r`
378
381
  when "firebird"
379
382
  set_firebird_env(abcs[Rails.env])
380
383
  db_string = firebird_db_string(abcs[Rails.env])
@@ -394,81 +397,81 @@ namespace :db do
394
397
  task :load => 'db:test:purge' do
395
398
  ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
396
399
  ActiveRecord::Schema.verbose = false
397
- Rake::Task["db:schema:load"].invoke
400
+ db_namespace['schema:load'].invoke
398
401
  end
399
402
 
400
403
  # desc "Recreate the test database from the current environment's database schema"
401
404
  task :clone => %w(db:schema:dump db:test:load)
402
405
 
403
406
  # desc "Recreate the test databases from the development structure"
404
- task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
407
+ task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do
405
408
  abcs = ActiveRecord::Base.configurations
406
- case abcs["test"]["adapter"]
407
- when /^(jdbc)?mysql/
409
+ case abcs['test']['adapter']
410
+ when /mysql/
408
411
  ActiveRecord::Base.establish_connection(:test)
409
412
  ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
410
413
  IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table|
411
414
  ActiveRecord::Base.connection.execute(table)
412
415
  end
413
- when /^(jdbc)?postgresql$/
414
- ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"]
415
- ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"]
416
- ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"]
417
- `psql -U "#{abcs["test"]["username"]}" -f #{Rails.root}/db/#{Rails.env}_structure.sql #{abcs["test"]["database"]}`
418
- when /^(jdbc)?sqlite/
419
- dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
416
+ when /postgresql/
417
+ ENV['PGHOST'] = abcs['test']['host'] if abcs['test']['host']
418
+ ENV['PGPORT'] = abcs['test']['port'].to_s if abcs['test']['port']
419
+ ENV['PGPASSWORD'] = abcs['test']['password'].to_s if abcs['test']['password']
420
+ `psql -U "#{abcs['test']['username']}" -f #{Rails.root}/db/#{Rails.env}_structure.sql #{abcs['test']['database']} #{abcs['test']['template']}`
421
+ when /sqlite/
422
+ dbfile = abcs['test']['database'] || abcs['test']['dbfile']
420
423
  `sqlite3 #{dbfile} < #{Rails.root}/db/#{Rails.env}_structure.sql`
421
- when "sqlserver"
422
- `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{Rails.env}_structure.sql`
423
- when "oci", "oracle"
424
+ when 'sqlserver'
425
+ `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
426
+ when 'oci', 'oracle'
424
427
  ActiveRecord::Base.establish_connection(:test)
425
428
  IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl|
426
429
  ActiveRecord::Base.connection.execute(ddl)
427
430
  end
428
- when "firebird"
429
- set_firebird_env(abcs["test"])
430
- db_string = firebird_db_string(abcs["test"])
431
+ when 'firebird'
432
+ set_firebird_env(abcs['test'])
433
+ db_string = firebird_db_string(abcs['test'])
431
434
  sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}"
432
435
  else
433
- raise "Task not supported by '#{abcs["test"]["adapter"]}'"
436
+ raise "Task not supported by '#{abcs['test']['adapter']}'"
434
437
  end
435
438
  end
436
439
 
437
440
  # desc "Empty the test database"
438
441
  task :purge => :environment do
439
442
  abcs = ActiveRecord::Base.configurations
440
- case abcs["test"]["adapter"]
441
- when /^(jdbc)?mysql/
443
+ case abcs['test']['adapter']
444
+ when /mysql/
442
445
  ActiveRecord::Base.establish_connection(:test)
443
- ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], mysql_creation_options(abcs["test"]))
444
- when /^(jdbc)?postgresql$/
446
+ ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], abcs['test'])
447
+ when /postgresql/
445
448
  ActiveRecord::Base.clear_active_connections!
446
449
  drop_database(abcs['test'])
447
450
  create_database(abcs['test'])
448
- when /^(jdbc)?sqlite/
449
- dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"]
451
+ when /sqlite/
452
+ dbfile = abcs['test']['database'] || abcs['test']['dbfile']
450
453
  File.delete(dbfile) if File.exist?(dbfile)
451
- when "sqlserver"
452
- dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-')
453
- `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}`
454
- `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{Rails.env}_structure.sql`
454
+ when 'sqlserver'
455
+ dropfkscript = "#{abcs['test']['host']}.#{abcs['test']['database']}.DP1".gsub(/\\/,'-')
456
+ `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{dropfkscript}`
457
+ `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
455
458
  when "oci", "oracle"
456
459
  ActiveRecord::Base.establish_connection(:test)
457
460
  ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
458
461
  ActiveRecord::Base.connection.execute(ddl)
459
462
  end
460
- when "firebird"
463
+ when 'firebird'
461
464
  ActiveRecord::Base.establish_connection(:test)
462
465
  ActiveRecord::Base.connection.recreate_database!
463
466
  else
464
- raise "Task not supported by '#{abcs["test"]["adapter"]}'"
467
+ raise "Task not supported by '#{abcs['test']['adapter']}'"
465
468
  end
466
469
  end
467
470
 
468
471
  # desc 'Check for pending migrations and load the test schema'
469
472
  task :prepare => 'db:abort_if_pending_migrations' do
470
473
  if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
471
- Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke
474
+ db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
472
475
  end
473
476
  end
474
477
  end
@@ -476,11 +479,11 @@ namespace :db do
476
479
  namespace :sessions do
477
480
  # desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
478
481
  task :create => :environment do
479
- raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
482
+ raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
480
483
  require 'rails/generators'
481
484
  Rails::Generators.configure!
482
485
  require 'rails/generators/rails/session_migration/session_migration_generator'
483
- Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ]
486
+ Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
484
487
  end
485
488
 
486
489
  # desc "Clear the sessions table"
@@ -490,20 +493,48 @@ namespace :db do
490
493
  end
491
494
  end
492
495
 
496
+ namespace :railties do
497
+ namespace :install do
498
+ # desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
499
+ task :migrations => :'db:load_config' do
500
+ to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
501
+ railties = {}
502
+ Rails.application.railties.all do |railtie|
503
+ next unless to_load == :all || to_load.include?(railtie.railtie_name)
504
+
505
+ if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
506
+ railties[railtie.railtie_name] = path
507
+ end
508
+ end
509
+
510
+ on_skip = Proc.new do |name, migration|
511
+ puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
512
+ end
513
+
514
+ on_copy = Proc.new do |name, migration, old_path|
515
+ puts "Copied migration #{migration.basename} from #{name}"
516
+ end
517
+
518
+ ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_paths.first, railties,
519
+ :on_skip => on_skip, :on_copy => on_copy)
520
+ end
521
+ end
522
+ end
523
+
493
524
  task 'test:prepare' => 'db:test:prepare'
494
525
 
495
526
  def drop_database(config)
496
527
  case config['adapter']
497
- when /^(jdbc)?mysql/
528
+ when /mysql/
498
529
  ActiveRecord::Base.establish_connection(config)
499
530
  ActiveRecord::Base.connection.drop_database config['database']
500
- when /^(jdbc)?sqlite/
531
+ when /sqlite/
501
532
  require 'pathname'
502
533
  path = Pathname.new(config['database'])
503
534
  file = path.absolute? ? path.to_s : File.join(Rails.root, path)
504
535
 
505
536
  FileUtils.rm(file)
506
- when /^(jdbc)?postgresql$/
537
+ when /postgresql/
507
538
  ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
508
539
  ActiveRecord::Base.connection.drop_database config['database']
509
540
  end
@@ -514,8 +545,8 @@ def session_table_name
514
545
  end
515
546
 
516
547
  def set_firebird_env(config)
517
- ENV["ISC_USER"] = config["username"].to_s if config["username"]
518
- ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"]
548
+ ENV['ISC_USER'] = config['username'].to_s if config['username']
549
+ ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
519
550
  end
520
551
 
521
552
  def firebird_db_string(config)