可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I thought it would be good to populate a status field in an activeRecord table using constants. However, when it comes to checking if this status has a particular status, I'm having trouble.
If I do the following,
e = Mytable.new
e.status = :cancelled
e.save
then refind the record and try and compare my status to the symbol, the check fails. I have some output from the console to show this.
irb(main):060:0> e.status.eql?("cancelled")
=> true
irb(main):061:0> e.status.eql?(:cancelled)
=> false
irb(main):062:0> e.status == :cancelled
=> false
irb(main):063:0> e.status == "cancelled"
=> true
irb(main):064:0> e.status == :cancelled.to_s
=> true
Is there a better way of holding a status in a record? Is there a way of testing if a current field value is equal to the :symbol without converting the :symbol to a string? I'm thinking there may be an operator I'm not aware of.
回答1:
At the request of ecoologic, here is my comment as an answer:
ecoologic has a good solution for you, but I would recommend steering away from this and making a class with constants in it. That you can do things like e.status = Statuses::CANCELLED. And internally that could be a string and it doesn't matter. You're still using constants, and it will error out if that constant doesn't exist, and it's cleaner that way.
回答2:
With Rails 4.1.0, you'd probably want to use Active Record enums.
To quote the official release notes:
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
conversation.archived!
conversation.active? # => false
conversation.status # => "archived"
Conversation.archived # => Relation for all archived Conversations
Conversation.statuses # => { "active" => 0, "archived" => 1 }
回答3:
This is kind of late, but may help someone else.
If you have classes with different statuses, you might consider an approach using constants along with scopes like this:
class Account < ActiveRecord::Base
#-------------------------------------------------------------------------------
# Configuration
#-------------------------------------------------------------------------------
# STATUS is used to denote what state the account is in.
STATUS = { :active => 1, :suspended => 2, :closed => 3 }
# Scopes
scope :active, where(:status => Account::STATUS[:active])
scope :suspended, where(:status => Account::STATUS[:suspended])
scope :closed, where(:status => Account::STATUS[:closed])
...
end
Then you can easily find records based on status, like so:
# get all active accounts
active_accounts = Consumer.active.all
# get 50 suspended accounts
suspended_accounts = Consumer.suspended.limit(50)
# get accounts that are closed and [some search criteria]
closed_accounts = Consumer.closed.where([some search criteria])
Hope this helps someone else!
EDIT:
If you're more into using gems, the simple_enum gem looks like an excellent alternative.
回答4:
If I remember well symbols in ActiveRecord are stored in yaml format, some kind of conversion must be done because there is no such thing as a symbol in relational db (which I'm aware of, at least). When you read it it's then a string which will not match your symbol and not even the string of the symbol, in fact it should be something like:
:x # => "--- :x\n"
I think this plugin can solve your problem, but I haven't used it honestly.
https://github.com/zargony/activerecord_symbolize
* EDIT *
I leave the above because I remember that was the situation I had and I can be corrected if I'm wrong, nonetheless I'm trying this right now and the stored value (Rails 3.1.3) is a simple string with the value of the symbol, so the following should be enough.
class Example < ActiveRecord::Base
def aaa
super.to_sym
end
def aaa=(value)
super(value.to_sym)
aaa
end
end
This of course will force the value to always be a symbol
** EDIT AFTER AGES **
I think it's fine in this situation as it's clear that in the db it's a string and the logic is simple, but I strongly discourage overriding db attribute methods to add more complex logic.
回答5:
As of Rails 4.1, Active Record now supports enums
From the release notes:
2.5 Active Record enums
Declare an enum attribute where the values map to integers in the
database, but can be queried by name.
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
conversation.archived!
conversation.active? # => false
conversation.status # => "archived"
Conversation.archived # => Relation for all archived Conversations
Conversation.statuses # => { "active" => 0, "archived" => 1 }
Additional documentation here: http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html
回答6:
Also you can overwrite the reader
method:
def status
read_attribute(:status).to_sym
end
回答7:
From Programming Ruby 1.9, regarding the == operator in the Symbol class (p. 729):
Returns true only if sym and obj are symbols with the same object_id.
Whatever you have stored in the DB will always have different object_id than the fixed object_id of the symbol (a pointer to a string literal, in this case).