Saving enum from select in Rails 4.1

Ruby on-RailsRubyRuby on-Rails-4Enums

Ruby on-Rails Problem Overview


I am using the enums in Rails 4.1 to keep track of colors of wine.

Wine.rb

class Wine < ActiveRecord::Base
    enum color: [:red, :white, :sparkling]
end

In my view, I generate a select so the user can select a wine with a certain color

f.input :color, :as => :select, :collection => Wine.colors

This generates the following HTML:

<select id="wine_color" name="wine[color]">
  <option value=""></option>
  <option value="0">red</option>
  <option value="1">white</option>
  <option value="2">sparkling</option>
</select>

However, upon submitting the form, I receive an argument error stating '1' is not a valid color. I realize this is because color must equal 1 and not "1".

Is there a way to force Rails to interpret the color as an integer rather than a string?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Alright, so apparently, you shouldn't send the integer value of the enum to be saved. You should send the text value of the enum.

I changed the input to be the following:

f.input :color, :as => :select, :collection => Wine.colors.keys.to_a

Which generated the following HTML:

<select id="wine_color" name="wine[color]">
  <option value=""></option>
  <option value="red">red</option>
  <option value="white">white</option>
  <option value="sparkling">sparkling</option>
</select>

Values went from "0" to "red" and now we're all set.


If you're using a regular ol' rails text_field it's:

f.select :color, Wine.colors.keys.to_a

If you want to have clean human-readable attributes you can also do:

f.select :color, Wine.colors.keys.map { |w| [w.humanize, w] }

Solution 2 - Ruby on-Rails

No need converting the enum hash to array with to_a. This suffice:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }

Solution 3 - Ruby on-Rails

I just put together an EnumHelper that I thought I'd share to help people who need more customised enum labels and locales for your enum selects.

module EnumHelper

  def options_for_enum(object, enum)
    options = enums_to_translated_options_array(object.class.name, enum.to_s)
    options_for_select(options, object.send(enum))
  end

  def enums_to_translated_options_array(klass, enum)
    klass.classify.safe_constantize.send(enum.pluralize).map {
        |key, value| [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}"), key]
    }
  end

end

In your locale:

 en:
   activerecord:
     enums:
      wine:
        color:
          red:   "Red Wine"
          white:  "White Wine"

In your views:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>

Solution 4 - Ruby on-Rails

The accepted solution didn't work for me for the human readable, but I was able to get it to work like this:

<%= f.select(:color, Wine.colors.keys.map {|key| [key.humanize, key]}) %>

This was the cleanest, but I really needed to humanize my keys:

<%= f.select(:color, Wine.colors.keys) %>

Solution 5 - Ruby on-Rails

If you use enum in Rails 4 then just call Model.enums:

f.select :color, Wine.colors.keys

To create HTML:

<select name="f[color]" id="f_color">
    <option value="red">red</option>
    <option value="white">white</option>
    <option value="sparkling"> sparkling </option>
</select>

Or add method in controller:

def update_or_create
    change_enum_to_i
    ....
end

def change_enum_to_i
    params[:f]["color"] = params[:f]["color"].to_i
end

Solution 6 - Ruby on-Rails

If you need to handle the i18n based on the enum keys you can use:

<%= f.select :color, Wine.colors.keys.map {|key| [t("wine.#{key}"), key]} %>

and in the tranlations you can set the colors:

wine:
 red: Red
 white: White

Solution 7 - Ruby on-Rails

Here is what worked for me, Rails 4+:

class Contract < ApplicationRecord

enum status: { active:  "active", 
               ended: "active", 
               on_hold: "on_hold", 
               terminated:  "terminated", 
               under_review:  "under_review" , 
               unknown: "unknown" 
              }


end

in my _form.html.erb , I have this:

  <div class="field">
    <%= form.select :status, Contract.statuses.keys, {}%>
  </div>

test from Console after adding a record:

2.3.0 :001 > Contract.last.status
  Contract Load (0.2ms)  SELECT  "contracts".* FROM "contracts" ORDER BY "contracts"."id" DESC LIMIT ?  [["LIMIT", 1]]
 => "active"

Solution 8 - Ruby on-Rails

Here's my solution (my roles have underscores in them like "sales_rep"), and for some reason this was how I needed to get a blank option to work (with simpleform?):

In ApplicationHelper:

def enum_collection_for_select(attribute, include_blank = true)
  x = attribute.map { |r| [r[0].titleize, r[0]] }
  x.insert(0,['', '']) if include_blank == true
  x
end

Then in my form:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>

Solution 9 - Ruby on-Rails

for me the following worked as well:

= f.input :color, collection: Wine.colors.keys.map{ |key| [key.humanize, key] }, include_blank: false

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrian WeinreichView Question on Stackoverflow
Solution 1 - Ruby on-RailsBrian WeinreichView Answer on Stackoverflow
Solution 2 - Ruby on-RailsFellow StrangerView Answer on Stackoverflow
Solution 3 - Ruby on-RailsAndrew CetinicView Answer on Stackoverflow
Solution 4 - Ruby on-RailsTom RossiView Answer on Stackoverflow
Solution 5 - Ruby on-RailsogelacinycView Answer on Stackoverflow
Solution 6 - Ruby on-RailsPaulo FidalgoView Answer on Stackoverflow
Solution 7 - Ruby on-Railsz atefView Answer on Stackoverflow
Solution 8 - Ruby on-RailsGreg BlassView Answer on Stackoverflow
Solution 9 - Ruby on-RailsOuttaSpaceTimeView Answer on Stackoverflow