Fellow Stranger
Fellow Stranger

Reputation: 34023

Share enum declaration values with multiple attributes

I want to have a class with several attributes that saves weekdays with numeric values.

summary_weekday    :integer
collection_weekday :integer

I thought I could map the integers to values using Enum with two declarations:

enum summary_weekday: %w(monday tuesday wednesday thursday friday saturday sunday)
enum collection_weekday: %w(monday tuesday wednesday thursday friday saturday sunday)

But Rails doesn't accept that, I cannot define the same value twice in the same class.

You tried to define an enum named "summary_weekday" on the model "QuestionCategory", but this will generate a instance method "monday?", which is already defined by another enum.

How can I solve this?

Upvotes: 33

Views: 11526

Answers (3)

Giles Bathgate
Giles Bathgate

Reputation: 752

As of Rails 5.0 you can use the _prefix or _suffix options when you need to define multiple enums with same values. If the passed value is true, the methods are prefixed/suffixed with the name of the enum.

class Invoice < ActiveRecord::Base
  enum verification: [:done, :fail], _prefix: true
end

It is also possible to supply a custom prefix.

class Invoice < ActiveRecord::Base
  enum verification: [:done, :fail], _prefix: :verification_status
end

Upvotes: 35

Archernar
Archernar

Reputation: 463

In case people are still looking for a solution for Rails 4, I just tried this the other day and it seems to work well. I need a similar system to work because of various checks an admin must do on an object. I solved it by creating a Model Concern and using the class method in the Model:

In models/concerns/review_states.rb

require 'active_support/concern'

module ReviewStates extend ActiveSupport::Concern

  class_methods do
    REVIEW_STATES = {not_initiated:0, in_progress:1, completed:2}
    SIGNUP_STATES = {pending:0, activated:1, disabled:2}
    def prefix_review_states(prefix)
      Hash[REVIEW_STATES.map { |k,v| [k.to_s.prepend(prefix).to_sym, v] }]
    end
  end
end

In your model:

class ModelName < ActiveRecord::Base
  include ReviewStates

  enum one_status: prefix_review_states("one_")
  enum two_status: prefix_review_states("two_")

This seems to make all of the correct database queries.

Good luck!

Upvotes: 1

kiddorails
kiddorails

Reputation: 13014

What you are looking for cannot be obtained by enum, as it will try to create methods like object.tuesday? for both of the enums, which is logically wrong.

I recall DataMapper ORM to have exactly this kind of support. For ActiveRecord, you will probably have to create getter and setter methods manually for these properties and map it there, like:

WEEKDAYS = %w(monday tuesday wednesday thursday friday saturday sunday)

def summary_weekday
  WEEKDAYS[read_attribute(:summary_weekday).to_i] 
end

def summary_weekday=(value)
  write_attribute(:summary_weekday, WEEKDAYS.index(value.to_s))
end

The code above may need some mangling while typecasting etc. I know that this is not a generic proof and I will really love to know any better solution for this interesting problem; will certainly be very useful.

Courtesy - Ruby Forum

Hope this helps.

Upvotes: 7

Related Questions