aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionview/lib/action_view/digestor.rb3
-rw-r--r--actionview/test/template/digestor_test.rb20
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb7
-rw-r--r--activerecord/lib/active_record/enum.rb42
-rw-r--r--activerecord/test/cases/enum_test.rb50
-rw-r--r--activerecord/test/fixtures/books.yml4
-rw-r--r--activerecord/test/models/book.rb4
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--guides/source/active_record_migrations.md3
-rw-r--r--railties/test/application/rake_test.rb2
12 files changed, 135 insertions, 16 deletions
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index d34149143f..96f161fb09 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -165,7 +165,7 @@ module ActionController
# class BooksControllerTest < ActionController::TestCase
# def test_create
# # Simulate a POST response with the given HTTP parameters.
- # post(:create, book: { title: "Love Hina" })
+ # post(:create, params: { book: { title: "Love Hina" }})
#
# # Assert that the controller tried to redirect us to
# # the created book's URI.
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 1f103786cb..b29eb48425 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -70,7 +70,8 @@ module ActionView
def dependencies
DependencyTracker.find_dependencies(name, template)
rescue ActionView::MissingTemplate
- [] # File doesn't exist, so no dependencies
+ logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
+ []
end
def nested_dependencies
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index c2b8439df3..f0afcdb5ae 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -111,6 +111,18 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_logging_of_missing_template_for_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ dependencies("messages/something_missing")
+ end
+ end
+
+ def test_logging_of_missing_template_for_nested_dependencies
+ assert_logged "'messages/something_missing' file doesn't exist, so no dependencies" do
+ nested_dependencies("messages/something_missing")
+ end
+ end
+
def test_nested_template_directory
assert_digest_difference("messages/show") do
change_template("messages/actions/_move")
@@ -298,6 +310,14 @@ class TemplateDigestorTest < ActionView::TestCase
ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options))
end
+ def dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).dependencies
+ end
+
+ def nested_dependencies(template_name)
+ ActionView::Digestor.new({ name: template_name, finder: finder }).nested_dependencies
+ end
+
def finder
@finder ||= FixtureFinder.new
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 76d65e41de..14906e441b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,8 +1,14 @@
-* Make remove_foreign_key invertible. Any foreign key options must be
- specified, similar to remove_column.
+* Make `remove_foreign_key` reversible. Any foreign key options must be
+ specified, similar to `remove_column`.
*Aster Ryan*
+* Add `:enum_prefix`/`:enum_suffix` option to `enum` definition.
+
+ Fixes #17511 and #17415
+
+ *Igor Kapkov*
+
* Correctly handle decimal arrays with defaults in the schema dumper.
Fixes #20515.
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 a89886721a..c8be038d76 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -777,8 +777,8 @@ module ActiveRecord
execute schema_creation.accept(at)
end
- # Removes the given foreign key from the table. Any options parameters provided
- # will be used to re-add the foreign key in case of a migration reversion.
+ # Removes the given foreign key from the table. Any option parameters provided
+ # will be used to re-add the foreign key in case of a migration rollback.
# It is recommended that you provide any options used when creating the foreign
# key so that the migration can be reverted properly.
#
@@ -794,8 +794,7 @@ module ActiveRecord
#
# remove_foreign_key :accounts, name: :special_fk_name
#
- # The +options+ hash can include all keys that can be used in add_foreign_key.
- # Please refer to the add_foreign_key documentation for a full list
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
def remove_foreign_key(from_table, options_or_to_table = {})
return unless supports_foreign_keys?
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 2b99899e42..ad33c84dbc 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -75,6 +75,22 @@ module ActiveRecord
#
# Conversation.where("status <> ?", Conversation.statuses[:archived])
#
+ # You can use <tt>:enum_prefix</tt>/<tt>:enum_suffix</tt> option then you need
+ # to define multiple enums with same values. If option value is <tt>true</tt>,
+ # the methods are prefixed/suffixed with the name of the enum.
+ #
+ # class Invoice < ActiveRecord::Base
+ # enum verification: [:done, :fail], enum_prefix: true
+ # end
+ #
+ # It is also possible to supply a custom prefix.
+ #
+ # class Invoice < ActiveRecord::Base
+ # enum verification: [:done, :fail], enum_prefix: :verification_status
+ # end
+ #
+ # Note that <tt>:enum_prefix</tt>/<tt>:enum_postfix</tt> are reserved keywords
+ # and can not be used as an enum name.
module Enum
def self.extended(base) # :nodoc:
@@ -121,6 +137,8 @@ module ActiveRecord
def enum(definitions)
klass = self
+ enum_prefix = definitions.delete(:enum_prefix)
+ enum_suffix = definitions.delete(:enum_suffix)
definitions.each do |name, values|
# statuses = { }
enum_values = ActiveSupport::HashWithIndifferentAccess.new
@@ -138,19 +156,31 @@ module ActiveRecord
_enum_methods_module.module_eval do
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
+ if enum_prefix == true
+ prefix = "#{name}_"
+ elsif enum_prefix
+ prefix = "#{enum_prefix}_"
+ end
+ if enum_suffix == true
+ suffix = "_#{name}"
+ elsif enum_suffix
+ suffix = "_#{enum_suffix}"
+ end
+
+ value_method_name = "#{prefix}#{value}#{suffix}"
enum_values[value] = i
# def active?() status == 0 end
- klass.send(:detect_enum_conflict!, name, "#{value}?")
- define_method("#{value}?") { self[name] == value.to_s }
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
# def active!() update! status: :active end
- klass.send(:detect_enum_conflict!, name, "#{value}!")
- define_method("#{value}!") { update! name => value }
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
+ define_method("#{value_method_name}!") { update! name => value }
# scope :active, -> { where status: 0 }
- klass.send(:detect_enum_conflict!, name, value, true)
- klass.scope value, -> { klass.where name => value }
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
+ klass.scope value_method_name, -> { klass.where name => value }
end
end
defined_enums[name.to_s] = enum_values
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 3641826daa..769b171717 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -14,16 +14,26 @@ class EnumTest < ActiveRecord::TestCase
assert_not @book.proposed?
assert @book.read?
+ assert @book.in_english?
+ assert @book.author_visibility_visible?
+ assert @book.illustrator_visibility_visible?
+ assert @book.with_medium_font_size?
end
test "query state with strings" do
assert_equal "published", @book.status
assert_equal "read", @book.read_status
+ assert_equal "english", @book.language
+ assert_equal "visible", @book.author_visibility
+ assert_equal "visible", @book.illustrator_visibility
end
test "find via scope" do
assert_equal @book, Book.published.first
assert_equal @book, Book.read.first
+ assert_equal @book, Book.in_english.first
+ assert_equal @book, Book.author_visibility_visible.first
+ assert_equal @book, Book.illustrator_visibility_visible.first
end
test "find via where with values" do
@@ -72,6 +82,10 @@ class EnumTest < ActiveRecord::TestCase
test "update by declaration" do
@book.written!
assert @book.written?
+ @book.in_english!
+ assert @book.in_english?
+ @book.author_visibility_visible!
+ assert @book.author_visibility_visible?
end
test "update by setter" do
@@ -96,42 +110,61 @@ class EnumTest < ActiveRecord::TestCase
test "enum changed attributes" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :spanish
assert_equal old_status, @book.changed_attributes[:status]
+ assert_equal old_language, @book.changed_attributes[:language]
end
test "enum changes" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :spanish
assert_equal [old_status, 'proposed'], @book.changes[:status]
+ assert_equal [old_language, 'spanish'], @book.changes[:language]
end
test "enum attribute was" do
old_status = @book.status
+ old_language = @book.language
@book.status = :published
+ @book.language = :spanish
assert_equal old_status, @book.attribute_was(:status)
+ assert_equal old_language, @book.attribute_was(:language)
end
test "enum attribute changed" do
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status)
+ assert @book.attribute_changed?(:language)
end
test "enum attribute changed to" do
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, to: 'proposed')
+ assert @book.attribute_changed?(:language, to: 'french')
end
test "enum attribute changed from" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, from: old_status)
+ assert @book.attribute_changed?(:language, from: old_language)
end
test "enum attribute changed from old status to new status" do
old_status = @book.status
+ old_language = @book.language
@book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, from: old_status, to: 'proposed')
+ assert @book.attribute_changed?(:language, from: old_language, to: 'french')
end
test "enum didn't change" do
@@ -201,11 +234,15 @@ class EnumTest < ActiveRecord::TestCase
test "building new objects with enum scopes" do
assert Book.written.build.written?
assert Book.read.build.read?
+ assert Book.in_spanish.build.in_spanish?
+ assert Book.illustrator_visibility_invisible.build.illustrator_visibility_invisible?
end
test "creating new objects with enum scopes" do
assert Book.written.create.written?
assert Book.read.create.read?
+ assert Book.in_spanish.create.in_spanish?
+ assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible?
end
test "_before_type_cast returns the enum label (required for form fields)" do
@@ -355,4 +392,17 @@ class EnumTest < ActiveRecord::TestCase
book2 = klass.single.create!
assert book2.single?
end
+
+ test "query state by predicate with prefix" do
+ assert @book.author_visibility_visible?
+ assert_not @book.author_visibility_invisible?
+ assert @book.illustrator_visibility_visible?
+ assert_not @book.illustrator_visibility_invisible?
+ end
+
+ test "query state by predicate with custom prefix" do
+ assert @book.in_english?
+ assert_not @book.in_spanish?
+ assert_not @book.in_french?
+ end
end
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index 380dd3dfca..93cfabd61c 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -5,6 +5,10 @@ awdr:
format: "paperback"
status: :published
read_status: :read
+ language: :english
+ author_visibility: :visible
+ illustrator_visibility: :visible
+ font_size: :medium
rfr:
author_id: 1
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index 2170018068..24bfe47bbf 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -10,6 +10,10 @@ class Book < ActiveRecord::Base
enum status: [:proposed, :written, :published]
enum read_status: {unread: 0, reading: 2, read: 3}
enum nullable_status: [:single, :married]
+ enum language: [:english, :spanish, :french], enum_prefix: :in
+ enum author_visibility: [:visible, :invisible], enum_prefix: true
+ enum illustrator_visibility: [:visible, :invisible], enum_prefix: true
+ enum font_size: [:small, :medium, :large], enum_prefix: :with, enum_suffix: true
def published!
super
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 8b010f265c..dffccc9326 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -100,6 +100,10 @@ ActiveRecord::Schema.define do
t.column :status, :integer, default: 0
t.column :read_status, :integer, default: 0
t.column :nullable_status, :integer
+ t.column :language, :integer, default: 0
+ t.column :author_visibility, :integer, default: 0
+ t.column :illustrator_visibility, :integer, default: 0
+ t.column :font_size, :integer, default: 0
end
create_table :booleans, force: true do |t|
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 80b1bde1c7..ad069a112e 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -475,7 +475,8 @@ column names can not be derived from the table names, you can use the
`:column` and `:primary_key` options.
Rails will generate a name for every foreign key starting with
-`fk_rails_` followed by 10 random characters.
+`fk_rails_` followed by 10 character which is deterministically
+generated from the `from_table` and `column`.
There is a `:name` option to specify a different name if needed.
NOTE: Active Record only supports single column foreign keys. `execute` and
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index a839a0b9b6..4c1913f0cc 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -209,7 +209,7 @@ module ApplicationTests
bundle exec rake db:migrate test`
end
- assert_match(/5 runs, 8 assertions, 0 failures, 0 errors/, output)
+ assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output)
assert_no_match(/Errors running/, output)
end