Permalink
Browse files

Added ActiveRecord::Base#enum for declaring enum attributes where the…

… values map to integers in the database, but can be queried by name
  • Loading branch information...
dhh committed Nov 2, 2013
1 parent deaf285 commit db41eb8a6ea88b854bf5cd11070ea4245e1639c5
View
@@ -1,3 +1,28 @@
+* Added ActiveRecord::Base#enum for declaring enum attributes where the values map to integers in the database, but can be queried by name.
+
+ Example:
+ class Conversation < ActiveRecord::Base
+ enum status: %i( active archived )
+ end
+
+ Conversation::STATUS # => { active: 0, archived: 1 }
+
+ # conversation.update! status: 0
+ conversation.active!
+ conversation.active? # => true
+ conversation.status # => :active
+
+ # conversation.update! status: 1
+ conversation.archived!
+ conversation.archived? # => true
+ conversation.status # => :archived
+
+ # conversation.update! status: 1
+ conversation.status = :archived
+
+ *DHH*
+
+
* ActiveRecord::Base#attribute_for_inspect now truncates long arrays (more than 10 elements)
*Jan Bernacki*
@@ -37,6 +37,7 @@ module ActiveRecord
autoload :ConnectionHandling
autoload :CounterCache
autoload :DynamicMatchers
+ autoload :Enum
autoload :Explain
autoload :Inheritance
autoload :Integration
@@ -291,6 +291,7 @@ class Base
extend Translation
extend DynamicMatchers
extend Explain
+ extend Enum
extend Delegation::DelegateCache
include Persistence
@@ -0,0 +1,60 @@
+module ActiveRecord
+ # Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example:
+ #
+ # class Conversation < ActiveRecord::Base
+ # enum status: %i( active archived )
+ # end
+ #
+ # Conversation::STATUS # => { active: 0, archived: 1 }
+ #
+ # # conversation.update! status: 0
+ # conversation.active!
+ # conversation.active? # => true
+ # conversation.status # => :active
+ #
+ # # conversation.update! status: 1
+ # conversation.archived!
+ # conversation.archived? # => true
+ # conversation.status # => :archived
+ #
+ # # conversation.update! status: 1
+ # conversation.status = :archived
+ #
+ # You can set the default value from the database declaration, like:
+ #
+ # create_table :conversation do
+ # t.column :status, :integer, default: 0
+ # end
+ #
+ # Good practice is to let the first declared status be the default.
+ module Enum
+ def enum(definitions)
+ definitions.each do |name, values|
+ const_name = name.to_s.upcase
+
+ # DIRECTION = { }
+ const_set const_name, {}
+
+ # def direction=(value) self[:direction] = DIRECTION[value] end
+ class_eval "def #{name}=(value) self[:#{name}] = #{const_name}[value] end"

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 3, 2013

Member

@tenderlove didn't we decide that using define_method was better than class_eval for dynamically defining methods - I remember you doing some research into this.

@pixeltrix

pixeltrix Nov 3, 2013

Member

@tenderlove didn't we decide that using define_method was better than class_eval for dynamically defining methods - I remember you doing some research into this.

This comment has been minimized.

Show comment
Hide comment
@tenderlove

tenderlove Nov 3, 2013

Member
@tenderlove

tenderlove via email Nov 3, 2013

Member

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 3, 2013

Member

I think it just appeared clearer to me in the code. Are you saying that class_eval is a performance issue? That seems surprising to me given that this is only called once per listing and at compile time.

@dhh

dhh Nov 3, 2013

Member

I think it just appeared clearer to me in the code. Are you saying that class_eval is a performance issue? That seems surprising to me given that this is only called once per listing and at compile time.

This comment has been minimized.

Show comment
Hide comment
@ka8725

ka8725 Nov 4, 2013

Contributor

define_method is better because you will have properly worked respond_to?

@ka8725

ka8725 Nov 4, 2013

Contributor

define_method is better because you will have properly worked respond_to?

This comment has been minimized.

Show comment
Hide comment
@thedarkone

thedarkone Nov 4, 2013

Contributor

define_method is better because you will have properly worked respond_to?

@ka8725 what do you mean?

@thedarkone

