Repository: rails/rails
File: activerecord/lib/active_record/enum.rb (Ruby)

Code before:
    private
      def _enum(name, values, prefix: nil, suffix: nil, scopes: true, **options)
        assert_valid_enum_definition_values(values)
        # statuses = { }
        enum_values = ActiveSupport::HashWithIndifferentAccess.new
        name = name.to_s

        # def self.statuses() statuses end
        detect_enum_conflict!(name, name.pluralize, true)
        singleton_class.define_method(name.pluralize) { enum_values }
        defined_enums[name] = enum_values

        detect_enum_conflict!(name, name)
        detect_enum_conflict!(name, "#{name}=")

        attribute(name, **options) do |subtype|
          subtype = subtype.subtype if EnumType === subtype
          EnumType.new(name, enum_values, subtype)
        end

        value_method_names = []
        _enum_methods_module.module_eval do
          prefix = if prefix
            prefix == true ? "#{name}_" : "#{prefix}_"
          end

          suffix = if suffix
            suffix == true ? "_#{name}" : "_#{suffix}"
          end

          pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
          pairs.each do |label, value|
            enum_values[label] = value
            label = label.to_s

            value_method_name = "#{prefix}#{label}#{suffix}"
            value_method_names << value_method_name
            define_enum_methods(name, value_method_name, value, scopes)

            method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
            value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"

            if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
              value_method_names << value_method_alias
              define_enum_methods(name, value_method_alias, value, scopes)
            end
          end
        end
        detect_negative_enum_conditions!(value_method_names) if scopes
        enum_values.freeze
      end

Review comment (suggestion):
Hum, I really don't like this overhead :/

The core of the problem is that we have to define the enum when the model is loaded, but it's only later, when the schema is loaded, that we'll know if the column exist.

Having an overhead on every assignment doesn't feel right when this could be checked only once when the schema is loaded.

I think there are some precedent for module that redefine the `load_schema` method (not sure what the actual name is just like that) to use it as a hook to validate some previous declarations.

Code after:
        return _enum(name, values, **options)
      end

      definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
      options.transform_keys! { |key| :"#{key[1..-1]}" }

      definitions.each { |name, values| _enum(name, values, **options) }
    end

    private
      def _enum(name, values, prefix: nil, suffix: nil, scopes: true, **options)
        assert_valid_enum_definition_values(values)
        # statuses = { }
        enum_values = ActiveSupport::HashWithIndifferentAccess.new
        name = name.to_s

        # def self.statuses() statuses end
        detect_enum_conflict!(name, name.pluralize, true)
        singleton_class.define_method(name.pluralize) { enum_values }
        defined_enums[name] = enum_values

        detect_enum_conflict!(name, name)
        detect_enum_conflict!(name, "#{name}=")

        attribute(name, **options) do |subtype|
          subtype = subtype.subtype if EnumType === subtype
          EnumType.new(name, enum_values, subtype)
        end

        value_method_names = []
        _enum_methods_module.module_eval do
          prefix = if prefix
            prefix == true ? "#{name}_" : "#{prefix}_"
          end

          suffix = if suffix
            suffix == true ? "_#{name}" : "_#{suffix}"
          end

          pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
          pairs.each do |label, value|
            enum_values[label] = value
            label = label.to_s

            value_method_name = "#{prefix}#{label}#{suffix}"
            value_method_names << value_method_name
            define_enum_methods(name, value_method_name, value, scopes)

            method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
            value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"