Skip to content
Browse files

Enum mappings are now exposed via class methods instead of constants.

Example:

    class Conversation < ActiveRecord::Base
      enum status: [ :active, :archived ]
    end

Before:

    Conversation::STATUS # => { "active" => 0, "archived" => 1 }

After:

    Conversation.statuses # => { "active" => 0, "archived" => 1 }
  • Loading branch information...
1 parent 028029c commit b242b2dbe75f0b5e86e2ce9ef7c2c5ee96e17862 @chancancode chancancode committed Jan 14, 2014
Showing with 31 additions and 10 deletions.
  1. +18 −0 activerecord/CHANGELOG.md
  2. +10 −7 activerecord/lib/active_record/enum.rb
  3. +3 −3 activerecord/test/cases/enum_test.rb
View
18 activerecord/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Enum mappings are now exposed via class methods instead of constants.
+
+ Example:
+
+ class Conversation < ActiveRecord::Base
+ enum status: [ :active, :archived ]
+ end
+
+ Before:
+
+ Conversation::STATUS # => { "active" => 0, "archived" => 1 }
+
+ After:
+
+ Conversation.statuses # => { "active" => 0, "archived" => 1 }
+
+ *Godfrey Chan*
+
* Set `NameError#name` when STI-class-lookup fails.
*Chulki Lee*
View
17 activerecord/lib/active_record/enum.rb
@@ -56,21 +56,24 @@ module ActiveRecord
# In rare circumstances you might need to access the mapping directly.
# The mappings are exposed through a constant with the attributes name:
@egilburg
egilburg added a note Jan 14, 2014

comment is now wrong

@chancancode
Ruby on Rails member
chancancode added a note Jan 14, 2014

Updated in e64a83c, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
#
- # Conversation::STATUS # => { "active" => 0, "archived" => 1 }
+ # Conversation.statuses # => { "active" => 0, "archived" => 1 }
#
# Use that constant when you need to know the ordinal value of an enum:
#
- # Conversation.where("status <> ?", Conversation::STATUS[:archived])
+ # Conversation.where("status <> ?", Conversation.statuses[:archived])
module Enum
def enum(definitions)
klass = self
definitions.each do |name, values|
- # STATUS = { }
- enum_values = _enum_methods_module.const_set name.to_s.upcase, ActiveSupport::HashWithIndifferentAccess.new
+ # statuses = { }
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
name = name.to_sym
+ # def self.statuses statuses end
+ klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
+
_enum_methods_module.module_eval do
- # def status=(value) self[:status] = STATUS[value] end
+ # def status=(value) self[:status] = statuses[value] end
define_method("#{name}=") { |value|
if enum_values.has_key?(value) || value.blank?
self[name] = enum_values[value]
@@ -84,10 +87,10 @@ def enum(definitions)
end
}
- # def status() STATUS.key self[:status] end
+ # def status() statuses.key self[:status] end
define_method(name) { enum_values.key self[name] }
- # def status_before_type_cast() STATUS.key self[:status] end
+ # def status_before_type_cast() statuses.key self[:status] end
define_method("#{name}_before_type_cast") { enum_values.key self[name] }
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
View
6 activerecord/test/cases/enum_test.rb
@@ -74,9 +74,9 @@ class EnumTest < ActiveRecord::TestCase
end
test "constant to access the mapping" do
- assert_equal 0, Book::STATUS[:proposed]
- assert_equal 1, Book::STATUS["written"]
- assert_equal 2, Book::STATUS[:published]
+ assert_equal 0, Book.statuses[:proposed]
+ assert_equal 1, Book.statuses["written"]
+ assert_equal 2, Book.statuses[:published]
end
test "building new objects with enum scopes" do

6 comments on commit b242b2d

@chancancode
Ruby on Rails member

fyi @senny @dhh

@senny
Ruby on Rails member
senny commented on b242b2d Jan 14, 2014

👍

@vipulnsward
Ruby on Rails member

@chancancode I think we should be notifying/raising an exception about the method definition, if it already exists.
Also, this is a bit on the side of conflicts,

I may have a state :enum on a User model, to define maybe on-boarding state of a user, and as a very bad example - states indicating states the user resides in.
This is bound to break a lot, if we take this approach.

@senny
Ruby on Rails member
senny commented on b242b2d Jan 14, 2014

@vipulnsward can you expand a breaking use-case in code? I can't follow your thoughts on the states example above.

@chancancode
Ruby on Rails member

@vipulnsward: For your specific example you'll be fine, states would be defined as a class method for the enum mapping and the attribute method states is defined an instance method. Overall I think it's quite unlikely for the mapping class method to cause problems.

That said, I'm already working on detecting conflicts and raising exceptions when there's a conflict on the scopes or the attribute methods, so I'll keep this in mind and can probably also check for conflicts on here.

@vipulnsward
Ruby on Rails member

@chancancode correct, I am concerned with conflicting attribute methods and scopes. The pluralization is consuming two possible field names for a single field.

Hope it doesn't cause issue for users, good to know you are already working on it.

Please sign in to comment.
Something went wrong with that request. Please try again.