thedarkone Nov 4, 2013

Contributor

define_method is better because you will have properly worked respond_to?

@ka8725 what do you mean?

This comment has been minimized.

Show comment
Hide comment
@ck3g

ck3g Nov 4, 2013

Contributor

@ka8725 I guess there is no difference for respond_to?.

2.0.0p247 :001 > class MyClass
2.0.0p247 :002?>   define_method :my_define_method do
2.0.0p247 :003 >       'my_define_method'
2.0.0p247 :004?>     end
2.0.0p247 :005?>   
2.0.0p247 :006 >     class_eval <<-EVAL
2.0.0p247 :007">       def my_class_eval
2.0.0p247 :008">         'my_class_eval'
2.0.0p247 :009">       end
2.0.0p247 :010">     EVAL
2.0.0p247 :011?>   end
 => nil 
2.0.0p247 :012 > MyClass.new.respond_to? :my_define_method
 => true 
2.0.0p247 :013 > MyClass.new.respond_to? :my_class_eval
 => true 

Did I understood you correctly?

@ck3g

ck3g Nov 4, 2013

Contributor

@ka8725 I guess there is no difference for respond_to?.

2.0.0p247 :001 > class MyClass
2.0.0p247 :002?>   define_method :my_define_method do
2.0.0p247 :003 >       'my_define_method'
2.0.0p247 :004?>     end
2.0.0p247 :005?>   
2.0.0p247 :006 >     class_eval <<-EVAL
2.0.0p247 :007">       def my_class_eval
2.0.0p247 :008">         'my_class_eval'
2.0.0p247 :009">       end
2.0.0p247 :010">     EVAL
2.0.0p247 :011?>   end
 => nil 
2.0.0p247 :012 > MyClass.new.respond_to? :my_define_method
 => true 
2.0.0p247 :013 > MyClass.new.respond_to? :my_class_eval
 => true 

Did I understood you correctly?

This comment has been minimized.

Show comment
Hide comment
@ka8725

ka8725 Nov 4, 2013

Contributor

Yes, you are right. It's my fault

@ka8725

ka8725 Nov 4, 2013

Contributor

Yes, you are right. It's my fault

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 4, 2013

Member

@dhh what @tenderlove is saying is that you should only use class_eval if you need maximum performance for a simple method. If the work done in the method is anything significant then the call performance gains are masked by the time taken to do the work. More information is in his blog post: http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html

@pixeltrix

pixeltrix Nov 4, 2013

Member

@dhh what @tenderlove is saying is that you should only use class_eval if you need maximum performance for a simple method. If the work done in the method is anything significant then the call performance gains are masked by the time taken to do the work. More information is in his blog post: http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 4, 2013

Member
@dhh

dhh via email Nov 4, 2013

Member

This comment has been minimized.

Show comment
Hide comment
@ck3g

ck3g Nov 4, 2013

Contributor

@dhh

Ah. I'd be ok with a patch for define_method. I prefer the look of class_eval but I'm not attached to that.

#12754

@ck3g

ck3g Nov 4, 2013

Contributor

@dhh

Ah. I'd be ok with a patch for define_method. I prefer the look of class_eval but I'm not attached to that.

#12754

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 4, 2013

Member

Gotta love open source! Thanks @ck3g!

@dhh

dhh Nov 4, 2013

Member

Gotta love open source! Thanks @ck3g!

+
+ # def direction() DIRECTION.key self[:direction] end
+ class_eval "def #{name}() #{const_name}.key self[:#{name}] end"
+
+ values.each_with_index do |value, i|
+ # DIRECTION[:incoming] = 0
+ const_get(const_name)[value] = i
+
+ # scope :incoming, -> { where direction: 0 }
+ scope value, -> { where name => i }
+
+ # def incoming?() direction == 0 end
+ class_eval "def #{value}?() self[:#{name}] == #{i} end"
+
+ # def incoming! update! direction: :incoming end
+ class_eval "def #{value}!() update! #{name}: :#{value} end"
+ end
+ end
+ end
+ end
+end
@@ -0,0 +1,36 @@
+require 'cases/helper'
+require 'models/book'
+
+class StoreTest < ActiveRecord::TestCase
+ fixtures :books
+
+ setup do
+ @book = Book.create! name: 'REMOTE'

