From d8b176855ed0f045961c2593e1af461c94457098 Mon Sep 17 00:00:00 2001 From: Volmer Campos Soares Date: Wed, 12 May 2021 10:37:05 -0400 Subject: [PATCH] Compute table name based on model name Instead of using the Active Record Base's class name, the table name is now inferred based on its model name object. This allows customization of the table name pattern since a distinct instance of `ActiveModel::Name` can be used instead. For example, to map a `PostRecord` class to a `posts` table: ```ruby class PostRecord < ActiveRecord::Base class << self def model_name ActiveModel::Name.new(self, nil, 'Post') end end end PostRecord.table_name # => "posts" ``` --- .../lib/active_record/model_schema.rb | 21 ++++++++++++++++--- .../test/cases/attribute_methods_test.rb | 1 + activerecord/test/cases/base_test.rb | 5 +++++ activerecord/test/cases/primary_keys_test.rb | 1 + activerecord/test/models/post.rb | 8 +++++++ .../source/active_support_core_extensions.md | 4 ++-- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 1971199a29..8ce155ff9f 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -213,6 +213,21 @@ module ActiveRecord # the table name guess for an Invoice class becomes "myapp_invoices". # Invoice::Lineitem becomes "myapp_invoice_lineitems". # + # Active Model Naming's +model_name+ is the base name used to guess the + # table name. In case a custom Active Model Name is defined, it will be + # used for the table name as well: + # + # class PostRecord < ActiveRecord::Base + # class << self + # def model_name + # ActiveModel::Name.new(self, nil, "Post") + # end + # end + # end + # + # PostRecord.table_name + # # => "posts" + # # You can also set your own table name explicitly: # # class Mouse < ActiveRecord::Base @@ -587,8 +602,8 @@ module ActiveRecord end # Guesses the table name, but does not decorate it with prefix and suffix information. - def undecorated_table_name(class_name = base_class.name) - table_name = class_name.to_s.demodulize.underscore + def undecorated_table_name(model_name) + table_name = model_name.to_s.demodulize.underscore pluralize_table_names ? table_name.pluralize : table_name end @@ -602,7 +617,7 @@ module ActiveRecord contained += "_" end - "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}" + "#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}" else # STI subclasses always use their superclass' table. base_class.table_name diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 0e20ceb3a1..6dead34126 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -965,6 +965,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase test "define_attribute_method works with both symbol and string" do klass = Class.new(ActiveRecord::Base) + klass.table_name = "foo" assert_nothing_raised { klass.define_attribute_method(:foo) } assert_nothing_raised { klass.define_attribute_method("bar") } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 9968479bc8..5af09ed196 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -505,6 +505,10 @@ class BasicsTest < ActiveRecord::TestCase Post.reset_table_name end + def test_table_name_based_on_model_name + assert_equal "posts", PostRecord.table_name + end + def test_null_fields assert_nil Topic.find(1).parent_id assert_nil Topic.create("title" => "Hey you").parent_id @@ -1478,6 +1482,7 @@ class BasicsTest < ActiveRecord::TestCase test "scoped can take a values hash" do klass = Class.new(ActiveRecord::Base) + klass.table_name = "bar" assert_equal ["foo"], klass.all.merge!(select: "foo").select_values end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 1ee8842fc6..1707126f12 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -174,6 +174,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase def test_quoted_primary_key_after_set_primary_key k = Class.new(ActiveRecord::Base) + k.table_name = "bar" assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key k.primary_key = "foo" assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 74dd008efe..21a1674457 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -382,3 +382,11 @@ class Postesque < ActiveRecord::Base belongs_to :author_with_address, class_name: "Author", foreign_key: :author_id belongs_to :author_with_the_letter_a, class_name: "Author", foreign_key: :author_id end + +class PostRecord < ActiveRecord::Base + class << self + def model_name + ActiveModel::Name.new(self, nil, "Post") + end + end +end diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 5b43a378d6..a454733f2a 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -1461,8 +1461,8 @@ Active Record uses this method to compute the default table name that correspond ```ruby # active_record/model_schema.rb -def undecorated_table_name(class_name = base_class.name) - table_name = class_name.to_s.demodulize.underscore +def undecorated_table_name(model_name) + table_name = model_name.to_s.demodulize.underscore pluralize_table_names ? table_name.pluralize : table_name end ```