Как Легко И Просто Реализовать Опции В Ruby On Rails

Пролог

Как легко и просто реализовать опции в Ruby on Rails

Наверное всем веб (а может и не только веб) разработчикам рано или поздно приходилось сталкиваться с такой проблемой как параметры пользователя .

То, что я имею в виду, легче всего показать на картинке.

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

Обычно таких вариантов не слишком много, около 2-10. Обычно они указываются при регистрации и/или редактировании профиля.

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

Я расскажу вам, как решить эту проблему и как я решил ее в рамках Ruby on Rails. Варианты реализации Вообще, я обычно задаю этот вопрос на собеседованиях; Я слышал самые разные ответы.

Один из них – использовать два стола : один для опций, другой для подключения пользовательских опций (есть запись, значит есть галочка в форме).

Преимущества:

  • Список редактируемых опций
Недостатки:
  • соедините таблицы, чтобы узнать, установлена ли эта опция
  • нетривиальный код во представлении
Позвольте мне объяснить второй момент: я хотел бы иметь что-то похожее на:
 
   <%= f.check_box :send_mail_on_friend_request %>
 
Конечно, это можно реализовать, но код в модели не будет тривиальным.

Другой вариант — использовать для опций один столбец — текст, в котором опции хранятся в сериализованном виде.

 
 class User
   serialize :options, Hash
 end
 
В этом примере непонятно, какие опции могут быть у пользователя, давайте улучшим:
 
 class User
   OPTION_1 = :key_1
   OPTION_2 = :key_2
 
   serialize :options, Hash
 
   def option_1=(value)
     self.options[:key_1] = value
   end
   def option_2=(value)
     self.options[:key_2] = value
   end
   def option_1
     self.options[:key_1]
   end
   def option_2
     self.options[:key_2]
   end
 end
 
Уже понятнее, но методов слишком много: а если вариантов 20? Define_method прекрасно решает эту проблему (пример его использования будет ниже).

Но все же давайте подумаем: ведь опция - это всего лишь чекбокс, совсем чуть-чуть - либо установлен, либо нет (да, прямо как женская интуиция :), зачем сериализовать, десериализовать? Может быть, можно сделать проще? Моя реализация Давайте подумаем, сколько таких вариантов может быть? Думаю максимум 10, ну 20, больше пользователь не заполнит. Что, если вы сохраните опционы как обычный номер? Неплохая идея, просто надо запомнить, как работать с битами:

 
 class User < ActiveRecord::Base
   NOTIFY_ON_MAIL           = 0b0001
   NOTIFY_ON_WALL_POST      = 0b0010
   NOTIFY_ON_FRIEND_REQUEST = 0b0100
   NOTIFY_ON_COMMENT_ANSWER = 0b1000
 
   def notify_on_mail
     self.options & NOTIFY_ON_MAIL != 0
   end
 
   def notify_on_mail=(value)
     if value != "0"
       self.options |= const
     else
       self.options &= ~const
     end
   end
 end
 
Так хорошо.

Теперь вы можете обобщить любое количество вариантов:

 
 class User < ActiveRecord::Base
   NOTIFY_ON_MAIL           = 0b0001
   NOTIFY_ON_WALL_POST      = 0b0010
   NOTIFY_ON_FRIEND_REQUEST = 0b0100
   NOTIFY_ON_COMMENT_ANSWER = 0b1000
 
   [NOTIFY_ON_MAIL, NOTIFY_ON_WALL_POST, NOTIFY_ON_FRIEND_REQUEST, NOTIFY_ON_COMMENT_ANSWER].

each do |notifer| const = self.const_get(notifer) define_method notifer.downcase.to_sym do (mail_options & const) != 0 end define_method "#{notifer.downcase}=".

to_sym do |flag| if flag != "0" self.mail_options |= const else self.mail_options &= ~const end end end end

Теперь, чтобы добавить новую опцию, вам нужно изменить всего две строки.

Хм, а что если свести все к одному? Результат:

 
 class User < ActiveRecord::Base
   NOTIFY_ON_MAIL           = 0b0001
   NOTIFY_ON_WALL_POST      = 0b0010
   NOTIFY_ON_FRIEND_REQUEST = 0b0100
   NOTIFY_ON_COMMENT_ANSWER = 0b1000
 
   constants.grep(/^NOTIFY_/).

each do |notifer| const = self.const_get(notifer) define_method notifer.downcase.to_sym do (mail_options & const) != 0 end define_method "#{notifer.downcase}=".

to_sym do |flag| if flag != "0" self.mail_options |= const else self.mail_options &= ~const end end end end

Поля для представлений также можно генерировать в цикле.

Надеюсь, эта небольшая заметка натолкнула вас на некоторые идеи :) Всего вам доброго! Теги: #ruby #ruby на рельсах #опции #ruby на рельсах

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.