Skip to content

Feature Request: Support string array enum values #27

@htcarr3

Description

@htcarr3

Currently array_enum assumes you have integer values stored in the db (and I recognize that the README explains this clearly). The query it generates casts values as integers:

where("#{table_name}.#{attr_name} #{comparison_operator} ARRAY[:db_values]::integer[]", db_values: db_values)

The generated scopes cause errors for any implementations where you have an array_enum defined like

array_enum roles: ["foo", "bar", "baz"].index_by(&:itself) # this is a string array column

It seems pretty trivial to just assume integer or varchar based on the values:

  def array_enum(definitions)
    definitions.each do |attr_name, mapping|
      attr_symbol = attr_name.to_sym
      mapping_hash = ActiveSupport::HashWithIndifferentAccess.new(mapping)

      define_singleton_method(attr_name.to_s.pluralize) do
        mapping_hash
      end

      {
        "with_#{attr_name}" => '@>',
        "only_with_#{attr_name}" => '=',
        "with_any_of_#{attr_name}" => '&&'
      }.each do |method_name, comparison_operator|
        define_singleton_method(method_name.to_sym) do |values|
          cast_as = "integer"
          db_values = Array(values).map do |value|
            unless value.is_a?(Integer)
              cast_as = "varchar"
            end
            mapping_hash[value] || raise(ArgumentError, format(MISSING_VALUE_MESSAGE, value: value, attr: attr_name))
          end
          where("#{table_name}.#{attr_name} #{comparison_operator} ARRAY[:db_values]::#{cast_as}[]", db_values: db_values)
        end
      end

      define_method(attr_symbol) do
        Array(self[attr_symbol]).map { |value| mapping_hash.key(value) }
      end

      define_method("#{attr_name}=".to_sym) do |values|
        self[attr_symbol] = Array(values).map do |value|
          mapping_hash[value] || raise(ArgumentError, format(MISSING_VALUE_MESSAGE, value: value, attr: attr_name))
        end.uniq
      end
    end
  end

Side Note: Rails has changed the argument structure for enums. This would be a breaking change for this gem but consider updating the API to look like:

  array_enum :roles, ["foo", "bar", "baz"].index_by(&:itself)

Happy to open a pr(s) for this!

Thanks for the great gem!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions