Enum

I want an enum of sorts in my Rails application.

I have a Project object, which has four possible status settings, as
indicated here:

class Project < ActiveRecord:Base

class << self
def statuses
[ ‘Hidden’, ‘Available’, ‘In progress’, ‘Complete’ ]
end
end

end

These are not expected to change, so hardcoding is preferable. Of
course, there is a ‘status’ column in the project table, so rails
creates a ‘status’ accessor.

However, later in the code I will want to check the status myself. For
example, ‘Complete’ projects won’t be shown in the same place as
‘Available’ ones, and ‘In progress’ and ‘Hidden’ projects won’t be shown
at all (not sure about In progress ATM though, so I want to keep it
flexible). Now, it would be pretty ugly checking

if a_project.status == 2 # Project is in progress

So one possibility is to add methods like

class Project
def in_progress?
self.status == 2
end
end

Another interesting option is this:

class Project
def status_is?(_status)
[ :hidden, :available, :in_progress, :complete ][self.status] ==
_status
end
end

If only I could count on hashes staying in order, a very pretty solution
would
be:

class Project
class << self
def statuses
{ :hidden => ‘Hidden’, :available => ‘Available’,
:in_progress => ‘In progress’, :complete => ‘Complete’ }
# Use Project.statuses.values for the dropdown
end
end
def status_is?(_status)
Project.statuses.keys[self.status] == _status
end
end

Unfortunately, “The order in which you traverse a hash by either key or
value may seem arbitrary, and will generally not be in the insertion
order.” (ri).

Any other ideas for elegance?

Hi

adding some informations to your hash may help, something like…

{ :hidden => {:id => 1, :label => ‘Hidden’}, :available => {:id => 2,
:label => ‘Available’}, :in_progress => {:id => 3, :label => ‘In
progress’}, :complete => {:id => 4, :label => ‘Complete’ }}

gives you an index to trust

and next you can do something like

(note, the code isn’t checked so probably won’t work as it is)

statuses.each do |key, value|
define_method(key.to_s + ‘?’) {
self.status == value[:id]
}
end

to have all the hidden? available? etc. methods.

Anyway I think that if you have a look to the rails humanize method
you could avoid the label field so you can manage the statuses with a
simple hash or even an array.

Paolo

On Sep 5, 2006, at 5:24 AM, Ohad L. wrote:

  end

‘Available’ ones, and ‘In progress’ and ‘Hidden’ projects won’t be
shown
at all (not sure about In progress ATM though, so I want to keep it
flexible). Now, it would be pretty ugly checking

if a_project.status == 2 # Project is in progress

[snip]
Any other ideas for elegance?

I usually do:

class A
[ ‘Hidden’, ‘Available’, ‘Etc’ ].each do |s|
const_set(s, k = Object.new)
def k.to_s
“A::#{s}”
end
k.freeze
end
end

Then you can say if obj.status == A::Available, etc.

Paolo, so beautiful it makes me weep. Thanks a bunch :slight_smile:

I ended up going for a redundant combo solution, in case I wanted to use
more than one way:

class Project

statuses.each_with_index do |_status, i|
define_method("#{_status.to_s}?") do
self.status == i
end
end

def status_is(sym)
  statuses[self.status] == sym
end
...

end

…and using humanize for the form.

Logan: Thanks, but that won’t give me numerical values like I want (I
want the indices stored in mysql, this is a rails app). I can see how to
apply that to my case though.

Hmm, which reminds me. Why don’t we get one of these with ruby?

module Enumerable
def collect_with_index
arr = []
self.each_with_index do |a,i|
arr << yield(a,i)
end
arr
end
end

On Sep 5, 2006, at 9:06 AM, Ohad L. wrote:

end

We do:

require ‘enumerator’
[‘a’, ‘b’, ‘c’].to_enum(:each_with_index).map { |value, index|
{ index => value } } #=> [{0=>‘a’}, {1=>‘b’}, {2=>‘c’}]

Use Struct.