This comment has been minimized.

Show comment
Hide comment
@drbonzo

drbonzo Nov 2, 2013

nice line :)

@drbonzo

drbonzo Nov 2, 2013

nice line :)

+ end
+
+ test "query state by predicate" do
+ assert @book.proposed?
+ assert_not @book.written?
+ assert_not @book.published?
+ end
+
+ test "query state with symbol" do
+ assert_equal :proposed, @book.status
+ end
+
+ test "update by declaration" do
+ @book.written!
+ assert @book.written?
+ end
+
+ test "update by setter" do
+ @book.update! status: :written
+ assert @book.written?
+ end
+
+ test "constant" do
+ assert_equal 0, Book::STATUS[:proposed]
+ assert_equal 1, Book::STATUS[:written]
+ assert_equal 2, Book::STATUS[:published]
+ end
+end
@@ -2,8 +2,10 @@ class Book < ActiveRecord::Base
has_many :authors
has_many :citations, :foreign_key => 'book1_id'

This comment has been minimized.

Show comment
Hide comment
@mcspring

mcspring Nov 3, 2013

Refactor this to new ruby hash syntax?

@mcspring

mcspring Nov 3, 2013

Refactor this to new ruby hash syntax?

- has_many :references, -> { distinct }, :through => :citations, :source => :reference_of
+ has_many :references, -> { distinct }, through: :citations, source: :reference_of
has_many :subscriptions
- has_many :subscribers, :through => :subscriptions
+ has_many :subscribers, through: :subscriptions
+
+ enum status: %i( proposed written published )
end
@@ -94,6 +94,7 @@ def create_table(*args, &block)
create_table :books, :force => true do |t|
t.integer :author_id
t.column :name, :string
+ t.column :status, :integer, default: 0
end
create_table :booleans, :force => true do |t|

29 comments on commit db41eb8

@lbramos

This comment has been minimized.

Show comment
Hide comment
@lbramos

lbramos Nov 2, 2013

awesome x2 (Y)

awesome x2 (Y)

@byroot

This comment has been minimized.

Show comment
Hide comment
@byroot

byroot Nov 2, 2013

Member

I though it was a support for mysql and postgres enum types. :sad:

Member

byroot replied Nov 2, 2013

I though it was a support for mysql and postgres enum types. :sad:

@gotar

This comment has been minimized.

Show comment
Hide comment
@gotar

gotar Nov 2, 2013

thx, (+1)

gotar replied Nov 2, 2013

thx, (+1)

@mlangenberg

This comment has been minimized.

Show comment
Hide comment
@mlangenberg

mlangenberg Nov 2, 2013

Contributor

Be careful when adding or removing values later on. It might be better to do an explicit mapping to an integer, instead of relying on the order of elements in the status array.

Given the example enum status: %i( proposed written published ) and you eventually decide you don't need the 'written' state at all. Making this change enum status: %i( proposed published ), will be very confusing, since written books are now suddenly published. You could argue for a migration, but I would rather stick with mapping '2' to 'published'.

At least when using MySQL enum type, it can migrate data for you (albeit expensive). With the mapping in Rails, why not support a tuples with values and integers?

enum status: [[:proposed, 0], [:written, 1], [:published, 2]]

Contributor

mlangenberg replied Nov 2, 2013

Be careful when adding or removing values later on. It might be better to do an explicit mapping to an integer, instead of relying on the order of elements in the status array.

Given the example enum status: %i( proposed written published ) and you eventually decide you don't need the 'written' state at all. Making this change enum status: %i( proposed published ), will be very confusing, since written books are now suddenly published. You could argue for a migration, but I would rather stick with mapping '2' to 'published'.

At least when using MySQL enum type, it can migrate data for you (albeit expensive). With the mapping in Rails, why not support a tuples with values and integers?

enum status: [[:proposed, 0], [:written, 1], [:published, 2]]

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 2, 2013

Member

@mlangenberg, I'd be happy to see a patch that also supports explicit mapping, but let's go with this format:

enum status: { proposed: 0, written: 1, published: 2 }

