【问题标题】:How to use the "number_to_currency" helper method in the model rather than view?如何在模型而不是视图中使用“number_to_currency”辅助方法?
【发布时间】:2011-03-03 04:39:25
【问题描述】:

我想在我的模型中使用to_dollar 方法,如下所示:

module JobsHelper      
  def to_dollar(amount)
    if amount < 0
      number_to_currency(amount.abs, :precision => 0, :format => "-%u%n")
    else
      number_to_currency(amount, :precision => 0)
    end
  end      
end

class Job < ActiveRecord::Base
  include JobsHelper
  def details
    return "Only " + to_dollar(part_amount_received) + 
           " out of " + to_dollar(price) + " received."
  end
end

很遗憾,这里无法识别number_to_currency 方法:

#<0x311eb00>

    标签: ruby-on-rails


    【解决方案1】:

    我同意你们所有人的观点,这可能会破坏 MVC 模式,但总是有理由打破模式,在我的情况下,我需要这些 货币格式化方法在一个模板过滤器(在我的例子中是液体)。

    最后我发现我可以使用以下方法访问这些货币格式化方法

    ActionController::Base.helpers.number_to_currency
    

    【讨论】:

    • 这很好,尽管有一种更简洁的方法。见http://railscasts.com/episodes/132-helpers-outside-views
    • 是的 RailsCasts 中的评论跟踪:在 2013 年的 Rails 3 中,在 Controller 中使用 View 助手的方式类似于 view_context.number_to_currency(amount)
    • 您是否考虑过使用“金钱”宝石?由于money对象提供了format()方法,你可以在模型、控制器或视图中调用它。
    【解决方案2】:

    它不可用,因为它在模型中的使用(通常)违反了 MVC(在您的情况下似乎确实如此)。您正在获取数据并对其进行操作以进行演示。根据定义,这属于视图,而不是模型。

    这里有一些解决方案:

    • 使用演示者或视图模型对象在模型和视图之间进行调解。这几乎肯定比其他解决方案需要更多的初始工作,但几乎总是一个更好的设计。在演示者/视图模型中使用帮助器不会违反 MVC,因为它们位于视图层中,取代了传统的自定义 Rails 帮助器和逻辑填充视图。

    • JobsHelper 中显式地使用include ActionView::Helpers::NumberHelper,而不是依赖Rails 为您神奇地加载它。这仍然不是很好,因为您不应该从模型中访问帮助器。

    • 违反 MVC 和SRP。请参阅fguillen’s answer 了解如何执行此操作。我不会在这里附和它,因为我不同意它。不过,更重要的是,我是否不同意使用 Sam’s answer 中的表示方法污染您的模型。

    如果你认为“但我真的需要这个来在我的模型中编写我的 to_csvto_pdf 方法!”,那么你的整个前提都是错误的——毕竟,你没有 to_html 方法,你?然而,您的对象经常呈现为 HTML。考虑创建一个新类来生成输出,而不是让数据模型知道 CSV 是什么 (because it shouldn’t)。

    至于在模型中使用帮助器来处理 ActiveModel 验证错误,我很抱歉,但是 ActiveModel/Rails 通过强制在数据层中实现错误消息而不是返回语义 把我们都搞砸了以后会意识到错误的想法——叹息。你可以解决这个问题,但这基本上意味着不再使用 ActiveModel::Errors。我做了,效果很好。

    顺便说一句,这是一种在演示者/视图模型中包含助手而不污染其方法集的有用方法(因为能够这样做,例如 MyPresenterOrViewModel.new.link_to(...) 没有意义):

    class MyPresenterOrViewModel
      def some_field
        helper.number_to_currency(amount, :precision => 0)
      end
    
      private
    
      def helper
        @helper ||= Class.new do
          include ActionView::Helpers::NumberHelper
        end.new
      end
    end
    

    【讨论】:

    • 我通常遵循这个规则,但是当我需要一个视图助手来格式化模型中定义的验证错误消息时打破它。
    • 这是个好建议,但答案很糟糕,因为它不能解决问题。
    • 在某些情况下,这不是一个很好的答案,例如,现在我正在构建一个 csv 报告,并且需要在一个永远不会看到视图的类的 to_csv 方法中使用类似的东西.仅仅萌芽编程理想并不总是有帮助的。
    • 是的,nitecoder 说的。我遇到了同样的问题。我正在生成 PDF 报告,只是想很好地格式化电话号码。
    • @maurice 从“仅此而已”到臃肿模型,这是一个滑坡。 Rails 中的应用程序助手是一个垃圾抽屉,演示者/视图模型更易于管理。我不认为为报告创建 data 和生成该数据的 (html|pdf|csv|etc.) view 作为一项单一职责例如,为一个人和一个 HTML 人物显示页面做。
    【解决方案3】:

    我知道这个线程很老了,但是有人可以在 Rails 4+ 中寻找这个问题的解决方案。开发者添加了 ActiveSupport::NumberHelper,可以在不访问视图相关模块/类的情况下使用:

    ActiveSupport::NumberHelper.number_to_currency(amount, precision: 0)
    

    【讨论】:

    • 当我想在 Rails 控制台中试验 number_to_percentage 的行为时,这种方法对我有用。谢谢!
    【解决方案4】:

    您还需要包含 ActionView::Helpers::NumberHelper

    class Job < ActiveRecord::Base
      include ActionView::Helpers::NumberHelper
      include JobsHelper
      def details
        return "Only " + to_dollar(part_amount_received) + 
               " out of " + to_dollar(price) + " received."
      end
    end
    

    【讨论】:

    • 谢谢,看起来不错,但我必须同意其他人说我违反了 MVC。我会将details 放在帮助器中。
    • 如果您像 Florent2 并且需要将其作为验证消息的一部分,这将很有帮助。谢谢山姆。
    • 这对我有用。如果违反该原则的解决方案明显优于遵守该原则的解决方案,我认为始终遵循 MVC(或任何原则)是没有意义的。
    • 不推荐这种方式。它添加了许多您不需要的方法,并且使您的命名空间变得混乱,它可能会覆盖某些方法,并且某些辅助模块依赖于其他辅助模块(因此您可能需要包含多个模块),从而产生问题更糟糕。有关解释和更好的方法,请参阅:http://railscasts.com/episodes/132-helpers-outside-views
    【解决方案5】:

    借助@fguillen 的响应,我想覆盖ApplicationHelper 模块中的number_to_currency 方法,以便如果值为0blank,它将输出一个破折号。

    这是我的代码,以防你们发现这样有用的东西:

    module ApplicationHelper
      def number_to_currency(value)
        if value == 0 or value.blank?
          raw "&ndash;"
        else
          ActionController::Base.helpers.number_to_currency(value)
        end
      end
    end
    

    【讨论】:

      【解决方案6】:

      您可以直接从您的控制器或模型中使用view_context.number_to_currency

      【讨论】:

        【解决方案7】:

        @fguillen 的方法很好,尽管这里有一个更简洁的方法,特别是考虑到该问题对to_dollar 进行了两次引用。我将首先使用 Ryan Bates 的代码 (http://railscasts.com/episodes/132-helpers-outside-views) 进行演示。

        def description
          "This category has #{helpers.pluralize(products.count, 'product')}."
        end
        
        def helpers
          ActionController::Base.helpers
        end
        

        注意电话helpers.pluralize。这是可能的,因为方法定义 (def helpers) 只返回 ActionController::Base.helpers。因此helpers.pluralizeActionController::Base.helpers.pluralize 的缩写。现在您可以多次使用helpers.pluralize,而无需重复冗长的模块路径。

        所以我想这个特定问题的答案可能是:

        class Job < ActiveRecord::Base
          include JobsHelper
          def details
            return "Only " + helpers.to_dollar(part_amount_received) + 
                   " out of " + helpers.to_dollar(price) + " received."
          end
        
          def helpers
            ActionView::Helpers::NumberHelper
          end
        end
        

        【讨论】:

          【解决方案8】:

          这不是一个好习惯,但它对我有用!

          在控制器中导入包括 ActionView::Helpers::NumberHelper。例如:

          class ProveedorController < ApplicationController
              include ActionView::Helpers::NumberHelper
              # layout 'example'
          
              # GET /proveedores/filtro
              # GET /proveedores/filtro.json
              def filtro
                  @proveedores = Proveedor.all
          
                  respond_to do |format|
                      format.html # filtro.html.erb
                      format.json { render json: @proveedores }
                  end
              end
          
              def valuacion_cartera
                  @total_valuacion = 0
                  facturas.each { |fac|
                      @total_valuacion = @total_valuacion + fac.SumaDeImporte
                  }
          
                  @total = number_to_currency(@total_valuacion, :unit => "$ ")
          
                  p '*'*80
                  p @total_valuacion
              end
          end
          

          希望对你有帮助!

          【讨论】:

            【解决方案9】:

            真的很惊讶没有人谈论过使用装饰器。他们的目的是解决您面临的问题等等。

            https://github.com/drapergem/draper

            编辑:看起来接受的答案基本上确实建议做这样的事情。但是,是的,你想使用装饰器。这是一个很棒的教程系列,可帮助您了解更多信息:

            https://gorails.com/episodes/decorators-from-scratch?autoplay=1

            附: - @excid3 我接受免费会员月 LOL

            【讨论】:

              【解决方案10】:

              如果您不需要ActionView 定义的附加功能,您可以只使用include ActiveSupport::NumberHelper 模块。

              https://github.com/rails/rails/blob/44260581bec06e4ce05f3dd838c8b4736fc7eb1d/actionview/lib/action_view/helpers/number_helper.rb#L383

              【讨论】:

                【解决方案11】:

                Helper 方法通常用于查看文件。 在 Model 类中使用这些方法不是一个好习惯。 但如果你想使用,那么 Sam 的回答是可以的。 或者我建议您可以编写自己的自定义方法。

                【讨论】:

                • 这不是答案。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-11-25
                • 1970-01-01
                • 1970-01-01
                • 2016-09-17
                • 1970-01-01
                • 2023-03-03
                相关资源
                最近更新 更多