12

I'm trying to use Rails' enum with PostgreSQL's array column.

class Post < ActiveRecord::Base
  enum tags: { a: 0, b: 1, c: 2 }, array: true
end

However the above code does not work

Is there any way to using enum on array column like arrtibute supporting array: true?

EDIT

I would like to see that the following test case passes, but actually it fails.

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "5.1.4"
  gem "pg"
end

require "active_record"
require "minitest/autorun"
require "logger"

# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "test")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :products, force: true do |t|
    t.integer :type_delivery, default: [], array: true, limit: 8
  end
end

class Product < ActiveRecord::Base
  enum type_delivery: { a: 1, b: 2, c: 3, d: 5 }, array: true
end

class BugTest < Minitest::Test
  def test_array_enum
    product = Product.create!(type_delivery: %w[a b c])
    assert_equal products.type_delivery, %w[a b c]
  end
end

error is:

/usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/enum.rb:172:in `block (2 levels) in enum': undefined method `each_with_index' for true:TrueClass (NoMethodError)
    from /usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/enum.rb:171:in `module_eval'
    from /usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/enum.rb:171:in `block in enum'
    from /usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/enum.rb:154:in `each'
    from /usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.1.4/lib/active_record/enum.rb:154:in `enum'
    from guides/bug_report_templates/active_record_gem.rb:38:in `<class:Product>'
    from guides/bug_report_templates/active_record_gem.rb:37:in `<main>'
1
  • Did you find a solution to this? Commented Feb 2, 2018 at 17:03

4 Answers 4

7

You can use array_enum gem that was created just for this usecase https://github.com/freeletics/array_enum

Sign up to request clarification or add additional context in comments.

Comments

2

Since this question is still without answer, this is how it can be done:

First, there is no array: true option on the enum method, just leave it out.

Second, add a custom scope to retrieve the products matching the delivery

scope :with_delivery_type, ->(*delivery_types) do
  normalized = Array(delivery_types).flatten
  where('delivery_types @> ?', "{#{normalized.join(',')}}")
end

Last but not least, I'd recommend using a string or Postgres enum type instead of integer columns. Integer columns are problematic because for one, to read it, one needs the source code of the application that wrote the record (the version at the time of insertion) and second it is unnecessarily hard to remove or replace values.

Comments

1

Rails' enum api maps attributes to a single integer column. If you wanted, you could create your own bit mask to support multiple attributes (like is often done with user roles).

I think what you're looking for is the attributes api and you could even implement a custom ActiveRecord::Type to handle validation.

Comments

-3

Rails 5++

rails generate migration AddTypeDeliveryToProducts type_delivery:integer

class AddTypeDeliveryToProducts < ActiveRecord::Migration[5.1]
  def change
    add_column :products, :type_delivery, :integer, array:true, default: [], limit: 8
  end
end

class Product < ApplicationRecord
  enum type_delivery: { a: 1, b: 2, c: 3, d: 4 }
end

1 Comment

Your code does not work. Please see my edit on the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.