But it would be in addition to what we have now.

Member

dhh replied Nov 2, 2013

@mlangenberg, I'd be happy to see a patch that also supports explicit mapping, but let's go with this format:

enum status: { proposed: 0, written: 1, published: 2 }

But it would be in addition to what we have now.

@egilburg

This comment has been minimized.

Show comment
Hide comment
@egilburg

egilburg Nov 2, 2013

Contributor

Personally a more common pattern I've seen is where the enum values are already stored in db under the matching column name. For example:

In seeds.rb:

Role.create!(name: 'admin')
Role.create!(name: 'user')

And what is needed API like:

Role.admin # => [<#Role>]
Role.first.admin? # => true or false, instead of the more clunky StringInquirer way of Role.first.name.admin?
Role.first.admin! # (sets 'name' to 'admin')

It would be cool if this was supported by:

class Role < ActiveRecord::Base
  enum name: i%( admin user )
end

Not sure if this will clash with current implementation though.

Contributor

egilburg replied Nov 2, 2013

Personally a more common pattern I've seen is where the enum values are already stored in db under the matching column name. For example:

In seeds.rb:

Role.create!(name: 'admin')
Role.create!(name: 'user')

And what is needed API like:

Role.admin # => [<#Role>]
Role.first.admin? # => true or false, instead of the more clunky StringInquirer way of Role.first.name.admin?
Role.first.admin! # (sets 'name' to 'admin')

It would be cool if this was supported by:

class Role < ActiveRecord::Base
  enum name: i%( admin user )
end

Not sure if this will clash with current implementation though.

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 2, 2013

Member

It's pretty inefficient to store enums as text. You're going to repeat the same text over and over and over again. I'd consider that an anti pattern. People are better off doing a migration to ints if they want to use this.

Member

dhh replied Nov 2, 2013

It's pretty inefficient to store enums as text. You're going to repeat the same text over and over and over again. I'd consider that an anti pattern. People are better off doing a migration to ints if they want to use this.

@byroot

This comment has been minimized.

Show comment
Hide comment
@byroot

byroot Nov 2, 2013

Member

But integers remove readability / discoverability.

This is why MySQL, Postgres and Oracle (at least) offer enums that expose text but store integers. Support was added for postgres arrays and hstore, why not adding support for enums ?

Member

byroot replied Nov 2, 2013

But integers remove readability / discoverability.

This is why MySQL, Postgres and Oracle (at least) offer enums that expose text but store integers. Support was added for postgres arrays and hstore, why not adding support for enums ?

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Nov 2, 2013

Member
Member

dhh replied Nov 2, 2013

@byroot

This comment has been minimized.

Show comment
Hide comment
@byroot

byroot Nov 3, 2013

Member

I'll do.

Member

byroot replied Nov 3, 2013

I'll do.

@yury

This comment has been minimized.

Show comment
Hide comment
@yury

yury Nov 3, 2013

Contributor

@dhh here is PR for explicit mapping.

Contributor

yury replied Nov 3, 2013

@dhh here is PR for explicit mapping.

@rahult

This comment has been minimized.

Show comment
Hide comment
@rahult

rahult Nov 3, 2013

awesome 👍

awesome 👍

@ck3g

This comment has been minimized.

Show comment
Hide comment
@ck3g

ck3g Nov 3, 2013

Contributor

👍

Contributor

ck3g replied Nov 3, 2013

👍

@koteus

This comment has been minimized.

Show comment
Hide comment
@koteus

koteus Nov 3, 2013

It would be nice if this had a bitmask feature out of the box. For example to assign multiple roles to a user:

enum roles: %i(writer publisher editor admin), multiple: true

And usage:

user = User.create name: "David", roles: %i(publisher editor)
user.roles
# => [:publisher, :editor]
user.roles << :writer
user.roles
# => [:publisher, :editor, :writer]

But there's a gem for this already: https://github.com/joelmoss/bitmask_attributes

It would be nice if this had a bitmask feature out of the box. For example to assign multiple roles to a user:

enum roles: %i(writer publisher editor admin), multiple: true

And usage:

user = User.create name: "David", roles: %i(publisher editor)
user.roles
# => [:publisher, :editor]
user.roles << :writer
user.roles
# => [:publisher, :editor, :writer]

