Reputation: 890
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>'
Upvotes: 12
Views: 7527
Reputation: 1451
You can use array_enum
gem that was created just for this usecase https://github.com/freeletics/array_enum
Upvotes: 7
Reputation: 2208
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.
Upvotes: 1
Reputation: 7231
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.
Upvotes: 2
Reputation: 2744
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
Upvotes: -3