Proposal: create a helper for ActiveRecord enum attributes for large tables (without DB column default)

Problem to solve

For large tables, adding a default value for columns is difficult or impossible. Adding ActiveRecord::Enum attributes for such columns is pretty verbose and is currently done with significant code duplication, e.g.

class AddGroupViewToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :group_view, :integer # `users` table is not the largest one but is a good example still
  end
end


class User
  DEFAULT_GROUP_VIEW = 'details'.freeze

  enum group_view: { details: 1, security_dashboard: 2 }, _prefix: true
  # need to override scope because NULL is now considered a default value as well as :details
  scope :group_view_details, -> { where('group_view = ? OR group_view IS NULL', group_view[:details]) }

  def group_view
    super || DEFAULT_GROUP_VIEW # to provide a default value if NULL i.e. was not set for this record yet
  end
end

Further details

One could suggest solving this problem with EnumWithNil, e.g.

class User
  include EnumWithNil

  enum_with_nil group_view: { details: nil, security_dashboard: 1 }
end

But, using nil (NULL) one of the peer values for the choice seems to be semantically incorrect. From another hand, a choice providing an unknown, undefined, or unavailable option is a proper candidate for enum_with_nil.

This proposal is for enum attributes where all values are semantically of the same value comparing to each other.

It's a debatable topic. This issue is created for discussion of the best and MVC approach to this problem.

Proposal

Adding a model concern with a helper method that will:

  1. Create an enum attribute with the same options supported by native ActiveRecord::Enum.enum method
  2. Add a model instance getter for the default value of the enum attribute
  3. Redefine the model scope provided by ActiveRecord::Enum for fetching records with the default value of the attributes to include NULLs

E.g.

class User
  include EnumWithDefault

  enum_with_default group_view: { details: 1, security_dashboard: 2 }  # first value is considered default or one might add a default: option to the helper method
end

What does success look like, and how can we measure that?

GitLab Team and contributors are using the aforementioned concern for all new enum attributes without a column default value since such a concern is implemented and merged into master.

Links / references

Assignee Loading
Time tracking Loading