0

I'm trying to think of a best solution for following scenario. I've a model called an 'Article' with an integer field called 'status'. I want to provide class level array of statuses as shown below,

 class Article < ActiveRecord::Base
 STATUSES = %w(in_draft published canceled)

 validates :status, presence: true
 validates_inclusion_of :status, :in => STATUSES

  def status_name
    STATUSES[status]
  end

  # Status Finders

  def self.all_in_draft
    where(:status => "in_draft")
  end

  def self.all_published
    where(:status => "published")
  end

  def self.all_canceled
    where(:status => "canceled")
  end

  # Status Accessors

  def in_draft?
    status == "in_draft"
  end

  def published?
    status == "published"
  end

  def canceled?
    status == "canceled"
  end

end

So my question is if this is the best way to achieve without having a model to store statuses? And secondly how to use these methods in ArticlesController and corresponding views? I'm struggling to understand the use of these methods. To be specific, how to do following?

article = Article.new  
article.status = ???? 
article.save!  

or  
<% if article.in_draft? %>

<% end %>

I greatly appreciate any sample code example.  I'm using rails 4.0.0 (not 4.1.0 which has enum support). 
4
  • Sure, what's the issue with it? How to do what? The only other thing I might add are some constants with the allowed statuses, or just create methods that set them the same way you already have status indicator methods. Commented Nov 19, 2014 at 17:05
  • How to set article.status indicated by question marks. Can I do following, article.status = Article::STATUSES[0] or article.status = Article::STATUSES['in_draft']? I'm not sure about this particular syntax. Commented Nov 19, 2014 at 17:12
  • Have you considered using a state machine? Commented Nov 19, 2014 at 17:21
  • I thought doing it myself will help me learn Rails framework so I avoided to use gems like simple_enum and such. If you see the answer from Sergio, it's not lot of code which gets the job done. Commented Nov 19, 2014 at 17:29

1 Answer 1

2

You could define all the methods using define_method, and use a hash instead of an array:

STATUSES = {:in_draft => 1, :published => 2, :cancelled => 3}

# Use the values of the hash, to validate inclusion
validates_inclusion_of :status, :in => STATUSES.values

STATUSES.each do |method, val|
  define_method("all_#{method)") do
    where(:status => method.to_s)
  end

  define_method("#{method}?") do
    self.status == val
  end
end

In that way, you can add statuses in the future without needing to create the methods manually. Then you can do something like:

article = Article.new  
article.status = Article::STATUSES[:published]
...

article.published? # => true
Sign up to request clarification or add additional context in comments.

7 Comments

This exactly is what I needed to know.
@Atarang Notice that in this case, the status field would be an integer, but you could use whatever you want.
Yes I do have status as a integer field. Thanks.
How do you validate the status while saving an object? I'm getting ActiveRecord::RecordInvalid: Validation failed: Status is not included in the list, error.
In this case, you should pass the values of the hash. validates_inclusion_of :status, :in => STATUSES.values (answer updated).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.