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"
```
This commit is contained in:
Volmer Campos Soares 2021-05-12 10:37:05 -04:00
parent 8b5ebb4132
commit d8b176855e
6 changed files with 35 additions and 5 deletions

View File

@ -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

View File

@ -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") }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
```