How can I use C# style enumerations in Ruby?
Specifically, I would like to be able to perform logical tests against the set of values given some variable. Example would be the state of a window: "minimized, maximized, closed, open"If you need the enumerations to map to values (eg, you need minimized to equal 0, maximised to equal 100, etc) I'd use a hash of symbols to values, like this:
WINDOW_STATES = { :minimized => 0, :maximized => 100 }.freeze
The freeze (like nate says) stops you from breaking things in future by accident.You can check if something is valid by doing this
WINDOW_STATES.keys.include?(window_state)
Alternatively, if you don't need any values, and just need to check 'membership' then an array is fineWINDOW_STATES = [:minimized, :maximized].freeze
Use it like thisWINDOW_STATES.include?(window_state)
If your keys are going to be strings (like for example a 'state' field in a RoR app), then you can use an array of strings. I do this ALL THE TIME in many of our rails apps.WINDOW_STATES = %w(minimized maximized open closed).freeze
This is pretty much what rails validates_inclusion_of
validator is purpose built for :-)Personal Note:
I don't like typing include? all the time, so I have this (it's only complicated because of the .in?(1, 2, 3) case:class Object
# Lets us write array.include?(x) the other way round
# Also accepts multiple args, so we can do 2.in?( 1,2,3 ) without bothering with arrays
def in?( *args )
# if we have 1 arg, and it is a collection, act as if it were passed as a single value, UNLESS we are an array ourselves.
# The mismatch between checking for respond_to on the args vs checking for self.kind_of?Array is deliberate, otherwise
# arrays of strings break and ranges don't work right
args.length == 1 && args.first.respond_to?(:include?) && !self.kind_of?(Array) ?
args.first.include?( self ) :
args.include?( self )
end
end
end
This lets you typewindow_state.in? WINDOW_STATES
How to implement Enums in Ruby?
Two ways. Symbols (:foo
notation) or constants (FOO
notation).
Symbols are appropriate when you want to enhance readability without littering code with literal strings.
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
Constants are appropriate when you have an underlying value that is important. Just declare a module to hold your constants and then declare the constants within that.module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
Added 2021-01-17If you are passing the enum value around (for example, storing it in a database) and you need to be able to translate the value back into the symbol, there's a mashup of both approaches
COMMODITY_TYPE = {
currency: 1,
investment: 2,
}
def commodity_type_string(value)
COMMODITY_TYPE.key(value)
end
COMMODITY_TYPE[:currency]
This approach inspired by andrew-grimm's answer https://stackoverflow.com/a/5332950/13468I'd also recommend reading through the rest of the answers here since there are a lot of ways to solve this and it really boils down to what it is about the other language's enum that you care about
Ruby - File for Enums
Good question. Simply require
the GlobalEnums.rb
module at the top of your file and then refer to the module and constant like this:
newGuy.currentContractType = ContractType::Internship
You'll note that in Ruby ::
is used to refer to a constant in a namespace (class or module), rather than .
. If you have more than one level of nesting, you just chain the ::
s:module Foo
module Bar
class Baz
Qux = "quux"
end
end
end
p Foo::Bar::Baz::Qux
# => "quux"
P.S. I suggest glancing through a Ruby style guide such as this one, in particular the Naming section. With rare exceptions†, method and variable names in Ruby are snake_case
. Module and class names are CamelCase
and other constants are usually SCREAMING_CAMEL_CASE
.With that in mind, a seasoned Rubyist would probably write your code like this:
module MyApp
module ContractType
UNDEFINED = 0
INTERNSHIP = 1
CLT = 2
CONTRACTOR = 4
end
end
# Assuming this is somewhere inside the MyApp namespace...
new_guy.current_contract_type = ContractType::INTERNSHIP
†For example, strict conversion methods like Integer(n)
and shortcut constructors like URI(str)
or Nokogiri::XML(str)
. Enums in Rails: uppercase or lowercase?
In Rails enums should be snake_case
.
Why? Because enums are used to construct method names. And method names in Ruby should be snake_case
according to the community convention. Using ALLCAPS
or CamelCase
can lead to bugs and confusion as Ruby treats such identifiers as constants.
ActiveRecord::Enum is not comparable to a language level enumeration construct such as in Java.
The keys mappings in an ActiveRecord::Enum are not constants. Rather it's just a list which is used with Ruby style metaprogramming that adds methods to make bitmask columns easier (and more fun) to work with.Declare an enum attribute where the values map to integers in the
database, but can be queried by name.
http://api.rubyonrails.org/classes/ActiveRecord/Enum.html
In your example its actually comparable to:
enum status: [ :ACTIVE, :DRAFT, :INACTIVE ]
Enums in Java are basically glorified switch statements where the class constants denote the possible values.An enum type is a special data type that enables for a variable to be
a set of predefined constants. The variable must be equal to one of
the values that have been predefined for it. Common examples include
compass directions (values of NORTH, SOUTH, EAST, and WEST) and the
days of the week.
https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
enum option new not working
The error clearly states that you cannot have an enum with new
key as it will conflict with an existing ActiveRecord
method. There is no way out of this.
This issue is not new and it has been discussed before.
I would recommend you to read
enum: Option not to generate "dangerous" class methods
As per Godfrey Chan
, Collaborator of Rails:
In this case, if you want to use enum, you are probably better off
renaming your label to something else. This is not unique to enums – a
lot of Active Record features generates methods for you and usually
there aren't ways to opt-out of those generated methods.Gonna give this one a close for now....
Rails 4 - how to use enum?
You're so close :) The fix is right in the error.
Your select is calling
Preference.self_governance.to_a.map { |p| [p.humanize, p] }
And your error tells you the pluralization is wrong. Remember that if you call enum on a single object, it will be @preference.self_governance
But if you call on the model itself, Preference, and request a collection it's plural.Preference.self_governances
Because enum
is special, uour enum's could just be arrays, instead of hashes:enum self_governance: [ tier_1, tier_2, tier_3, tier_4, tier_55 ]
enum autonomy: [ tier_11, tier_21, tier_31, tier_41, tier_51 ]
Your view would look like:<%= f.input :self_governance, as: :select, label: "Select your governance approach", collection: Preference.self_governances.map { |key, value| [key.humanize, key] } %>
It will store the index number of the array, like magic :) How to use an enum value in an :if validation
You can evaluate your field using pending?
class Job < ActiveRecord::Base
enum status: [ :active, :archived ]
validates_presence_of :explanation, if: Proc.new { |a| a.extended? }
end
Complete example : class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status # => "active"
# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status # => "archived"
# conversation.status = 1
conversation.status = "archived"
conversation.status = nil
conversation.status.nil? # => true
conversation.status # => nil
Reference:http://api.rubyonrails.org/classes/ActiveRecord/Enum.html
Rails 4 enum - how to test equality?
@user.domain_admin? # return true if :domain_admin
instead:@user.role == :domain_admin
use:@user.role == "domain_admin"
Some test:=> User.roles
=> {"user"=>0, "staff"=>1, "admin"=>2}
=> u = User.last
=> u.role
=> "user"
=> u.role == "user" # <-- this
=> true
=> User.roles.each_pair { |x, _| puts u.role == x }
=> true
=> false
=> false
Related Topics
How to Test If a Value Is a Prime Number in Ruby? Both The Easy and The Hard Way
Get Route for Base Class of Sti Class in Rails
Adding Two Activerecord::Relation Objects
How to Evaluate a Block Inside a Proc
Obtaining Number of Block Parameters
Using Rails with Paperclip and Swfupload
How to Render Blob Images in a Prawn Document
Rails 4 - Devise: Getting Actioncontroller::Unknownformat on Signup
Nlp to Classify/Label The Content of a Sentence (Ruby Binding Necesarry)
Rvm Finding System Gems' Executables
Rvm Can No Longer Install 1.8.7-P352 on MAC Os X Mountain Lion
Truncate String When It Is Too Long
Undefined Method 'Merge' for '####':String <%= Form_For %> Helper
How to Open File in Default Application. Ruby
How to Deploy a Threadsafe Asynchronous Rails App