What is the best way to handle constants in Ruby when using Rails?
Solution 1
You can use an array or hash for this purpose (in your environment.rb):
OPTIONS = ['one', 'two', 'three']
OPTIONS = {:one => 1, :two => 2, :three => 3}
or alternatively an enumeration class, which allows you to enumerate over your constants as well as the keys used to associate them:
class Enumeration
def Enumeration.add_item(key,value)
@hash ||= {}
@hash[key]=value
end
def Enumeration.const_missing(key)
@hash[key]
end
def Enumeration.each
@hash.each {|key,value| yield(key,value)}
end
def Enumeration.values
@hash.values || []
end
def Enumeration.keys
@hash.keys || []
end
def Enumeration.[](key)
@hash[key]
end
end
which you can then derive from:
class Values < Enumeration
self.add_item(:RED, '#f00')
self.add_item(:GREEN, '#0f0')
self.add_item(:BLUE, '#00f')
end
and use like this:
Values::RED => '#f00'
Values::GREEN => '#0f0'
Values::BLUE => '#00f'
Values.keys => [:RED, :GREEN, :BLUE]
Values.values => ['#f00', '#0f0', '#00f']
Solution 2
I put them directly in the model class, like so:
class MyClass < ActiveRecord::Base
ACTIVE_STATUS = "active"
INACTIVE_STATUS = "inactive"
PENDING_STATUS = "pending"
end
Then, when using the model from another class, I reference the constants
@model.status = MyClass::ACTIVE_STATUS
@model.save
Solution 3
If it is driving model behavior, then the constants should be part of the model:
class Model < ActiveRecord::Base
ONE = 1
TWO = 2
validates_inclusion_of :value, :in => [ONE, TWO]
end
This will allow you to use the built-in Rails functionality:
>> m=Model.new
=> #<Model id: nil, value: nil, created_at: nil, updated_at: nil>
>> m.valid?
=> false
>> m.value = 1
=> 1
>> m.valid?
=> true
Alternatively, if your database supports enumerations, then you can use something like the Enum Column plugin.
Solution 4
Rails 4.1 added support for ActiveRecord enums.
Declare an enum attribute where the values map to integers in the database, but can be queried by name.
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
conversation.archived!
conversation.active? # => false
conversation.status # => "archived"
Conversation.archived # => Relation for all archived Conversations
See its documentation for a detailed write up.
Solution 5
You can also use it within your model inside a hash like this:
class MyModel
SOME_ATTR_OPTIONS = {
:first_option => 1,
:second_option => 2,
:third_option => 3
}
end
And use it like this:
if x == MyModel::SOME_ATTR_OPTIONS[:first_option]
do this
end
Related videos on Youtube
Comments
-
Jblanc almost 2 years
I have some constants that represent the valid options in one of my model's fields. What's the best way to handle these constants in Ruby?
-
Mitkins over 15 years
-
-
Jblanc over 15 yearsI decided to go with this solution. I have to say, it does just what I want and plays nicely with ActiveRecord. Thank you! :D
-
weexpectedTHIS about 12 yearsThat is a method not a class. Need to use "class" instead of "def".
-
chipairon about 11 yearsThanks for this. It was an inspiration to group symbols in an array.
-
Islam Azab over 8 yearsI think this is the best answer.
-
BitOfUniverse almost 8 yearsBe ready to get exceptions when you try to assign some value that is not in the list to the status field, and be ready to get name conflicts if you add another enum field with [:active] value. Rails implementation of this feature really awkward.
-
cristi_razvi over 6 yearsi have made a gem for validating enums inclusion github.com/CristiRazvi/enum_attributes_validation