aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md18
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb16
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb32
-rw-r--r--activerecord/lib/active_record/railties/databases.rake19
-rw-r--r--activerecord/lib/active_record/reflection.rb83
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb7
-rw-r--r--activerecord/test/cases/migrator_test.rb61
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb2
11 files changed, 197 insertions, 81 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 55773f2172..abc415ea14 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Fix `rake db:schema:load` with subdirectories.
+
+ *Ryuta Kamizono*
+
+* Fix `rake db:migrate:status` with subdirectories.
+
+ *Ryuta Kamizono*
+
+* Don't share options between reference id and type columns
+
+ When using a polymorphic reference column in a migration, sharing options
+ between the two columns doesn't make sense since they are different types.
+ The `reference_id` column is usually an integer and the `reference_type`
+ column a string so options like `unsigned: true` will result in an invalid
+ table definition.
+
+ *Ryuta Kamizono*
+
* Use `max_identifier_length` for `index_name_length` in PostgreSQL adapter.
*Ryuta Kamizono*
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index badde9973f..120d75416c 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -25,7 +25,7 @@ module ActiveRecord
chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
scope.extending! Array(reflection.options[:extend])
- add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
+ add_constraints(scope, owner, reflection, chain_head, chain_tail)
end
def join_type
@@ -60,8 +60,8 @@ module ActiveRecord
table.create_join(table, table.create_on(constraint), join_type)
end
- def last_chain_scope(scope, table, reflection, owner, association_klass)
- join_keys = reflection.join_keys(association_klass)
+ def last_chain_scope(scope, table, reflection, owner)
+ join_keys = reflection.join_keys
key = join_keys.key
foreign_key = join_keys.foreign_key
@@ -80,8 +80,8 @@ module ActiveRecord
value_transformation.call(value)
end
- def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
- join_keys = reflection.join_keys(association_klass)
+ def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
+ join_keys = reflection.join_keys
key = join_keys.key
foreign_key = join_keys.foreign_key
@@ -120,10 +120,10 @@ module ActiveRecord
[runtime_reflection, previous_reflection]
end
- def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
+ def add_constraints(scope, owner, refl, chain_head, chain_tail)
owner_reflection = chain_tail
table = owner_reflection.alias_name
- scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
+ scope = last_chain_scope(scope, table, owner_reflection, owner)
reflection = chain_head
while reflection
@@ -132,7 +132,7 @@ module ActiveRecord
unless reflection == chain_tail
foreign_table = next_reflection.alias_name
- scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
+ scope = next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
end
# Exclude the scope of the association itself, because that
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index 935777d485..97cfec0302 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -34,35 +34,16 @@ module ActiveRecord
table = tables.shift
klass = reflection.klass
- join_keys = reflection.join_keys(klass)
+ join_keys = reflection.join_keys
key = join_keys.key
foreign_key = join_keys.foreign_key
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
- scope_chain_items = reflection.scopes.map do |item|
- if item.is_a?(Relation)
- item
- else
- ActiveRecord::Relation.create(klass, table, predicate_builder)
- .instance_exec(&item)
- end
- end
+ scope_chain_items = reflection.join_scopes(table, predicate_builder)
+ klass_scope = reflection.klass_join_scope(table, predicate_builder)
- klass_scope =
- if klass.current_scope
- klass.current_scope.clone.tap { |scope|
- scope.joins_values = []
- }
- else
- relation = ActiveRecord::Relation.create(
- klass,
- table,
- predicate_builder,
- )
- klass.send(:build_default_scope, relation)
- end
scope_chain_items.concat [klass_scope].compact
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 5eb7787226..4682afc188 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -116,16 +116,12 @@ module ActiveRecord
private
- def as_options(value, default = {})
- if value.is_a?(Hash)
- value
- else
- default
- end
+ def as_options(value)
+ value.is_a?(Hash) ? value : {}
end
def polymorphic_options
- as_options(polymorphic, options)
+ as_options(polymorphic)
end
def index_options
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index e683106527..c9dd915e98 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1028,9 +1028,8 @@ module ActiveRecord
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
- paths = migrations_paths.map { |p| "#{p}/[0-9]*_*.rb" }
- versions = Dir[*paths].map do |filename|
- filename.split("/").last.split("_").first.to_i
+ versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
+ ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
end
unless migrated.include?(version)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 263a8a7da3..c47e32df59 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1056,10 +1056,6 @@ module ActiveRecord
Array(@migrations_paths)
end
- def match_to_migration_filename?(filename) # :nodoc:
- Migration::MigrationFilenameRegexp.match?(File.basename(filename))
- end
-
def parse_migration_filename(filename) # :nodoc:
File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
end
@@ -1067,9 +1063,7 @@ module ActiveRecord
def migrations(paths)
paths = Array(paths)
- files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
-
- migrations = files.map do |file|
+ migrations = migration_files(paths).map do |file|
version, name, scope = parse_migration_filename(file)
raise IllegalMigrationNameError.new(file) unless version
version = version.to_i
@@ -1081,6 +1075,30 @@ module ActiveRecord
migrations.sort_by(&:version)
end
+ def migrations_status(paths)
+ paths = Array(paths)
+
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
+
+ file_list = migration_files(paths).map do |file|
+ version, name, scope = parse_migration_filename(file)
+ raise IllegalMigrationNameError.new(file) unless version
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
+ status = db_list.delete(version) ? "up" : "down"
+ [status, version, (name + scope).humanize]
+ end.compact
+
+ db_list.map! do |version|
+ ["up", version, "********** NO FILE **********"]
+ end
+
+ (db_list + file_list).sort_by { |_, version, _| version }
+ end
+
+ def migration_files(paths)
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
+ end
+
private
def move(direction, migrations_paths, steps)
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index fabd326649..1c7206aca4 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -110,28 +110,13 @@ db_namespace = namespace :db do
unless ActiveRecord::SchemaMigration.table_exists?
abort "Schema migrations table does not exist yet."
end
- db_list = ActiveRecord::SchemaMigration.normalized_versions
-
- file_list =
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path|
- Dir.foreach(path).map do |file|
- next unless ActiveRecord::Migrator.match_to_migration_filename?(file)
-
- version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
- status = db_list.delete(version) ? "up" : "down"
- [status, version, (name + scope).humanize]
- end.compact
- end
- db_list.map! do |version|
- ["up", version, "********** NO FILE **********"]
- end
# output
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
puts "-" * 50
- (db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
+ paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
+ ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name|
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
end
puts
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 61a2279292..24ca8b0be4 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -172,8 +172,8 @@ module ActiveRecord
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
- def join_keys(association_klass)
- JoinKeys.new(foreign_key, active_record_primary_key)
+ def join_keys
+ get_join_keys klass
end
# Returns a list of scopes that should be applied for this Reflection
@@ -187,6 +187,30 @@ module ActiveRecord
end
deprecate :scope_chain
+ def join_scopes(table, predicate_builder) # :nodoc:
+ if scope
+ [ActiveRecord::Relation.create(klass, table, predicate_builder)
+ .instance_exec(&scope)]
+ else
+ []
+ end
+ end
+
+ def klass_join_scope(table, predicate_builder) # :nodoc:
+ if klass.current_scope
+ klass.current_scope.clone.tap { |scope|
+ scope.joins_values = []
+ }
+ else
+ relation = ActiveRecord::Relation.create(
+ klass,
+ table,
+ predicate_builder,
+ )
+ klass.send(:build_default_scope, relation)
+ end
+ end
+
def constraints
chain.map(&:scopes).flatten
end
@@ -260,6 +284,20 @@ module ActiveRecord
def chain
collect_join_chain
end
+
+ def get_join_keys(association_klass)
+ JoinKeys.new(join_pk(association_klass), join_fk)
+ end
+
+ private
+
+ def join_pk(_)
+ foreign_key
+ end
+
+ def join_fk
+ active_record_primary_key
+ end
end
# Base class for AggregateReflection and AssociationReflection. Objects of
@@ -687,11 +725,6 @@ module ActiveRecord
end
end
- def join_keys(association_klass)
- key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
- JoinKeys.new(key, foreign_key)
- end
-
def join_id_for(owner) # :nodoc:
owner[foreign_key]
end
@@ -701,6 +734,14 @@ module ActiveRecord
def calculate_constructable(macro, options)
!polymorphic?
end
+
+ def join_fk
+ foreign_key
+ end
+
+ def join_pk(klass)
+ polymorphic? ? association_primary_key(klass) : association_primary_key
+ end
end
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
@@ -720,7 +761,7 @@ module ActiveRecord
class ThroughReflection < AbstractReflection #:nodoc:
attr_reader :delegate_reflection
delegate :foreign_key, :foreign_type, :association_foreign_key,
- :active_record_primary_key, :type, to: :source_reflection
+ :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
def initialize(delegate_reflection)
@delegate_reflection = delegate_reflection
@@ -806,6 +847,10 @@ module ActiveRecord
source_reflection.scopes + super
end
+ def join_scopes(table, predicate_builder) # :nodoc:
+ source_reflection.join_scopes(table, predicate_builder) + super
+ end
+
def source_type_scope
through_reflection.klass.where(foreign_type => options[:source_type])
end
@@ -816,10 +861,6 @@ module ActiveRecord
through_reflection.has_scope?
end
- def join_keys(association_klass)
- source_reflection.join_keys(association_klass)
- end
-
# A through association is nested if there would be more than one join table
def nested?
source_reflection.through_reflection? || through_reflection.through_reflection?
@@ -954,6 +995,7 @@ module ActiveRecord
end
private
+
def actual_source_reflection # FIXME: this is a horrible name
source_reflection.send(:actual_source_reflection)
end
@@ -990,6 +1032,15 @@ module ActiveRecord
end
end
+ def join_scopes(table, predicate_builder) # :nodoc:
+ scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
+ if @previous_reflection.options[:source_type]
+ scopes + [@previous_reflection.source_type_scope]
+ else
+ scopes
+ end
+ end
+
def klass
@reflection.klass
end
@@ -1006,10 +1057,6 @@ module ActiveRecord
@reflection.plural_name
end
- def join_keys(association_klass)
- @reflection.join_keys(association_klass)
- end
-
def type
@reflection.type
end
@@ -1023,6 +1070,10 @@ module ActiveRecord
source_type = @previous_reflection.options[:source_type]
lambda { |object| where(type => source_type) }
end
+
+ def get_join_keys(association_klass)
+ @reflection.get_join_keys(association_klass)
+ end
end
class RuntimeReflection < PolymorphicReflection # :nodoc:
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index df15d7cb45..06c44c8c52 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -50,6 +50,13 @@ module ActiveRecord
assert column_exists?(table_name, :taggable_type, :string, default: "Photo")
end
+ def test_does_not_share_options_with_reference_type_column
+ add_reference table_name, :taggable, type: :integer, limit: 2, polymorphic: true
+ assert column_exists?(table_name, :taggable_id, :integer, limit: 2)
+ assert column_exists?(table_name, :taggable_type, :string)
+ assert_not column_exists?(table_name, :taggable_type, :string, limit: 2)
+ end
+
def test_creates_named_index
add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id" }
assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id")
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 20d70b75ac..aadbc375af 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -124,6 +124,67 @@ class MigratorTest < ActiveRecord::TestCase
assert_equal migration_list.last, migrations.first
end
+ def test_migrations_status
+ path = MIGRATIONS_ROOT + "/valid"
+
+ ActiveRecord::SchemaMigration.create(version: 2)
+ ActiveRecord::SchemaMigration.create(version: 10)
+
+ assert_equal [
+ ["down", "001", "Valid people have last names"],
+ ["up", "002", "We need reminders"],
+ ["down", "003", "Innocent jointable"],
+ ["up", "010", "********** NO FILE **********"],
+ ], ActiveRecord::Migrator.migrations_status(path)
+ end
+
+ def test_migrations_status_in_subdirectories
+ path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
+
+ ActiveRecord::SchemaMigration.create(version: 2)
+ ActiveRecord::SchemaMigration.create(version: 10)
+
+ assert_equal [
+ ["down", "001", "Valid people have last names"],
+ ["up", "002", "We need reminders"],
+ ["down", "003", "Innocent jointable"],
+ ["up", "010", "********** NO FILE **********"],
+ ], ActiveRecord::Migrator.migrations_status(path)
+ end
+
+ def test_migrations_status_with_schema_define_in_subdirectories
+ path = MIGRATIONS_ROOT + "/valid_with_subdirectories"
+ prev_paths = ActiveRecord::Migrator.migrations_paths
+ ActiveRecord::Migrator.migrations_paths = path
+
+ ActiveRecord::Schema.define(version: 3) do
+ end
+
+ assert_equal [
+ ["up", "001", "Valid people have last names"],
+ ["up", "002", "We need reminders"],
+ ["up", "003", "Innocent jointable"],
+ ], ActiveRecord::Migrator.migrations_status(path)
+ ensure
+ ActiveRecord::Migrator.migrations_paths = prev_paths
+ end
+
+ def test_migrations_status_from_two_directories
+ paths = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
+
+ ActiveRecord::SchemaMigration.create(version: "20100101010101")
+ ActiveRecord::SchemaMigration.create(version: "20160528010101")
+
+ assert_equal [
+ ["down", "20090101010101", "People have hobbies"],
+ ["down", "20090101010202", "People have descriptions"],
+ ["up", "20100101010101", "Valid with timestamps people have last names"],
+ ["down", "20100201010101", "Valid with timestamps we need reminders"],
+ ["down", "20100301010101", "Valid with timestamps innocent jointable"],
+ ["up", "20160528010101", "********** NO FILE **********"],
+ ], ActiveRecord::Migrator.migrations_status(paths)
+ end
+
def test_migrator_interleaved_migrations
pass_one = [Sensor.new("One", 1)]
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 277280b42e..28605d2f8e 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -385,7 +385,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validate_uniqueness_with_limit_and_utf8
if current_adapter?(:SQLite3Adapter)
- # Event.title has limit 5, but does SQLite doesn't truncate.
+ # Event.title has limit 5, but SQLite doesn't truncate.
e1 = Event.create(title: "一二三四五六七八")
assert e1.valid?, "Could not create an event with a unique 8 characters title"