Ruby样式问题:使用不同的可能值存储哈希常量

时间:2010-01-21 04:37:39

标签: ruby-on-rails ruby

这更像是一个风格问题,我想知道别人做了什么。

假设我的数据库中有一个名为“status”的字段用于博客帖子。我希望它有几个可能的值,例如“草稿”,“等待审核”和“发布”,仅作为示例。

显然,我们不希望每次都在这些神奇的值中“硬编码”,这不会是干的。

所以我有时会这样做:

class Post
  STATUS = {
    :draft => "draft",
    :awaiting_review => "awaiting review",
    :posted => "posted"
  }

  ...

end

然后我可以编写稍后引用它的代码STATUS[:draft]Post::STATUS[:draft]等。

这样可行,但有一些我不喜欢的事情。

  1. 如果你有一个拼写错误并且调用类似STATUS[:something_that_does_not_exist]的东西它不会抛出错误,它只会返回nil,并且可能会在你发现错误之前在数据库中设置它等
  2. if some_var == Post::STATUS[:draft] ...
  3. 等内容看起来不干净或红宝石

    我不知道,有些东西告诉我有更好的方法,但只是想看看其他人做了什么。谢谢!

6 个答案:

答案 0 :(得分:8)

您可以使用Hash.new并为其指定一个块参数,如果密钥未知,则会调用该参数。

class Post
  STATUS = Hash.new{ |hash, key| raise( "Key #{ key } is unknown" )}.update(
    :draft => "draft",
    :awaiting_review => "awaiting review",
   :posted => "posted" )
end

它有点乱,但它有效。

irb(main):007:0> Post::STATUS[ :draft ]
=> "draft"
irb(main):008:0> Post::STATUS[ :bogus ]
RuntimeError: Key bogus is unknown
    from (irb):2
    from (irb):8:in `call'
    from (irb):8:in `default'
    from (irb):8:in `[]'
    from (irb):8

答案 1 :(得分:6)

这是一个常见问题。考虑这样的事情:

class Post < ActiveRecord::Base
  validates_inclusion_of :status, :in => [:draft, :awaiting_review, :posted]
  def status
    read_attribute(:status).to_sym
  end
  def status= (value)
    write_attribute(:status, value.to_s)
  end
end

您可以使用名为symbolize的第三方ActiveRecord插件来简化此操作:

class Post < ActiveRecord::Base
  symbolize :status
end

答案 2 :(得分:2)

您可以使用类方法在缺少的键上引发异常:

class Post
  def self.status(key)
    statuses = {
      :draft => "draft",
      :awaiting_review => "awaiting review",
      :posted => "posted"
    }
    raise StatusError unless statuses.has_key?(key)
    statuses[key]
  end
end

class StatusError < StandardError; end

潜在地,您还可以使用此方法将状态存储为数据库中的整数,方法是将字符串更改为整数(在散列中),转换列类型以及添加getter和setter。

答案 3 :(得分:1)

我这样做:

class Post
  DRAFT = "draft"
  AWAITING_REPLY = "awaiting reply"
  POSTED = "posted"
  STATUSES = [DRAFT, AWAITING_REPLY, POSTED]

  validates_inclusion_of :status, :in => STATUSES
  ...
end

这样一来,如果拼错一个就会出错。如果我有多组常量,我可能会像DRAFT_STATUS那样区分。

答案 4 :(得分:1)

看一下attribute_mapper gem。

有一个related article显示了如何以声明方式处理问题,就像这样(借鉴文章):

class Post < ActiveRecord::Base
  include AttributeMapper

  map_attribute :status, :to => {
    :draft => 1,
    :reviewed => 2,
    :published => 3
  }
end

......看起来很时尚。

答案 5 :(得分:0)

即使这是一篇旧帖子,但对于遇到这种情况的人来说,你可以在Hash上使用 fetch 方法,这会引发错误(如果没有传递默认值)找不到给定的密钥。

STATUS = {
  :draft => "draft",
  :awaiting_review => "awaiting review",
  :posted => "posted"
}

STATUS.fetch(:draft) #=> "draft"
STATUS.fetch(:invalid_key) #=> KeyError: key not found: invalid_key