But there's a gem for this already: https://github.com/joelmoss/bitmask_attributes

@joelmoss

This comment has been minimized.

Show comment
Hide comment
@joelmoss

joelmoss Nov 3, 2013

I might be biased here, but not seeing why you wouldn't just use the bitmask_attributes gem.

I might be biased here, but not seeing why you wouldn't just use the bitmask_attributes gem.

@yonbergman

This comment has been minimized.

Show comment
Hide comment
@yonbergman

yonbergman Nov 4, 2013

Hey, I have a pretty similar gem that adds enum functionality /yonbergman/enumify
and we found that having a not scope for each enum value is really helpful -

# scope :not_incoming, -> { where.not direction: 0 }
scope "not_#{value}", -> { where.not name => i }

Other than that awesome feature :)

Hey, I have a pretty similar gem that adds enum functionality /yonbergman/enumify
and we found that having a not scope for each enum value is really helpful -

# scope :not_incoming, -> { where.not direction: 0 }
scope "not_#{value}", -> { where.not name => i }

Other than that awesome feature :)

@ka8725

This comment has been minimized.

Show comment
Hide comment
@ka8725

ka8725 Nov 4, 2013

Contributor

@yonbergman, you forgot to mention about validations, form helpers, i18n, ability to change column type, multiplying and some more useful features.

Contributor

ka8725 replied Nov 4, 2013

@yonbergman, you forgot to mention about validations, form helpers, i18n, ability to change column type, multiplying and some more useful features.

@samqiu

This comment has been minimized.

Show comment
Hide comment
@samqiu

samqiu Nov 6, 2013

@dhh Why are you so awesome!? 👍 👍 👍

@dhh Why are you so awesome!? 👍 👍 👍

@brendanstennett

This comment has been minimized.

Show comment
Hide comment
@brendanstennett

brendanstennett Dec 20, 2013

I think it would be helpful to prepend the enum name to the generated instance methods. This would help avoid collisions of two different enum fields on the same model having an identical enum value. Example below:

class Foo < ActiveRecord::Base
  enum :state, [:open, :closed]
  enum :other_state, [:something, :closed]
end

The generated methods #closed, #closed? and #closed! for other_state would collide. Generating methods like #state_closed, #state_closed? and #state_closed! (likewise with other_state) would solve the collision issue.

I think it would be helpful to prepend the enum name to the generated instance methods. This would help avoid collisions of two different enum fields on the same model having an identical enum value. Example below:

class Foo < ActiveRecord::Base
  enum :state, [:open, :closed]
  enum :other_state, [:something, :closed]
end

The generated methods #closed, #closed? and #closed! for other_state would collide. Generating methods like #state_closed, #state_closed? and #state_closed! (likewise with other_state) would solve the collision issue.

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Dec 20, 2013

Member
Member

dhh replied Dec 20, 2013

@chancancode

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Dec 20, 2013

Member

We are adding the error, see #13389

Member

chancancode replied Dec 20, 2013

We are adding the error, see #13389

@brendanstennett

This comment has been minimized.

Show comment
Hide comment
@brendanstennett

brendanstennett Dec 20, 2013

@dhh make sense, probably not a likely scenario anyways I suppose.

@dhh make sense, probably not a likely scenario anyways I suppose.

@chancancode

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Dec 20, 2013

Member

By the way, if you ended up with a collision, you can always prefix them yourself, like enum :state, [:state_open, :state_closed], and you'll get exactly the same thing. Meanwhile, the common case gets the nice syntax. Best of both worlds(TM)

Member

chancancode replied Dec 20, 2013

By the way, if you ended up with a collision, you can always prefix them yourself, like enum :state, [:state_open, :state_closed], and you'll get exactly the same thing. Meanwhile, the common case gets the nice syntax. Best of both worlds(TM)

@kenn

This comment has been minimized.

Show comment
Hide comment
@kenn

kenn Dec 20, 2013

Contributor

I think raise on collision is a sane default for simple use cases, but when your requirements need more, here's another gem: https://github.com/kenn/enum_accessor it does what @huffmoody mentioned above.

