首页 > 解决方案 > Rails 模型根据用户标志调用不同的类

问题描述

学习 Rails,我刚刚遇到了一些灯可能会有所帮助的东西。

我有类,A,B,C,它们都做一个动作。

我有一个消息模型,我想在保存时根据用户输出调用其中一个类。

我现在正在努力为模型以及类编写代码的更 ruby​​ist 方式,这取决于模型方法。

选项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 知之甚少,我正在寻找如何编写最干净和可重用的代码。提前致谢。

标签: ruby-on-railsruby

解决方案


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

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

其次,由于您在Message保存模型后对其进行操作,因此您可以利用after_save回调并将操作保留在Message模型本身内。这使得它与消息模型更加相关,并且总体上更具可读性。

最后,如果您的操作出现错误,您将需要某种保证,即您的保存/交易会回滚after_save。离开这个答案,您可以通过在 `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

推荐阅读