Модель Rails вызывает другой класс в зависимости от флага пользователя - PullRequest
0 голосов
/ 27 августа 2018

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

У меня есть классы A, B, C, которые все выполняют действие.

И у меня есть Сообщениемодель, и я хотел бы, когда я собираюсь сохранить, вызвать один из этих классов, основанный на выводе пользователя.

Я сейчас борюсь за то, что было бы более рубиновым способом написать код длямодель, но и классы, в зависимости от метода модели.

Опция A :

case @user.flag:
  when 'alpha'
    A.new(message)
  when 'beta'
    B.new(message)
  when 'gamma'
    C.new(message)

Опция B : Перемещение A, B,C от классов к пользовательскому флагу Методы экземпляра модуля с именем Functions

Functions.send(@user.flag.to_sym,message)

Поскольку у меня мало знаний о Rails, я ищу, как написать наиболее чистый и многократно используемый код.Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Это интересный вопрос, так как @ user2490003 сказал, что нет записи / неправильного способа сделать это.

Ваш подход будет меняться в зависимости от того, как реализуются ваши классы A, B и C и их представление, а также от того, что ваш метод делает в каждом классе.

Давайте рассмотрим пример, метод с именем talk и два класса Man, Women.

Итак, вы можете реализовать это как

Отдельные методы класса

class Man
  def talk
    # talk like an adult
  end
end

class Women
  def talk
    # talk like an adult
  end
end

Однако, как вы можете видеть, этот метод talk одинаков как для Man, так и для *Women и вы также можете видеть, что они обычно имеют одинаковые функции и атрибуты.Итак, создайте базовый класс с именем Human и переместите туда метод talk

Как метод базового класса

class Human
  def talk
    # talk like an adult
  end
end

class Man < Human

end

class Woman < Human

end

Теперь давайте возьмем пример ребенка и скажем ребенок talk отличается от мужчины и женщины, хотя ребенок все еще наследует от Human.В таких случаях вы можете сделать

class Baby < Human
  def talk
    # baby talk
  end
end

. То, что здесь происходит, Ребенок наследует от Человека, но когда вы звоните

Baby.new.talk # => baby talk

, он выполняет talk метод в классе Baby (не в классе Human)

Извлечение метода в модуль

Давайте возьмем класс Parrot и предположим, что он также имеет метод talk,а также он такой же как Human talk.

Теперь проблема в том, что мы не можем унаследовать Parrot класс от Human, но мы все еще хотим иметь код в методе talk.В таком случае вы можете использовать модуль, то есть вы можете сделать

module Talkable
  def talk
    # talk like an adult
  end
end

class Human
   include Talkable
end

class Parrot
  include Talkable
end

. Как, как я объяснил (или хотя бы попытался), ваша реализация будет зависеть от того, как ваш класс A,Классы B, C и Message связаны между собой.

Что я лично делаю в таких ситуациях, так это достаю ручку и бумагу и пытаюсь нанести на карту эти объекты, не задумываясь о том, как их реализовать на рубине или любом другом языке.Как только вы поймете, как все они держатся вместе, легко найти синтаксис для его реализации

0 голосов
/ 27 августа 2018

Как и во многих дизайнерских решениях, существует множество подходов, каждый из которых может быть «правильным» в основном на основе предпочтений.Вот как я это сделаю.

Во-первых, я бы удостоверился, что @user.flags может принимать только определенные значения, так как его значение используется для решения других действий.В Ruby общепринятым способом обработки этих значений также являются символы, поскольку данный символ является неизменным.

Во-вторых, поскольку вы что-то делаете с моделью Message после ее сохранения, вы можете использовать обратный вызов after_save и сохранить действие внутри самой модели Message.Это делает его более привязанным к модели сообщений и делает его более читабельным в целом.

Наконец, вам понадобится какая-то гарантия того, что ваша транзакция сохранения / отката будет откатываться, если в вашем действии after_save произошла ошибка,Выключив этот ответ , вы можете сделать это, вызвав ошибку в `after_save_

In app/models/user.rb

class User < ActiveRecord::Base
  FLAGS = %w[alpha beta gamma].freeze

  # Ensuure that `flag` field can only take on certain pre-defined values
  # Also validate that flag can never be nil. You may need to change that
  # as needed for your application
  validates :flag, presence: true, inclusion: FLAGS

  def flag
    # This method isn't 100% necessary but I like to personally follow 
    # the pracitce of returning symbols for enumerated values
    super(flag).try(:to_sym)
  end
end

In app/models/message.rb

class Message < ActiveRecord::Base
  after_save :post_process_message

  private

  # I'd recommend a better name for this method based on what you're
  # specifically doing
  def post_process_message
    # Notice the more descriptive method name
    # Also no need to pass `message` as a param since it's now located
    # inside this model. You could also move it to a separate class/service
    # as needed but don't over-optimize until you need to
    send("handle_post_process_for_flag_#{user.flag}")
  rescue StandardError => e
    # Something went wrong, rollback!
    # It isn't "great practice" to rescue all errors so you may want to replace
    # this with whatever errrors you excpect your methods to throw. But if you
    # need to, it's fine to be conservative and rescue all on a case-by-case
    # basis
    raise ActiveRecord::RecordInvalid.new(self)
  end

  def handle_post_process_for_flag_alpha
  end

  def handle_post_process_for_flag_beta
  end

  def handle_post_process_for_flag_gamma
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...