Skip to content

Feature Request: Support string array enum values #27

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!

Activity

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

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