Rails模型根据用户标志调用不同的类

时间:2018-08-27 01:09:42

标签: ruby-on-rails ruby

学习Rails时,我只是遇到一些可能会有帮助的东西。

我有A,B,C类,它们都可以执行操作。

我有一个Message模型,当我要保存时,我想根据用户输出来调用这些类之一。

我现在正在努力寻找一种更红宝石的方式来编写模型代码以及类,具体取决于模型方法。

选项 A

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

选项 B : 将A,B,C从类移动到用户标记(称为函数)的模块的实例方法

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

由于我对Rails的了解很少,因此我正在寻找如何编写最干净和可重用的代码。预先感谢。

2 个答案:

答案 0 :(得分:1)

与许多设计决策一样,您可以采用多种方法,每种方法都是“正确的”,主要是基于偏好。这就是我的做法。

首先,我将确保@user.flags仅可以采用某些值,因为它的值被用于决定其他动作。在Ruby中,由于给定符号是不可变的,因此处理这些值的普遍接受的方式也是符号。

第二,由于您在保存Message模型后要进行某些操作,因此可以使用after_save callback并将操作保留在Message模型本身中。这使其与消息模型的联系更加紧密,并且通常更具可读性。

最后,如果after_save操作出错,您将需要某种保证,即保存/事务会回滚。从this answer开始,您可以通过在`after_save _

中引发错误来做到这一点

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

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

答案 1 :(得分:0)

这是一个有趣的问题,正如@ user2490003所说,没有写/错误的方法可以做到这一点。

您的方法将根据A,B和C类的实现方式及其表示形式以及方法在每个类中的作用而改变。

让我们举个例子,一个名为talk的方法和两个类ManWomen

因此,您可以将其实现为

单个类方法

class Man
  def talk
    # talk like an adult
  end
end

class Women
  def talk
    # talk like an adult
  end
end

但是,正如您所看到的,talkMan的{​​{1}}方法是相同的,并且您还可以看到它们通常共享相同的功能和属性。因此,创建一个名为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类(不是Human类)中执行 Baby.new.talk # => baby talk 方法

将方法提取到模块中

让我们得到一个talk类,并假设它也有一个Parrot方法,并且它与talk相同。

现在存在的问题是我们无法从Human talk继承Parrot类,但是我们仍然希望在Human方法中包含代码。在这种情况下,您可以使用模块,因此可以

talk

正如我所解释(或至少尝试过..)那样,您的实现将取决于类A,B,C和Message类之间的关系。

在这种情况下,我个人要做的是拿笔和纸,尝试映射这些对象,而无需考虑如何以红宝石或任何语言实现。一旦了解了它们如何相互连接,就很容易找到实现它的语法