3

Rails 新手,遇到一些关于最佳实践错误处理的问题。

我在 /lib 文件夹中的帮助程序中有以下代码,用于将优惠券应用于某些定价。

正如您从下面的代码中看到的那样,我正在尝试几种不同的方法来返回任何错误。具体来说,model.errors.add 和 flash[:error]。

一旦应用了有效的优惠券,下面的代码应该会返回一个新价格。我不确定在发生错误时要返回什么?我不能像下面那样返回 false,因为这个方法实际上应该返回一个数值,而不是一个布尔值。从 lib/helper 向视图冒泡并停止执行的最佳方法是什么?

def calc_coupon transaction_price, discount_code, current_tenant
   coupon = Coupon.where(code: discount_code, tenant_id: current_tenant.id).first

   if coupon.nil?
     current_tenant.errors.add :base, "No such coupon exists"
     return false
   else
     if coupon.maxed? || coupon.expired?
       flash[:error] = "This coupon is no longer valid."
       return false
     else
      transaction_price = coupon.calculate(transaction_price)
     end
   end
   return transaction_price
end
4

1 回答 1

3

我认为您走在正确的轨道上,但您正在苦苦挣扎,因为您将视图逻辑与业务逻辑混合在一起。忘记辅助类并将功能添加到您的模型中。

通过将验证添加到模型而不是辅助类并将错误添加到基础您的控制器操作将能够在尝试以正常方式保存/创建记录时处理错误,从而“冒泡错误”

例如

    class MyModel < ActiveRecord ...
      validate :validate_coupon
      protected
        def validate_coupon
#          add your validations here - call your boolean check which adds errors to the model
#          if validations are ok then calculate your transaction price here
        end
    end

如果您将检查作为模型上的公共方法,则可以从任何地方调用它们,即在视图中,因此模型上的公共 transaction_price 方法首先检查错误并设置价格,可以在这样的视图中使用

<%= @some_object.transaction_price %>

正如我之前所说,因为验证是在模型中添加的,那么您的控制器操作只需要检查调用的值以保存并在返回 false 时正常闪烁错误,因此根本不需要在控制器操作中执行任何自定义操作. 只是默认的脚手架行为。

您可以使用大量的验证选项。看看 http://guides.rubyonrails.org/active_record_validations_callbacks.html

希望这是有道理的。

更新回应评论

1)你有很多地方。这只是一个建议,您可以计算交易价格作为验证的一部分。如果这不符合您的要求,那很好。我只是想给你最适合你的问题。问题是你应该有两个单独的实例方法,然后你可以从任何你想要的地方调用它们,这使得实验和移动东西变得更加简单。使用您建议的解决方案很好:)

2) 仔细查看脚手架控制器和视图。您将使用 if 语句看到保存/更新如何失败(如果您在验证过程中添加错误,它们将自动失败),您将看到 _form 部分如何显示错误

这是处理我正在开发的应用程序中的引擎的表单示例。

<%= form_for(@engine) do |f| %>
  <% if @engine.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@engine.errors.count, "error") %> prohibited this engine from being saved:</h2>

      <ul>
      <% @engine.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

基本上,它正在检查错误并遍历它们(如果存在)以显示它们。

要处理 flash 消息,您应该使用应用程序主布局以下代码将在名为“info”的 div 中显示 flash 消息,您可以根据需要设置样式

  <% unless flash.empty? %>
    <div class="info">
    <%- flash.each do |name, msg| -%>
      <%= content_tag :div, msg, :id => "flash_#{name}" %>
    <%- end -%>
    </div>
  <% end %>

通过将它放在 view/layouts 文件夹中 application.html.erb 的 main yield 之前,您可以确保无论呈现什么页面,flash 消息都将始终显示给用户

当然,如果您正在显示 Flash 消息,那么也许您不需要显示错误。有时两者都是正确的做法,有时只是其中一个或另一个很好。这取决于您根据具体情况决定。

主要的是,HTTP POST 或 HTTP PUT(创建或更新操作)的接收操作会处理您认为合适的使用或不使用闪存保存的失败,例如创建操作

  def create
    @engine = Engine.new(params[:engine])

    respond_to do |format|
      if @engine.save
        format.html { redirect_to @engine, notice: 'Engine was successfully created.' }
        format.json { render json: @engine, status: :created, location: @engine }
      else
        flash.now[:notice] = "Failed to save!" # flash.now because we are rendering. flash[:notice] if using a redirect
        format.html { render action: "new" }
        format.json { render json: @engine.errors, status: :unprocessable_entity }
      end
    end
  end

希望这能说明问题

更新 2 忘了提到 flash 只是一个哈希并使用会话 cookie,因此您可以设置任何您喜欢的键,例如

flash.now[:fred]

如果您希望通过添加代码来处理特定视图中的 :fred 键(或您选择的名称)的代码与应用程序布局提供的样式不同,这将使您能够在特定视图中设置和处理特定 fred 通知的样式

于 2013-02-04T08:42:16.277 回答