Ruby Style Вопрос: сохранение хеш-константы с различными возможными значениями - PullRequest
6 голосов
/ 21 января 2010

Это больше вопрос стиля, мне интересно, что делают другие люди.

Допустим, в моей базе данных есть поле с названием "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], она не выдаст ошибку, она просто вернет ноль и может в конечном итоге установить ее в базе данных и т. Д., Прежде чем вы заметите ошибку
  2. Писать что-то вроде if some_var == Post::STATUS[:draft] ...

Я не знаю, что-то говорит мне, что есть лучший способ, но просто хотел посмотреть, что делают другие люди. Спасибо!

Ответы [ 6 ]

8 голосов
/ 21 января 2010

Вы можете использовать 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
6 голосов
/ 21 января 2010

Это распространенная проблема. Рассмотрим что-то вроде этого:

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

Вы можете использовать сторонний плагин ActiveRecord с именем , символизирующий , чтобы сделать это еще проще:

class Post < ActiveRecord::Base
  symbolize :status
end
2 голосов
/ 21 января 2010

Вы можете использовать метод класса, чтобы вызвать исключение для отсутствующего ключа:

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

Потенциально вы также можете использовать этот метод для сохранения статусов как целых чисел в базе данных, изменив ваши строки на целые (в хэше), преобразовав типы столбцов и добавив геттер и сеттер.

1 голос
/ 21 января 2010

Посмотрите на attribute_mapper gem.

Есть связанная статья , которая показывает, как вы можете решить проблему декларативно, как это (заимствовано из статьи):

class Post < ActiveRecord::Base
  include AttributeMapper

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

... выглядит довольно стильно.

1 голос
/ 21 января 2010

Я делаю это так:

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

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

Таким образом, вы получаете ошибки, если ошиблись. Если у меня несколько наборов констант, я могу сделать что-то вроде DRAFT_STATUS, чтобы различать.

0 голосов
/ 07 ноября 2015

Несмотря на то, что это старый пост, для кого-то, кто наткнулся на это, вы можете использовать метод fetch в Hash, который вызывает ошибку (если по умолчанию не передается), если данный ключ не найден.

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

STATUS.fetch(:draft) #=> "draft"
STATUS.fetch(:invalid_key) #=> KeyError: key not found: invalid_key
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...