I’m sure this makes sense in various cases, but every project I’ve been on, we’ve eschewed Rails enums specifically because it makes using any non-rails DB tooling a pain to use. I want to know what role a user is using, and reading “admin” is a lot easier than “6”.
Hi! I agree that integer keys for enums are hard to read. The last couple projects I’ve added them to, I’ve used this blog post to help me use string keys, and have PostgreSQL also enforce the validation with string keys. Hope this helps!
That’s really helpful, thank you!
I would love a way to have Rails default to using string keys. I’d be open to building this if the Rails team were open to it.
This bothered me so much, I built an entire gem to rectify it. Check it out if you’re using Postgres.
We’ve been burned by integer enums and would like/have started to switch to strings or MySQL native enums, though for a different reason than I see here. Integers make it difficult to remove values other than the last, or insert values anywhere other than the end of the list.
Agree with this. The enum feature does make it possible to define the integer associated with each value, but it’s not obvious, nor is it obvious why you’d want to do that. Given that Rails presents itself as omakase, I think it would be nice to have the “easiest” enum implementation also be the right one.
I didn’t know MySQL had native enums! I know that gems like GitHub - SchemaPlus/schema_plus_enums: Support for enum data types in ActiveRecord support PG native enums, but I’ve been burned by SchemaPlus gems in the past – IME they monkeypatch aggressively enough that their gem will get into a fight with at least one other gem on one’s project.
I feel like there’s a broader theme here around “maintaining database agnosticism, but being better able to leverage native database features when they are available.”
I agree that native enum types are a great thing and worth encouraging. If there’s differences in DB compatibility, I’m not sure they make a great default.
I think considering strings as a default makes a lot of sense. It’s not technically the most “correct” approach, but anyone who cares about that will probably be using an enum type anyway. For newbie users it makes much more sense than an integer (we’ve fallen into the same trap George notes around changing your enum list).
@georgeclaghorn How do you see backwards-compatibility questions working here? It sounds like integers are not a great default, but changing that default off the bat would probably break a lot of existing code.
I’m not familiar enough with Active Record internals or the history here, so I’ll have to defer to someone who is. (@matthewd?) Naïvely, I imagine we’d need to check the type of the backing column lazily.
As far as i know, sqlite has no native enum support, and since rails wants to be database-agnostic it won’t pass easily.
Strings aren’t a great default too for many reasons - renaming, possible database corruptions, performance and so on. The only db-agnostic alternative is separate enums table.
Be careful here.
Native enums also usually require ALTER statements to change in the future, meaning they lock tables and require a ton of care when migrating in the future; they’re designed for things that you typically want to be absolutely certain will not change in the future - the classic example is days of the week (“sunday”, “monday”, “tuesday”…)
In other words, native enums are not a go-to default as much as a lot of people think, and might not be appropriate for a default in Rails; maybe! But not as obvious as it might initially seem.
Please note I’m advising this as a huge, huge fan of native enums
In my last few projects I used this gem: GitHub - bibendi/activerecord-postgres_enum: Integrate PostgreSQL's enum data type into ActiveRecord's schema and migrations. because Rails does not support “proper” enums.
I would love if it or better Enums would become a part of Rails.
Hmm, this is a good point.
Do you think there’s a fluent design that lets people choose the correct option for their use case?
Not sure! Maybe, and that’d be really neat if possible.
My initial gut reaction if I was a maintainer being tasked with pragmatically making this better is “I understand the desire to not have integer-based enums anymore, real native enums are a potentially big can of worms, so perhaps the best comprise here is to indeed just use strings instead and call it good”. And again, I say this through clinched teeth as I’m usually the one wishing Rails utilized more native DB features. It’d be extremely dope if Rails gave the option for either as part of its API if possible, but just using strings instead of integers is definitely an awesome improvement by itself.
I think we can probably do something here with the fact that Rails, by default, interprets native enum values as strings… so it might be as simple as having optional create_enum
, alter_enum
, etc migration commands (along with the schema.rb
pieces.)
Naïvely, I imagine we’d need to check the type of the backing column lazily
I guess the only edge case there would be if someone’s currently using integer-based enum values while (accidentally?) storing them in a text/varchar column.
But barring that, I agree it seems feasible for us to dynamically choose between string and integer values based on the backing column’s type. (At least in principle. In practice that means we no longer know the enum values at model-definition time [or have to force a connection to find out], which could have some annoying flow-on effects if some code asks us about them. But it’s probably fine. )
And as @Betsy_Haibel notes, that should in turn give transparent support for both actually-strings and native enums; we see both as string-typed columns, so it’s just a matter of which column type you’ve chosen to use.