Skip to content

Comments

Allow default to be configured for Enum#39820

Merged
kamipo merged 1 commit intorails:masterfrom
kamipo:enum_default
Jul 13, 2020
Merged

Allow default to be configured for Enum#39820
kamipo merged 1 commit intorails:masterfrom
kamipo:enum_default

Conversation

@kamipo
Copy link
Member

@kamipo kamipo commented Jul 10, 2020

class Book < ActiveRecord::Base
  enum status: [:proposed, :written, :published], _default: :published
end

Book.new.status # => "published"

```ruby
class Book < ActiveRecord::Base
  enum status: [:proposed, :written, :published], _default: :published
end

Book.new.status # => "published"
```
@kamipo kamipo merged commit 04fe959 into rails:master Jul 13, 2020
@kamipo kamipo deleted the enum_default branch July 13, 2020 15:16
HParker added a commit to HParker/rails that referenced this pull request Jul 21, 2020
Before this change using a boolean or TINYINT as a enum value would be set correctly in the database, but always return nil.

This behavior changed in: rails#39820
@dwightwatson
Copy link
Contributor

Maybe a silly question, but why is default prefixed with an underscore?

@abhaynikam
Copy link
Contributor

@dwightwatson I had similar question asked here: #39895 (comment)

Thanks, @kamipo 👍 .

People may have a default column.

I am sorry but I did not clearly understand so I am putting it here again. Hope you don't mind 😊 . The enum options like _default or _prefix get deleted here(https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L164-L169) so how would default column get affected by the naming convention used for enum options? I tried adding a default column in sample Rails app with renaming _default -> default. Worked fine at least. I am sure I am missing something here 😅

@kamipo
Copy link
Member Author

kamipo commented Jul 22, 2020

If people have a enum default: [:foo, :bar] definition, introducing new option as :default will break the code.

@sebastiandeutsch
Copy link

sebastiandeutsch commented Jul 24, 2020

@kamipo would it be possible to have a second parameter that will be hash (to differentiate between data) like:

enum default: [:foo, :bar], { default: :foo }

Using this approach you could:

  1. avoid the underscore
  2. you would be open for modification if enum would need additional parameters

What are your thoughts?

@kamipo
Copy link
Member Author

kamipo commented Jul 24, 2020

It is impossible to omit hash braces for non-last arguments.

kamipo added a commit to kamipo/rails that referenced this pull request Feb 19, 2021
Unlike other features built on Attribute API, reserved options for
`enum` has leading `_`.

* `_prefix`/`_suffix`: rails#19813, rails#20999
* `_scopes`: rails#34605
* `_default`: rails#39820

That is due to `enum` takes one hash argument only, which contains both
enum definitions and reserved options.

I propose new syntax for `enum` to avoid leading `_` from reserved
options, by allowing `enum(attr_name, ..., **options)` more Attribute
API like syntax.

Before:

```ruby
class Book < ActiveRecord::Base
  enum status: [ :proposed, :written ], _prefix: true, _scopes: false
  enum cover: [ :hard, :soft ], _suffix: true, _default: :hard
end
```

After:

```ruby
class Book < ActiveRecord::Base
  enum :status, [ :proposed, :written ], prefix: true, scopes: false
  enum :cover, [ :hard, :soft ], suffix: true, default: :hard
end
```
@pomartel
Copy link

pomartel commented Mar 2, 2021

It looks like the default value does not apply to NULL database values. Here is my use case. I have a database with millions of records. I want to add an enum column (integer) in the database but it would be disruptive to also set the default value for millions of records. I would prefer the ActiveRecord enum to just use the default value when the database column is NULL. Is this something that makes sense?

@joemsak
Copy link

joemsak commented Mar 17, 2021

@pomartel For what it's worth, I was feeling the same way when I discovered the same issue today, and was about to write up a test for a pull request, but as I thought about it, I'm pretty sure rails expects us to add the default value for pre-existing records through a migration. I can't say I share your opinion that it would be disruptive to add this change even with millions of rows of nulls. If you want the default to be a certain value in the code, what's stopping you from making it so in the database? Happy to hear your explanation if you'd like to elaborate.

UPDATE
However, even upon adding the migration to change my column to the default value that matches my _default option, I still get nil in a rails console. This seems off to me.

I also had to add a migration to update existing records from null to the new default

@pomartel
Copy link

@joemsak I am not quite sure how to update millions of records in a non-blocking way but I am also no database expert. I just wish there was a way to set a default for NULL values without touching the database. I tried to override the getter but it did not play nice with the enum.

@joemsak
Copy link

joemsak commented Mar 17, 2021

@pomartel Gotcha yeah. I would have to say it would have to be done in a scheduled maintenance window while shutting down access to the database. Unless you have rolling migrations enabled but I don't know off hand how to do that. I prefer scheduling the maintenance period, alerting customers, getting a backup snapshot, and doing the migrations at the scheduled time.

As for overriding, yeah, I started getting into the guts of active record enum/attributes and based on the workflow of the code it really seems like Rails would prefer we just have a default set and updated on all null rows in the database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants