lostintranslation
lostintranslation

Reputation: 24573

Best way to store Enum value in ActiveRecord and convert to string for display

I am trying to figure out what is the best way to store an enum value in activerecord but convert it to a 'title' for display in an app.

I.E.

Review Enum:

UNREVIEWED = {:title => "Unreviewed", :name => "UNREVIEWED"}
REVIEWED = {:title => "Reviewed", :name => "REVIEWED"}
FLAGGED = {:title => "Flagged as inappropriate", :name => "FLAGGED"} 

So in java land I was used to storing the ENUMs name ie (REVIEWED) in the database and then converting that name into that actual enum on the server such that I could call helper methods on it, ie:

review = Review.valueOf(review)
review.title()

Is there something similar I can do in rails to accomplish this?

FYI we are trying to keep our app super small so if I can easily accomplish this or something similar without a GEM that would be great.

Any 'standard' way to do this, as I imagine I am not the first to struggle with this issue?

Thanks!

Upvotes: 13

Views: 20091

Answers (5)

AlexChaffee
AlexChaffee

Reputation: 8252

I agree with @tamersalama. Here's a simple way to do it using strings in the DB with a custom Enum class. Note that this also supports human readable names for use in dropdown menus.

https://gist.github.com/alexch/a7be54e1b085718473ff

SQL:

Table name: snacks
 id                         :integer
 ice_cream                  :string

Rails:

class Snack < ActiveRecord::Base
  FLAVORS = Enum.new [
      [:vanilla, "Vanilla"],
      [:chocolate, "Chocolate"],
      [:rocky_road, "Rocky Road"]
      ])
  ]
end

HTML:

<%= simple_form_for @snack do |f| %>
<%= f.collection_select :ice_cream, Snack::FLAVORS, :value, :label %>
<% end %>

Upvotes: 0

Ramon Carlos
Ramon Carlos

Reputation: 65

Using @Godsaur example.

class MyModel < ActiveRecord::Base
  enum status: [:draft, :beta, :public]
end 

You can get the string value as:

MyModel.last.status
=> "beta"

But if you want a "title" you can:

MyModel.last.status.titleize
=> "Beta"

Upvotes: 5

tamersalama
tamersalama

Reputation: 4133

While I agree that @Godsaur answer is correct - I personally don't like the approach of storing integer values to represent meaningful string equivalents (provided that enough database indexing is made and/or the cost of querying strings in the DB engine is similar to that of querying integers).

My approach is usually storing the text values in the database (for easier understanding of DB records).

my_model.rb

class MyModel < ActiveRecord::Base

  STATUSES = HashWithIndifferentAccess.new({unverified: 'unverified', reviewed: 'reviewed', flagged: 'flagged'})
  #the text values are similar to their symbols key
  #you could also define additional attributes for each key if needed. Ex:
  #STATUSES = {unverified: {title: 'Unverified Text Title', txt: 'unverified'}, ...}

  # assuming a 'status' field
  scope :unverified, -> { where(status: STATUSES[:unverified]) }

  def unverified?
    status == STATUSES[:unverified]
  end

  # Or
  STATUSES.each do |sym, val|
    define_method("#{sym}?") {status == val}
  end
end

my_view.erb

<%= MyModel::STATUSES[@my_model.status] %> or 
<%= MyModel::STATUSES[@my_model.status].title %>

Upvotes: 1

alexs333
alexs333

Reputation: 12563

ActiveRecord enums is the best way to go since it's a part of the framework (since version 4.1).

Its usage is quite simple:

Migration:

class AddEnumToMyModel < ActiveRecord::Migration
  def change
    add_column :my_model, :status, :integer, default: 0
  end
end

Model:

class MyModel < ActiveRecord::Base
  enum status: [:draft, :beta, :public]
end

Then use it a will:

MyModel.draft # gets all drafts
MyModel.last.draft? # checks if the last model is draft
MyModel.last.status # gets the string description of the status of my model

For mode information refer to documentation.

Upvotes: 29

phoet
phoet

Reputation: 18835

there are a lot of posts about this issue, i guess that this points to most of them: http://thinkinginrails.com/2010/04/using-enums-for-constants/

i think that this is an over engineered thing, that you don't need in a dynamically typed language like ruby.

just use strings!

you could then use that like:

class Review < ActiveRecord::Base
  validates_inclusion_of :status, :in => ["UNREVIEWED", "REVIEWED", "FLAGGED"]
  def status
    read_attribute(:status)
  end
  def status= (value)
    write_attribute(:status, value)
  end
end

Upvotes: 6

Related Questions