views:

258

answers:

6

I have items in my database schema that are an integer and are set to a specific number correlating to an item. For instance, a column named appointment_type can have the value set of 0=Unknown, 1=Medical, 3=Trial, etc... I don't want to use magic numbers in my rails code and would prefer an Enum like solution to make the code more maintainable and readable. Furthermore, there is more than one table that has this appointment_type column, so I want to be able to use the "enum" to address other columns as well.

I was thinking having a global enum like item since I will need to access this in my model, controller and view. It may be less likely that I need to access it in my model, but definitely within the controller and view.

Is there a good way to handle this?

A: 

Why don't you put it in the Application Controller? This way, it can be accessed from any other controller (since they all inherit from ApplicationController), and passed to the view when needed.

NolanDC
+4  A: 

It may be less likely that I need to access it in my model, but definitely within the controller and view.

Are you sure? When you talk about database and schema, you are talking about the Model part of your application. I think the best place to store these variables would be the model that uses them.

If these variables belongs to a single model you can store them directly in the model itself:

class Item < ActiveRecord::Base
  STATUS_UNKNOWN = 0
  STATUS_MEDICAL = 3
end

Then you can reference the values inside and outside the model scope

class Item
  def my_method
    STATUS_UNKNOWN
  end
end

Item::STATUS_UNKNOWN # => 0
Item.new.my_method # => 0

When there is an enumeration of values, Rubyists often use hashes or arrays.

class Item
  AVAILABLE_STATUSES = { :unkwnown => 0, :medical => 3 }

  def self.statuses
    self.AVAILABLE_STATUSES.keys.sort
  end

  def self.status_value(key)
    self.AVAILABLE_STATUSES[:key]
  end

end

Item::AVAILABLE_STATUS # => { :unkwnown => 0, :medical => 3 }
Item.statuses # => [:medical, :unkwnown]
Item.status_value(:medical) # => 3

If more than one model share the same logic, you can put it in a Module and mix the module in all Models that requires it.

Simone Carletti
Where would I put the module? In lib/ ?
intargc
You can use /lib, although I usually prefer to create a folder called /apps/concerns with all controller/model custom mixins.
Simone Carletti
A: 

My call would be to put that in the associated model

marcgg
A: 

this code could help you...

First, create in the lib folder a file named:

integer_to_enum.rb

In the file put this code:

module IntegerToEnum

  class << self

    def included(klass)
      klass.extend(ModelClassMethods)
    end

  end

  module ModelClassMethods

    def fields_to_enum(options={})
      class_eval <<-end_eval
        def set_enum_options
          @enum_options = #{options.inspect}
        end
      end_eval

      options.each_pair do |k,v|
        class_eval <<-end_eval
          def #{k.to_s}
            self.set_enum_options
            @enum_options[:#{k.to_s}][read_attribute(:#{k.to_s}).to_i]
          end

          def #{k.to_s}=(v)
            self.set_enum_options
            unless @enum_options[:#{k.to_s}].include?(v)
              return false
            end

            write_attribute(:#{k.to_s}, @enum_options[:#{k.to_s}].index(v))
            @#{k.to_s}
          end
        end_eval
      end
    end
  end

end

On the enviroment.rb, put this at the bottom, after 'end'

ActiveRecord::Base.send :include, IntegerToEnum

And last, in the model you want to 'translate' the Integer field put:

class YourModel < ActiveRecord::Base

  fields_to_enum :appointment_type => [:unknown, :medical, :trial], :other_field_type => [:type_a, :type_b, :type_c]

end

With this, you could do something like:

m = YourModel.find(1)
m.appointment_type #=> :unknown
m.appointment_type = :trial #=> :trial
m.save #=> and this will save with the index value of the array definition, since ':trial' is in the 3er position, the sql resulting will save with this number

and that all.. hope to help you

Alvaro Talavera
Note that this code could be much better and elegant, sorry my english ;)
Alvaro Talavera
A: 

You could have a table which holds all this information and cache the initial table select on environment load.

That way you could even implement referential integrity between the appointment_types table and also the appointment_type_id table on other tables.

Omar Qureshi