2

I have a model in Rails that has an enum attribute "status". I want to have a concept of public and private statuses, like so:

class Something < ActiveRecord::Base
  @public_statuses = [:open, :closed, :current]
  @private_statuses = [:deleted]

  enum status: @public_statuses + @private_statuses
end

So that I can do the following in a view:

<select>

<% Something.public_statuses.each do |status| %>
    <option value="<%= status %>"><%= status.humanize %></option>
<% end %>

</select>

This way, I don't expose the private statuses to the end user.

Unfortunately I don't understand Ruby classes very well and just cannot get this to work regardless of whether I do @public_statuses, @@public_statuses, public_statuses=[...] etc. I'm familiar with Java and other OO languages but just don't get what to do in Ruby here.

What is the right way to do this?

2 Answers 2

2

You can treat your variables like Class methods and define private the ones you don't want to be accessed (that's not absolutely true in Ruby). Like:

    def self.public_statuses
        [:open, :closed, :current]
    end

    def self.private_statuses
        [:deleted]
    end

    private_class_method :private_statuses

But if you really gonna go with variables, in this case, constants, it's very similar:

PUBLIC_STATUSES = [:open, :closed, :current]
PRIVATE_STATUSES = [:deleted]
private_constant :PRIVATE_STATUSES

So, you can use it:

puts Something::PUBLIC_STATUSES
=> [:open, :closed, :current]
puts Something::PRIVATE_STATUSES
NameError: private constant Client::PRIVATE_STATUSES referenced
Sign up to request clarification or add additional context in comments.

Comments

1

What you've done is defined two instance variables not class variables so accessing them as Foo.instance_variable is not going to work. You need an instance of Foo to access them. So you'd replace your view each block with:

<% Something.new.public_statuses.each do |status| %>
  <option value="<%= status %>"><%= status.humanize %></option>
<% end %>

Here you instantiate Something then call pubic_statuses.each on that instance. So this will work for you.

However, if you'd like to use them as class variables then you need to add a class accessor method in your model class:

class Something < ActiveRecord::Base
  @@public_statuses = [:open, :closed, :current]
  @private_statuses = [:deleted]

  enum status: public_statuses + @private_statuses


  def self.public_statuses
    @@public_statuses
  end
end

As you are using Rails, you could also define a cattr_reader :public_statuses instead of the getter method defined in the code above. With these options of adding class method, you needn't change your view.

Another option is to make them constants:

class Something < ActiveRecord::Base
  PUBLIC_STATUSES = [:open, :closed, :current]
  PRIVATE_STATUSES = [:deleted]

  enum status: PUBLIC_STATUSES + PRIVATE_STATUSES
end

Then in your view:

<% Something::PUBLIC_STATUSES.each do |status| %>

Comments

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.