Ruby on Rails
HowtoUseSetAndEnumColumns

TODO: PostgreSQL 8.3+ includes ENUM type so this is not MySQL specific anymore.

ActiveRecord does not support the SET and ENUM column types directly. You should probably not use non-standard, mySQL-specific column types like SET or ENUM in your Rails applications.

It is recommended that you use VARCHAR columns along with Validations in situations that require ENUM or SET-like behaviour.

Another possibility is this plugin:
Enum Column in MySql

In particular, see validates_inclusion_of to check that the values submitted are allowed in that particular column, or read below for quick reference:
In your model put something like this:


Statuses = {
  :active => "ACTIVE", 
  :inactive =>"INACTIVE",
  :unspecified => nil
  }
validates_inclusion_of :status, :in => Statuses.values



For SET type, let’s use a multiple selection in the view, however, you should not use API like
<%= select(object, method, choices, options = {}, html_options = { :multiple => "" }) %>

This outputs as
<select name="object[method]" multiple="multiple">
  <option value="choice_1">choice_1</option>
  <option value="choice_2">choice_2</option>
  ....
  <option value="choice_n">choice_n</option>
</select>

and params[:object][:method] receives only one item.

Instead, write HTML code directly like

<select id="object_method" name="object[method][]" multiple="multiple">
  <% for choice in choices %>
    <% if @object.method and @object.method.split(',').include?(choice) %>
     <option value="<%= choice %>" selected><%= choice %></option>
    <% else %>
     <option value="<%= choice %>"><%= choice %></option>
    <% end %>      
  <% end %>
</select>
Ctrl+Click to choose multiple items. 

Note name="object[method][]". You can receive multiple items as an array. (The if statement is to be automatically selected in the edit view.)

In your controller, you will convert the received array to string in the form of “item,item,item”.
Then, the ‘save’ function can handle it.
if params[:object][:method].instance_of?(Array)
  params[:object][:method] = params[:object][:method].join(',')
else
  params[:object][:method] = "" 
end

@object = Object.new(params[:object])
@object.save

As you might’ve noticed, it is not necessary to use SET type, you can do the same way with VARCHAR type. In the case, you may like to validate the data by yourself, refer HowtoValidate.
- sonots


For ENUM type, let’s use select tag in the similiar way with above.
<select id="object_method" name="object[method]">
  <% for choice in choices %>
    <% if @object.method and @object.method == choice %>
     <option value="<%= choice %>" selected="selected"><%= choice %></option>
    <% else %>
     <option value="<%= choice %>"><%= choice %></option>
    <% end %>      
  <% end %>
</select>

That’s it. If you validate by yourself, you can do the same way with VARCHAR. I wish
<%= select 'object', 'method', choices, options = {}, html_options = {} %>

set ‘selected’ automatically if @object.method exists as above in the near future. – sonots


Is there any way to extract the array of choices from the Model? It doesn’t seem right to me to have the list of choices right in the rhtml file. -lode


I put it at the top of the model’s file above the class.


MySQL’s ENUM and SET types are designed to support high performance queries since they are stored as a bitset. You lose this when you switch to VARCHAR.


Here’s a simple way to do the same thing. In the model,

Types = %w(Air Environmental Health Safety Occupancy Water)
validates_inclusion_of :type, :in => Types

and in the view,

<%= select_tag :type, options_for_select(Model::Types)  %>



Are you sure? The code above produces this:

NoMethodError in Model#new

Showing model/new.html.erb where line #19 raised:

undefined method `Types' for #&lt;Class:0x241a904&gt;