activerecord 3.2.22.5 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,7 +1,5 @@
|
|
1
|
-
require "active_support/core_ext/module/delegation"
|
2
1
|
require "active_support/core_ext/class/attribute_accessors"
|
3
|
-
require
|
4
|
-
require 'active_support/deprecation'
|
2
|
+
require 'set'
|
5
3
|
|
6
4
|
module ActiveRecord
|
7
5
|
# Exception that can be raised to stop migrations from going backwards.
|
@@ -32,6 +30,12 @@ module ActiveRecord
|
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
33
|
+
class PendingMigrationError < ActiveRecordError#:nodoc:
|
34
|
+
def initialize
|
35
|
+
super("Migrations are pending; run 'rake db:migrate RAILS_ENV=#{Rails.env}' to resolve this issue.")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
35
39
|
# = Active Record Migrations
|
36
40
|
#
|
37
41
|
# Migrations can manage the evolution of a schema used by several physical
|
@@ -46,7 +50,7 @@ module ActiveRecord
|
|
46
50
|
#
|
47
51
|
# class AddSsl < ActiveRecord::Migration
|
48
52
|
# def up
|
49
|
-
# add_column :accounts, :ssl_enabled, :boolean, :
|
53
|
+
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
50
54
|
# end
|
51
55
|
#
|
52
56
|
# def down
|
@@ -58,7 +62,7 @@ module ActiveRecord
|
|
58
62
|
# if you're backing out of the migration. It shows how all migrations have
|
59
63
|
# two methods +up+ and +down+ that describes the transformations
|
60
64
|
# required to implement or remove the migration. These methods can consist
|
61
|
-
# of both the migration specific methods like add_column and remove_column
|
65
|
+
# of both the migration specific methods like +add_column+ and +remove_column+,
|
62
66
|
# but may also contain regular Ruby code for generating data needed for the
|
63
67
|
# transformations.
|
64
68
|
#
|
@@ -74,9 +78,9 @@ module ActiveRecord
|
|
74
78
|
# t.integer :position
|
75
79
|
# end
|
76
80
|
#
|
77
|
-
# SystemSetting.create :
|
78
|
-
# :
|
79
|
-
# :
|
81
|
+
# SystemSetting.create name: 'notice',
|
82
|
+
# label: 'Use notice?',
|
83
|
+
# value: 1
|
80
84
|
# end
|
81
85
|
#
|
82
86
|
# def down
|
@@ -84,19 +88,22 @@ module ActiveRecord
|
|
84
88
|
# end
|
85
89
|
# end
|
86
90
|
#
|
87
|
-
# This migration first adds the system_settings table, then creates the very
|
91
|
+
# This migration first adds the +system_settings+ table, then creates the very
|
88
92
|
# first row in it using the Active Record model that relies on the table. It
|
89
|
-
# also uses the more advanced create_table syntax where you can specify a
|
93
|
+
# also uses the more advanced +create_table+ syntax where you can specify a
|
90
94
|
# complete table schema in one block call.
|
91
95
|
#
|
92
96
|
# == Available transformations
|
93
97
|
#
|
94
|
-
# * <tt>create_table(name, options)</tt
|
98
|
+
# * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
|
95
99
|
# makes the table object available to a block that can then add columns to it,
|
96
|
-
# following the same format as add_column
|
100
|
+
# following the same format as +add_column+. See example above. The options hash
|
97
101
|
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
|
98
102
|
# table definition.
|
99
103
|
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
|
104
|
+
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
105
|
+
# the table called +name+. It makes the table object availabe to a block that
|
106
|
+
# can then add/remove columns, indexes or foreign keys to it.
|
100
107
|
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
|
101
108
|
# to +new_name+.
|
102
109
|
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
|
@@ -105,9 +112,9 @@ module ActiveRecord
|
|
105
112
|
# <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
|
106
113
|
# <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
|
107
114
|
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
|
108
|
-
# specified by passing an +options+ hash like <tt>{ :
|
115
|
+
# specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
|
109
116
|
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
|
110
|
-
# <tt>{ :
|
117
|
+
# <tt>{ limit: 50, null: false }</tt>) -- see
|
111
118
|
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
|
112
119
|
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
|
113
120
|
# a column but keeps the type and content.
|
@@ -118,11 +125,11 @@ module ActiveRecord
|
|
118
125
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
119
126
|
# with the name of the column. Other options include
|
120
127
|
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
121
|
-
# <tt>{ :
|
122
|
-
# (e.g. { :
|
123
|
-
# * <tt>remove_index(table_name, :
|
128
|
+
# <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
|
129
|
+
# (e.g. <tt>{ order: { name: :desc } }</tt>).
|
130
|
+
# * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
|
124
131
|
# specified by +column_name+.
|
125
|
-
# * <tt>remove_index(table_name, :
|
132
|
+
# * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
|
126
133
|
# specified by +index_name+.
|
127
134
|
#
|
128
135
|
# == Irreversible transformations
|
@@ -326,10 +333,30 @@ module ActiveRecord
|
|
326
333
|
class Migration
|
327
334
|
autoload :CommandRecorder, 'active_record/migration/command_recorder'
|
328
335
|
|
336
|
+
|
337
|
+
# This class is used to verify that all migrations have been run before
|
338
|
+
# loading a web page if config.active_record.migration_error is set to :page_load
|
339
|
+
class CheckPending
|
340
|
+
def initialize(app)
|
341
|
+
@app = app
|
342
|
+
end
|
343
|
+
|
344
|
+
def call(env)
|
345
|
+
ActiveRecord::Base.logger.silence do
|
346
|
+
ActiveRecord::Migration.check_pending!
|
347
|
+
end
|
348
|
+
@app.call(env)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
329
352
|
class << self
|
330
353
|
attr_accessor :delegate # :nodoc:
|
331
354
|
end
|
332
355
|
|
356
|
+
def self.check_pending!
|
357
|
+
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
|
358
|
+
end
|
359
|
+
|
333
360
|
def self.method_missing(name, *args, &block) # :nodoc:
|
334
361
|
(delegate || superclass.delegate).send(name, *args, &block)
|
335
362
|
end
|
@@ -342,26 +369,133 @@ module ActiveRecord
|
|
342
369
|
|
343
370
|
attr_accessor :name, :version
|
344
371
|
|
345
|
-
def initialize
|
346
|
-
@name =
|
347
|
-
@version =
|
372
|
+
def initialize(name = self.class.name, version = nil)
|
373
|
+
@name = name
|
374
|
+
@version = version
|
348
375
|
@connection = nil
|
349
|
-
@reverting = false
|
350
376
|
end
|
351
377
|
|
352
378
|
# instantiate the delegate object after initialize is defined
|
353
379
|
self.verbose = true
|
354
380
|
self.delegate = new
|
355
381
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
382
|
+
# Reverses the migration commands for the given block and
|
383
|
+
# the given migrations.
|
384
|
+
#
|
385
|
+
# The following migration will remove the table 'horses'
|
386
|
+
# and create the table 'apples' on the way up, and the reverse
|
387
|
+
# on the way down.
|
388
|
+
#
|
389
|
+
# class FixTLMigration < ActiveRecord::Migration
|
390
|
+
# def change
|
391
|
+
# revert do
|
392
|
+
# create_table(:horses) do |t|
|
393
|
+
# t.text :content
|
394
|
+
# t.datetime :remind_at
|
395
|
+
# end
|
396
|
+
# end
|
397
|
+
# create_table(:apples) do |t|
|
398
|
+
# t.string :variety
|
399
|
+
# end
|
400
|
+
# end
|
401
|
+
# end
|
402
|
+
#
|
403
|
+
# Or equivalently, if +TenderloveMigration+ is defined as in the
|
404
|
+
# documentation for Migration:
|
405
|
+
#
|
406
|
+
# require_relative '2012121212_tenderlove_migration'
|
407
|
+
#
|
408
|
+
# class FixupTLMigration < ActiveRecord::Migration
|
409
|
+
# def change
|
410
|
+
# revert TenderloveMigration
|
411
|
+
#
|
412
|
+
# create_table(:apples) do |t|
|
413
|
+
# t.string :variety
|
414
|
+
# end
|
415
|
+
# end
|
416
|
+
# end
|
417
|
+
#
|
418
|
+
# This command can be nested.
|
419
|
+
def revert(*migration_classes)
|
420
|
+
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
|
421
|
+
if block_given?
|
422
|
+
if @connection.respond_to? :revert
|
423
|
+
@connection.revert { yield }
|
424
|
+
else
|
425
|
+
recorder = CommandRecorder.new(@connection)
|
426
|
+
@connection = recorder
|
427
|
+
suppress_messages do
|
428
|
+
@connection.revert { yield }
|
429
|
+
end
|
430
|
+
@connection = recorder.delegate
|
431
|
+
recorder.commands.each do |cmd, args, block|
|
432
|
+
send(cmd, *args, &block)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
361
436
|
end
|
362
437
|
|
363
438
|
def reverting?
|
364
|
-
@reverting
|
439
|
+
@connection.respond_to?(:reverting) && @connection.reverting
|
440
|
+
end
|
441
|
+
|
442
|
+
class ReversibleBlockHelper < Struct.new(:reverting)
|
443
|
+
def up
|
444
|
+
yield unless reverting
|
445
|
+
end
|
446
|
+
|
447
|
+
def down
|
448
|
+
yield if reverting
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# Used to specify an operation that can be run in one direction or another.
|
453
|
+
# Call the methods +up+ and +down+ of the yielded object to run a block
|
454
|
+
# only in one given direction.
|
455
|
+
# The whole block will be called in the right order within the migration.
|
456
|
+
#
|
457
|
+
# In the following example, the looping on users will always be done
|
458
|
+
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
459
|
+
# even when migrating down:
|
460
|
+
#
|
461
|
+
# class SplitNameMigration < ActiveRecord::Migration
|
462
|
+
# def change
|
463
|
+
# add_column :users, :first_name, :string
|
464
|
+
# add_column :users, :last_name, :string
|
465
|
+
#
|
466
|
+
# reversible do |dir|
|
467
|
+
# User.reset_column_information
|
468
|
+
# User.all.each do |u|
|
469
|
+
# dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
|
470
|
+
# dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
|
471
|
+
# u.save
|
472
|
+
# end
|
473
|
+
# end
|
474
|
+
#
|
475
|
+
# revert { add_column :users, :full_name, :string }
|
476
|
+
# end
|
477
|
+
# end
|
478
|
+
def reversible
|
479
|
+
helper = ReversibleBlockHelper.new(reverting?)
|
480
|
+
execute_block{ yield helper }
|
481
|
+
end
|
482
|
+
|
483
|
+
# Runs the given migration classes.
|
484
|
+
# Last argument can specify options:
|
485
|
+
# - :direction (default is :up)
|
486
|
+
# - :revert (default is false)
|
487
|
+
def run(*migration_classes)
|
488
|
+
opts = migration_classes.extract_options!
|
489
|
+
dir = opts[:direction] || :up
|
490
|
+
dir = (dir == :down ? :up : :down) if opts[:revert]
|
491
|
+
if reverting?
|
492
|
+
# If in revert and going :up, say, we want to execute :down without reverting, so
|
493
|
+
revert { run(*migration_classes, direction: dir, revert: true) }
|
494
|
+
else
|
495
|
+
migration_classes.each do |migration_class|
|
496
|
+
migration_class.new.exec_migration(@connection, dir)
|
497
|
+
end
|
498
|
+
end
|
365
499
|
end
|
366
500
|
|
367
501
|
def up
|
@@ -387,29 +521,9 @@ module ActiveRecord
|
|
387
521
|
|
388
522
|
time = nil
|
389
523
|
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
390
|
-
|
391
|
-
|
392
|
-
if direction == :down
|
393
|
-
recorder = CommandRecorder.new(@connection)
|
394
|
-
suppress_messages do
|
395
|
-
@connection = recorder
|
396
|
-
change
|
397
|
-
end
|
398
|
-
@connection = conn
|
399
|
-
time = Benchmark.measure {
|
400
|
-
self.revert {
|
401
|
-
recorder.inverse.each do |cmd, args|
|
402
|
-
send(cmd, *args)
|
403
|
-
end
|
404
|
-
}
|
405
|
-
}
|
406
|
-
else
|
407
|
-
time = Benchmark.measure { change }
|
408
|
-
end
|
409
|
-
else
|
410
|
-
time = Benchmark.measure { send(direction) }
|
524
|
+
time = Benchmark.measure do
|
525
|
+
exec_migration(conn, direction)
|
411
526
|
end
|
412
|
-
@connection = nil
|
413
527
|
end
|
414
528
|
|
415
529
|
case direction
|
@@ -418,6 +532,21 @@ module ActiveRecord
|
|
418
532
|
end
|
419
533
|
end
|
420
534
|
|
535
|
+
def exec_migration(conn, direction)
|
536
|
+
@connection = conn
|
537
|
+
if respond_to?(:change)
|
538
|
+
if direction == :down
|
539
|
+
revert { change }
|
540
|
+
else
|
541
|
+
change
|
542
|
+
end
|
543
|
+
else
|
544
|
+
send(direction)
|
545
|
+
end
|
546
|
+
ensure
|
547
|
+
@connection = nil
|
548
|
+
end
|
549
|
+
|
421
550
|
def write(text="")
|
422
551
|
puts(text) if verbose
|
423
552
|
end
|
@@ -456,9 +585,9 @@ module ActiveRecord
|
|
456
585
|
arg_list = arguments.map{ |a| a.inspect } * ', '
|
457
586
|
|
458
587
|
say_with_time "#{method}(#{arg_list})" do
|
459
|
-
unless
|
588
|
+
unless @connection.respond_to? :revert
|
460
589
|
unless arguments.empty? || method == :execute
|
461
|
-
arguments[0] = Migrator.proper_table_name(arguments.first)
|
590
|
+
arguments[0] = Migrator.proper_table_name(arguments.first)
|
462
591
|
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
463
592
|
end
|
464
593
|
end
|
@@ -510,6 +639,15 @@ module ActiveRecord
|
|
510
639
|
"%.3d" % number
|
511
640
|
end
|
512
641
|
end
|
642
|
+
|
643
|
+
private
|
644
|
+
def execute_block
|
645
|
+
if connection.respond_to? :execute_block
|
646
|
+
super # use normal delegation to record the block
|
647
|
+
else
|
648
|
+
yield
|
649
|
+
end
|
650
|
+
end
|
513
651
|
end
|
514
652
|
|
515
653
|
# MigrationProxy is used to defer loading of the actual migration classes
|
@@ -547,14 +685,14 @@ module ActiveRecord
|
|
547
685
|
|
548
686
|
def migrate(migrations_paths, target_version = nil, &block)
|
549
687
|
case
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
688
|
+
when target_version.nil?
|
689
|
+
up(migrations_paths, target_version, &block)
|
690
|
+
when current_version == 0 && target_version == 0
|
691
|
+
[]
|
692
|
+
when current_version > target_version
|
693
|
+
down(migrations_paths, target_version, &block)
|
694
|
+
else
|
695
|
+
up(migrations_paths, target_version, &block)
|
558
696
|
end
|
559
697
|
end
|
560
698
|
|
@@ -566,25 +704,34 @@ module ActiveRecord
|
|
566
704
|
move(:up, migrations_paths, steps)
|
567
705
|
end
|
568
706
|
|
569
|
-
def up(migrations_paths, target_version = nil
|
570
|
-
|
707
|
+
def up(migrations_paths, target_version = nil)
|
708
|
+
migrations = migrations(migrations_paths)
|
709
|
+
migrations.select! { |m| yield m } if block_given?
|
710
|
+
|
711
|
+
self.new(:up, migrations, target_version).migrate
|
571
712
|
end
|
572
713
|
|
573
714
|
def down(migrations_paths, target_version = nil, &block)
|
574
|
-
|
715
|
+
migrations = migrations(migrations_paths)
|
716
|
+
migrations.select! { |m| yield m } if block_given?
|
717
|
+
|
718
|
+
self.new(:down, migrations, target_version).migrate
|
575
719
|
end
|
576
720
|
|
577
721
|
def run(direction, migrations_paths, target_version)
|
578
|
-
self.new(direction, migrations_paths, target_version).run
|
722
|
+
self.new(direction, migrations(migrations_paths), target_version).run
|
723
|
+
end
|
724
|
+
|
725
|
+
def open(migrations_paths)
|
726
|
+
self.new(:up, migrations(migrations_paths), nil)
|
579
727
|
end
|
580
728
|
|
581
729
|
def schema_migrations_table_name
|
582
|
-
|
730
|
+
SchemaMigration.table_name
|
583
731
|
end
|
584
732
|
|
585
733
|
def get_all_versions
|
586
|
-
|
587
|
-
Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
|
734
|
+
SchemaMigration.all.map { |x| x.version.to_i }.sort
|
588
735
|
end
|
589
736
|
|
590
737
|
def current_version
|
@@ -596,35 +743,37 @@ module ActiveRecord
|
|
596
743
|
end
|
597
744
|
end
|
598
745
|
|
746
|
+
def needs_migration?
|
747
|
+
current_version < last_version
|
748
|
+
end
|
749
|
+
|
750
|
+
def last_version
|
751
|
+
migrations(migrations_paths).last.try(:version)||0
|
752
|
+
end
|
753
|
+
|
599
754
|
def proper_table_name(name)
|
600
755
|
# Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
|
601
|
-
name.table_name
|
756
|
+
if name.respond_to? :table_name
|
757
|
+
name.table_name
|
758
|
+
else
|
759
|
+
"#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
|
760
|
+
end
|
602
761
|
end
|
603
762
|
|
604
763
|
def migrations_paths
|
605
764
|
@migrations_paths ||= ['db/migrate']
|
606
765
|
# just to not break things if someone uses: migration_path = some_string
|
607
|
-
Array
|
766
|
+
Array(@migrations_paths)
|
608
767
|
end
|
609
768
|
|
610
769
|
def migrations_path
|
611
770
|
migrations_paths.first
|
612
771
|
end
|
613
772
|
|
614
|
-
def migrations(paths
|
615
|
-
|
616
|
-
subdirectories = true
|
617
|
-
else
|
618
|
-
subdirectories = args.first
|
619
|
-
ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
|
620
|
-
end
|
621
|
-
|
622
|
-
paths = Array.wrap(paths)
|
623
|
-
|
624
|
-
glob = subdirectories ? "**/" : ""
|
625
|
-
files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
|
773
|
+
def migrations(paths)
|
774
|
+
paths = Array(paths)
|
626
775
|
|
627
|
-
|
776
|
+
files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
|
628
777
|
|
629
778
|
migrations = files.map do |file|
|
630
779
|
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
|
@@ -633,11 +782,6 @@ module ActiveRecord
|
|
633
782
|
version = version.to_i
|
634
783
|
name = name.camelize
|
635
784
|
|
636
|
-
raise DuplicateMigrationVersionError.new(version) if seen[version]
|
637
|
-
raise DuplicateMigrationNameError.new(name) if seen[name]
|
638
|
-
|
639
|
-
seen[version] = seen[name] = true
|
640
|
-
|
641
785
|
MigrationProxy.new(name, version, file, scope)
|
642
786
|
end
|
643
787
|
|
@@ -647,7 +791,7 @@ module ActiveRecord
|
|
647
791
|
private
|
648
792
|
|
649
793
|
def move(direction, migrations_paths, steps)
|
650
|
-
migrator = self.new(direction, migrations_paths)
|
794
|
+
migrator = self.new(direction, migrations(migrations_paths))
|
651
795
|
start_index = migrator.migrations.index(migrator.current_migration)
|
652
796
|
|
653
797
|
if start_index
|
@@ -658,19 +802,33 @@ module ActiveRecord
|
|
658
802
|
end
|
659
803
|
end
|
660
804
|
|
661
|
-
def initialize(direction,
|
805
|
+
def initialize(direction, migrations, target_version = nil)
|
662
806
|
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
|
663
|
-
|
664
|
-
@direction
|
807
|
+
|
808
|
+
@direction = direction
|
809
|
+
@target_version = target_version
|
810
|
+
@migrated_versions = nil
|
811
|
+
|
812
|
+
if Array(migrations).grep(String).empty?
|
813
|
+
@migrations = migrations
|
814
|
+
else
|
815
|
+
ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
|
816
|
+
@migrations = self.class.migrations(migrations)
|
817
|
+
end
|
818
|
+
|
819
|
+
validate(@migrations)
|
820
|
+
|
821
|
+
ActiveRecord::SchemaMigration.create_table
|
665
822
|
end
|
666
823
|
|
667
824
|
def current_version
|
668
|
-
migrated.last || 0
|
825
|
+
migrated.sort.last || 0
|
669
826
|
end
|
670
827
|
|
671
828
|
def current_migration
|
672
829
|
migrations.detect { |m| m.version == current_version }
|
673
830
|
end
|
831
|
+
alias :current :current_migration
|
674
832
|
|
675
833
|
def run
|
676
834
|
target = migrations.detect { |m| m.version == @target_version }
|
@@ -681,101 +839,108 @@ module ActiveRecord
|
|
681
839
|
end
|
682
840
|
end
|
683
841
|
|
684
|
-
def migrate
|
685
|
-
|
686
|
-
target = migrations.detect { |m| m.version == @target_version }
|
687
|
-
|
688
|
-
if target.nil? && @target_version && @target_version > 0
|
842
|
+
def migrate
|
843
|
+
if !target && @target_version && @target_version > 0
|
689
844
|
raise UnknownMigrationVersionError.new(@target_version)
|
690
845
|
end
|
691
846
|
|
692
|
-
|
693
|
-
finish = migrations.index(target) || migrations.size - 1
|
694
|
-
runnable = migrations[start..finish]
|
847
|
+
running = runnable
|
695
848
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
if block && !block.call(migration)
|
702
|
-
next
|
703
|
-
end
|
849
|
+
if block_given?
|
850
|
+
message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
|
851
|
+
ActiveSupport::Deprecation.warn message
|
852
|
+
running.select! { |m| yield m }
|
853
|
+
end
|
704
854
|
|
855
|
+
running.each do |migration|
|
705
856
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
706
857
|
|
707
|
-
seen = migrated.include?(migration.version.to_i)
|
708
|
-
|
709
|
-
# On our way up, we skip migrating the ones we've already migrated
|
710
|
-
next if up? && seen
|
711
|
-
|
712
|
-
# On our way down, we skip reverting the ones we've never migrated
|
713
|
-
if down? && !seen
|
714
|
-
migration.announce 'never migrated, skipping'; migration.write
|
715
|
-
next
|
716
|
-
end
|
717
|
-
|
718
858
|
begin
|
719
859
|
ddl_transaction do
|
720
860
|
migration.migrate(@direction)
|
721
861
|
record_version_state_after_migrating(migration.version)
|
722
862
|
end
|
723
|
-
ran << migration
|
724
863
|
rescue => e
|
725
864
|
canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
|
726
865
|
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
|
727
866
|
end
|
728
867
|
end
|
729
|
-
ran
|
730
868
|
end
|
731
869
|
|
732
|
-
def
|
733
|
-
|
734
|
-
|
735
|
-
|
870
|
+
def runnable
|
871
|
+
runnable = migrations[start..finish]
|
872
|
+
if up?
|
873
|
+
runnable.reject { |m| ran?(m) }
|
874
|
+
else
|
875
|
+
# skip the last migration if we're headed down, but not ALL the way down
|
876
|
+
runnable.pop if target
|
877
|
+
runnable.find_all { |m| ran?(m) }
|
736
878
|
end
|
737
879
|
end
|
738
880
|
|
881
|
+
def migrations
|
882
|
+
down? ? @migrations.reverse : @migrations.sort_by(&:version)
|
883
|
+
end
|
884
|
+
|
739
885
|
def pending_migrations
|
740
886
|
already_migrated = migrated
|
741
|
-
migrations.reject { |m| already_migrated.include?(m.version
|
887
|
+
migrations.reject { |m| already_migrated.include?(m.version) }
|
742
888
|
end
|
743
889
|
|
744
890
|
def migrated
|
745
|
-
@migrated_versions ||= self.class.get_all_versions
|
891
|
+
@migrated_versions ||= Set.new(self.class.get_all_versions)
|
746
892
|
end
|
747
893
|
|
748
894
|
private
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
@migrated_versions ||= []
|
753
|
-
if down?
|
754
|
-
@migrated_versions.delete(version)
|
755
|
-
stmt = table.where(table["version"].eq(version.to_s)).compile_delete
|
756
|
-
Base.connection.delete stmt
|
757
|
-
else
|
758
|
-
@migrated_versions.push(version).sort!
|
759
|
-
stmt = table.compile_insert table["version"] => version.to_s
|
760
|
-
Base.connection.insert stmt
|
761
|
-
end
|
762
|
-
end
|
895
|
+
def ran?(migration)
|
896
|
+
migrated.include?(migration.version.to_i)
|
897
|
+
end
|
763
898
|
|
764
|
-
|
765
|
-
|
766
|
-
|
899
|
+
def target
|
900
|
+
migrations.detect { |m| m.version == @target_version }
|
901
|
+
end
|
902
|
+
|
903
|
+
def finish
|
904
|
+
migrations.index(target) || migrations.size - 1
|
905
|
+
end
|
906
|
+
|
907
|
+
def start
|
908
|
+
up? ? 0 : (migrations.index(current) || 0)
|
909
|
+
end
|
767
910
|
|
768
|
-
|
769
|
-
|
911
|
+
def validate(migrations)
|
912
|
+
name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
|
913
|
+
raise DuplicateMigrationNameError.new(name) if name
|
914
|
+
|
915
|
+
version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
|
916
|
+
raise DuplicateMigrationVersionError.new(version) if version
|
917
|
+
end
|
918
|
+
|
919
|
+
def record_version_state_after_migrating(version)
|
920
|
+
if down?
|
921
|
+
migrated.delete(version)
|
922
|
+
ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
|
923
|
+
else
|
924
|
+
migrated << version
|
925
|
+
ActiveRecord::SchemaMigration.create!(:version => version.to_s)
|
770
926
|
end
|
927
|
+
end
|
771
928
|
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
929
|
+
def up?
|
930
|
+
@direction == :up
|
931
|
+
end
|
932
|
+
|
933
|
+
def down?
|
934
|
+
@direction == :down
|
935
|
+
end
|
936
|
+
|
937
|
+
# Wrap the migration in a transaction only if supported by the adapter.
|
938
|
+
def ddl_transaction
|
939
|
+
if Base.connection.supports_ddl_transactions?
|
940
|
+
Base.transaction { yield }
|
941
|
+
else
|
942
|
+
yield
|
779
943
|
end
|
944
|
+
end
|
780
945
|
end
|
781
946
|
end
|