Problem
In my Rails app, I have two classes each with the same enum for status. Code climate is complaining about duplication.
class Enrolment < ActiveRecord::Base
has_many :enrolment_presentations
has_many :presentations, through: :enrolment_presentations
enum status: {
'Pending' => 1,
'Suspended' => 2,
'Cancelled' => 3,
'Pass' => 4,
'Fail' => 5,
'DPR' => 6,
'Unknown' => 7,
'Enrolled' => 8
}
end
class EnrolmentPresentations < ActiveRecord::Base
belongs_to :enrolment
belongs_to :presentation
enum status: {
'Pending' => 1,
'Suspended' => 2,
'Cancelled' => 3,
'Pass' => 4,
'Fail' => 5,
'DPR' => 6,
'Unknown' => 7,
'Enrolled' => 8
}
end
How would you go about removing this duplication?
Solution
ActiveRecord enums “are exposed through a class method with the pluralized attribute name”, so I recommend the following code. However, I would highly recommend making the enums symbols instead of strings for compatibility. Also, enrollment is spelled with two l’s. Note that you can control the capitalization of DPR using inflections.
class Enrollment < ActiveRecord::Base
has_many :enrollment_presentations
has_many :presentations, through: :enrollment_presentations
enum status: {
pending: 1,
suspended: 2,
cancelled: 3,
pass: 4,
fail: 5,
dpr: 6,
unknown: 7,
enrolled: 8
}
end
class EnrollmentPresentations < ActiveRecord::Base
belongs_to :enrollment
belongs_to :presentation
enum status: Enrollment.statuses
end
In Rails 6, you can put the enum in a module in concerns
and include it in your models.
model/concerns/statusable.rb
module Statusable
extend ActiveSupport::Concern
included do
enum status: {
pending: 1,
suspended: 2,
cancelled: 3,
pass: 4,
fail: 5,
dpr: 6,
unknown: 7,
enrolled: 8
}
end
end
model/enrollment.rb
class Enrolment < ApplicationRecord
include Statusable
has_many :enrolment_presentations
has_many :presentations, through: :enrolment_presentations
end
Forgive my ignorance, but isn’t ActiveRecord::Base#enum
just a method? Therefore, to help reduce duplication (but not eliminate it), you could do something like this:
STATUS_FLAGS = {
'Pending' => 1,
'Suspended' => 2,
'Cancelled' => 3,
'Pass' => 4,
'Fail' => 5,
'DPR' => 6,
'Unknown' => 7,
'Enrolled' => 8
}
class Enrolment < ActiveRecord::Base
has_many :enrolment_presentations
has_many :presentations, through: :enrolment_presentations
enum status: STATUS_FLAGS
end
class EnrolmentPresentations < ActiveRecord::Base
belongs_to :enrolment
belongs_to :presentation
enum status: STATUS_FLAGS
end
This, of course, doesn’t eliminate duplication. One way around that might be to use a module
as a mixin (though I’m not sure how that would work with ActiveRecord
) or use singular inheritance to create a class that extends ActiveRecord::Base
with the enum status
, and then inherit from that – though I have to admit it feels hacky extending Active Records through inheritance.
As a stylistic note, it appears that the convention for #enum
is to use lowercase names rather than PascalCase.