Search code examples
rubyenumerationlanguage-construct

How can I use C# style enumerations in Ruby?


I just want to know the best way to emulate a C# style enumeration in Ruby.


Solution

  • 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 fine

    WINDOW_STATES = [:minimized, :maximized].freeze
    

    Use it like this

    WINDOW_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 type

    window_state.in? WINDOW_STATES