mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Should not fail if mangling enum names collision
Just skip defining mangling enum methods in that case. Fixes #40804.
This commit is contained in:
parent
2a12fe0b12
commit
307d50292d
4 changed files with 62 additions and 34 deletions
|
@ -158,8 +158,6 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def enum(definitions)
|
||||
klass = self
|
||||
|
||||
enum_prefix = definitions.delete(:_prefix)
|
||||
enum_suffix = definitions.delete(:_suffix)
|
||||
enum_scopes = definitions.delete(:_scopes)
|
||||
|
@ -187,55 +185,71 @@ module ActiveRecord
|
|||
EnumType.new(attr, enum_values, subtype)
|
||||
end
|
||||
|
||||
value_method_names = []
|
||||
_enum_methods_module.module_eval do
|
||||
enum_prefix = name if enum_prefix == true
|
||||
prefix = "#{enum_prefix}_" if enum_prefix
|
||||
|
||||
enum_suffix = name if enum_suffix == true
|
||||
suffix = "_#{enum_suffix}" if enum_suffix
|
||||
|
||||
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
||||
value_method_names = []
|
||||
pairs.each do |label, value|
|
||||
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
|
||||
|
||||
method_friendly_label = label.to_s.gsub(/[\W&&[:ascii:]]+/, "_")
|
||||
value_method_name = "#{prefix}#{method_friendly_label}#{suffix}"
|
||||
value_method_names << value_method_name
|
||||
enum_values[label] = value
|
||||
label = label.to_s
|
||||
enum_values[label] = value
|
||||
|
||||
# def active?() status == "active" end
|
||||
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
||||
define_method("#{value_method_name}?") { self[attr] == label }
|
||||
value_method_name = "#{prefix}#{label}#{suffix}"
|
||||
value_method_names << value_method_name
|
||||
define_enum_methods(name, value_method_name, label, enum_scopes)
|
||||
|
||||
# def active!() update!(status: 0) end
|
||||
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
||||
define_method("#{value_method_name}!") { update!(attr => value) }
|
||||
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
||||
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
||||
|
||||
# scope :active, -> { where(status: 0) }
|
||||
# scope :not_active, -> { where.not(status: 0) }
|
||||
if enum_scopes != false
|
||||
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
||||
klass.scope value_method_name, -> { where(attr => value) }
|
||||
|
||||
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
||||
klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
|
||||
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
||||
value_method_names << value_method_alias
|
||||
define_enum_methods(name, value_method_alias, label, enum_scopes)
|
||||
end
|
||||
end
|
||||
klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
|
||||
end
|
||||
detect_negative_enum_conditions!(value_method_names) if enum_scopes != false
|
||||
enum_values.freeze
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
class EnumMethods < Module # :nodoc:
|
||||
def initialize(klass)
|
||||
@klass = klass
|
||||
end
|
||||
|
||||
private
|
||||
attr_reader :klass
|
||||
|
||||
def define_enum_methods(name, value_method_name, label, enum_scopes)
|
||||
# def active?() status == "active" end
|
||||
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
||||
define_method("#{value_method_name}?") { self[name] == label }
|
||||
|
||||
# def active!() update!(status: 0) end
|
||||
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
||||
define_method("#{value_method_name}!") { update!(name => label) }
|
||||
|
||||
# scope :active, -> { where(status: 0) }
|
||||
# scope :not_active, -> { where.not(status: 0) }
|
||||
if enum_scopes != false
|
||||
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
||||
klass.scope value_method_name, -> { where(name => label) }
|
||||
|
||||
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
||||
klass.scope "not_#{value_method_name}", -> { where.not(name => label) }
|
||||
end
|
||||
end
|
||||
end
|
||||
private_constant :EnumMethods
|
||||
|
||||
def _enum_methods_module
|
||||
@_enum_methods_module ||= begin
|
||||
mod = Module.new
|
||||
mod = EnumMethods.new(self)
|
||||
include mod
|
||||
mod
|
||||
end
|
||||
|
|
|
@ -657,6 +657,17 @@ class EnumTest < ActiveRecord::TestCase
|
|||
assert_not_predicate book, :🇪🇸?
|
||||
end
|
||||
|
||||
test "mangling collision for enum names" do
|
||||
klass = Class.new(ActiveRecord::Base) do
|
||||
self.table_name = "computers"
|
||||
enum timezone: [:"Etc/GMT+1", :"Etc/GMT-1"]
|
||||
end
|
||||
|
||||
computer = klass.public_send(:"Etc/GMT+1").build
|
||||
assert_predicate computer, :"Etc/GMT+1?"
|
||||
assert_not_predicate computer, :"Etc/GMT-1?"
|
||||
end
|
||||
|
||||
test "enum logs a warning if auto-generated negative scopes would clash with other enum names" do
|
||||
old_logger = ActiveRecord::Base.logger
|
||||
logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
|
||||
|
|
2
activerecord/test/fixtures/computers.yml
vendored
2
activerecord/test/fixtures/computers.yml
vendored
|
@ -3,8 +3,10 @@ workstation:
|
|||
system: 'Linux'
|
||||
developer: 1
|
||||
extendedWarranty: 1
|
||||
timezone: 1
|
||||
|
||||
laptop:
|
||||
system: 'MacOS 1'
|
||||
developer: 1
|
||||
extendedWarranty: 1
|
||||
timezone: 1
|
||||
|
|
|
@ -265,6 +265,7 @@ ActiveRecord::Schema.define do
|
|||
t.string :system
|
||||
t.integer :developer, null: false
|
||||
t.integer :extendedWarranty, null: false
|
||||
t.integer :timezone
|
||||
end
|
||||
|
||||
create_table :computers_developers, id: false, force: true do |t|
|
||||
|
|
Loading…
Reference in a new issue