aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/enum.rb
diff options
context:
space:
mode:
authorGodfrey Chan <godfreykfc@gmail.com>2014-01-27 01:39:52 -0800
committerGodfrey Chan <godfreykfc@gmail.com>2014-01-29 10:54:51 -0800
commit40f0257e05d8735d94684043ea7be7295fbbae57 (patch)
tree3f8d763cfc6d0d87b9bbc530444b632b3b31ba19 /activerecord/lib/active_record/enum.rb
parent7e8e91c439c1a877f867cd7ba634f7297ccef04b (diff)
downloadrails-40f0257e05d8735d94684043ea7be7295fbbae57.tar.gz
rails-40f0257e05d8735d94684043ea7be7295fbbae57.tar.bz2
rails-40f0257e05d8735d94684043ea7be7295fbbae57.zip
`enum` now raises on "dangerous" name conflicts
Dangerous name conflicts includes instance or class method conflicts with methods defined within `ActiveRecord::Base` but not its ancestors, as well as conflicts with methods generated by other enums on the same class. Fixes #13389.
Diffstat (limited to 'activerecord/lib/active_record/enum.rb')
-rw-r--r--activerecord/lib/active_record/enum.rb46
1 files changed, 43 insertions, 3 deletions
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 53dde5e564..059bfe9a0f 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -77,10 +77,12 @@ module ActiveRecord
name = name.to_sym
# def self.statuses statuses end
+ detect_enum_conflict!(name, name.to_s.pluralize, true)
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
_enum_methods_module.module_eval do
# def status=(value) self[:status] = statuses[value] end
+ klass.send(:detect_enum_conflict!, name, "#{name}=")
define_method("#{name}=") { |value|
if enum_values.has_key?(value) || value.blank?
self[name] = enum_values[value]
@@ -95,23 +97,28 @@ module ActiveRecord
}
# def status() statuses.key self[:status] end
+ klass.send(:detect_enum_conflict!, name, name)
define_method(name) { enum_values.key self[name] }
# def status_before_type_cast() statuses.key self[:status] end
+ klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
define_method("#{name}_before_type_cast") { enum_values.key self[name] }
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
enum_values[value] = i
- # scope :active, -> { where status: 0 }
- klass.scope value, -> { klass.where name => i }
-
# def active?() status == 0 end
+ klass.send(:detect_enum_conflict!, name, "#{value}?")
define_method("#{value}?") { self[name] == i }
# def active!() update! status: :active end
+ klass.send(:detect_enum_conflict!, name, "#{value}!")
define_method("#{value}!") { update! name => value }
+
+ # scope :active, -> { where status: 0 }
+ klass.send(:detect_enum_conflict!, name, value, true)
+ klass.scope value, -> { klass.where name => i }
end
DEFINED_ENUMS[name.to_s] = enum_values
@@ -148,5 +155,38 @@ module ActiveRecord
mod
end
end
+
+ ENUM_CONFLICT_MESSAGE = \
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
+ "this will generate a %{type} method \"%{method}\", which is already defined " \
+ "by %{source}."
+
+ def detect_enum_conflict!(enum_name, method_name, klass_method = false)
+ if klass_method && dangerous_class_method?(method_name)
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
+ enum: enum_name,
+ klass: self.name,
+ type: 'class',
+ method: method_name,
+ source: 'Active Record'
+ }
+ elsif !klass_method && dangerous_attribute_method?(method_name)
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
+ enum: enum_name,
+ klass: self.name,
+ type: 'instance',
+ method: method_name,
+ source: 'Active Record'
+ }
+ elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
+ enum: enum_name,
+ klass: self.name,
+ type: 'instance',
+ method: method_name,
+ source: 'another enum'
+ }
+ end
+ end
end
end