Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Rails 5 enum with PG array

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>'
like image 818
yskkin Avatar asked Jan 15 '17 09:01

yskkin


Video Answer


2 Answers

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.

like image 88
milgner Avatar answered Oct 23 '22 19:10

milgner


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

like image 28
morgoth Avatar answered Oct 23 '22 19:10

morgoth