I'm looking forward to seeing validation / i18n support and hopefully I'll be able to ditch my own implementation someday. :)

Contributor

kenn replied Dec 20, 2013

I think raise on collision is a sane default for simple use cases, but when your requirements need more, here's another gem: https://github.com/kenn/enum_accessor it does what @huffmoody mentioned above.

I'm looking forward to seeing validation / i18n support and hopefully I'll be able to ditch my own implementation someday. :)

@imanel

This comment has been minimized.

Show comment
Hide comment
@imanel

imanel Dec 21, 2013

Contributor

I tried to implement optional prefixing for those that need it in #13433 - I believe that it's much easier to use it like:

# enum :state, [:open, :state], nested: true
conversation.status = :open
conversation.status #=> :open
conversation.status_open?
Conversation.status_open

instead of currently proposed solution:

# enum :state, [:state_open, :state_closed]
conversation.status = :status_open
conversation.status #=> :status_open
conversation.status_open?
Conversation.status_open

Current implementation might be little to verbose (I have this slight Java-like feeling ;)

Contributor

imanel replied Dec 21, 2013

I tried to implement optional prefixing for those that need it in #13433 - I believe that it's much easier to use it like:

# enum :state, [:open, :state], nested: true
conversation.status = :open
conversation.status #=> :open
conversation.status_open?
Conversation.status_open

instead of currently proposed solution:

# enum :state, [:state_open, :state_closed]
conversation.status = :status_open
conversation.status #=> :status_open
conversation.status_open?
Conversation.status_open

Current implementation might be little to verbose (I have this slight Java-like feeling ;)

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Dec 21, 2013

Member
Member

dhh replied Dec 21, 2013

@imanel

This comment has been minimized.

Show comment
Hide comment
@imanel

imanel Dec 21, 2013

Contributor

Which, I believe, is very inconvenient and counterintuitive for some users. It's much more obvious to set status to "open" than to "status_open", needles to say that it's really unnecessary duplication. Best example is exposing such value via API, where you would need to convert from "status_open" to "open" during both read and write (I can't imagine API with "status" set to "status_open", so conversion would be a must). Prefixing is also optional, so default behavior is not changed and it should not interrupt how your proposal works.

Contributor

imanel replied Dec 21, 2013

Which, I believe, is very inconvenient and counterintuitive for some users. It's much more obvious to set status to "open" than to "status_open", needles to say that it's really unnecessary duplication. Best example is exposing such value via API, where you would need to convert from "status_open" to "open" during both read and write (I can't imagine API with "status" set to "status_open", so conversion would be a must). Prefixing is also optional, so default behavior is not changed and it should not interrupt how your proposal works.

@kenn

This comment has been minimized.

Show comment
Hide comment
@kenn

kenn Dec 21, 2013

Contributor

IMO, enum :state, [ :state_value ] should not be recommended as a workaround, I know first hand how ugly it would become in the source, that's the main reason that I created enum_accessor when I found at least 7 enum gems at the time but none did prefixing.

user.gender = :gender_man
user.save

user.gender
 => :gender_man

This is just so uncool.

Let's just raise on a collision and if multiple enums are necessary, say use gems like enum_accessor.

Contributor

kenn replied Dec 21, 2013

IMO, enum :state, [ :state_value ] should not be recommended as a workaround, I know first hand how ugly it would become in the source, that's the main reason that I created enum_accessor when I found at least 7 enum gems at the time but none did prefixing.

user.gender = :gender_man
user.save

user.gender
 => :gender_man

This is just so uncool.

Let's just raise on a collision and if multiple enums are necessary, say use gems like enum_accessor.

@imanel

This comment has been minimized.

Show comment
Hide comment
@imanel

imanel Dec 21, 2013

Contributor

I agree we should raise on a collision, but I would also be glad to see optional namespacing in Rails core so there would be no need to duplicate it using third party gems (especially that having it and not having it is 1 line of code)

Contributor

imanel replied Dec 21, 2013

I agree we should raise on a collision, but I would also be glad to see optional namespacing in Rails core so there would be no need to duplicate it using third party gems (especially that having it and not having it is 1 line of code)

Please sign in to comment.