如何在模型中使用“number_to_currency”辅助方法而不是查看?

时间:2011-03-03 04:39:26

标签: ruby-on-rails

我想在我的模型中使用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方法:

  

#&lt; Job:0x311eb00&gt;

的未定义方法`number_to_currency'

任何想法如何使其发挥作用?

11 个答案:

答案 0 :(得分:171)

我同意你们所有人的意见,这可能会打破MVC模式,但总有理由打破模式,在我的情况下,我需要这些货币格式化方法使用它们模板过滤器(在我的情况下, Liquid )。

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

ActionController::Base.helpers.number_to_currency

答案 1 :(得分:92)

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

以下是一些解决方案:

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

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

  • 违反MVC&amp; SRP。有关如何执行此操作,请参阅fguillen’s answer。我不会在此回应,因为我不同意。但更重要的是,我不同意使用Sam’s answer中的演示方法污染模型。

如果您认为“但我真的需要这样写to_csv&amp;我的模型中的to_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

答案 2 :(得分:57)

我知道这个帖子已经很老了,但有人可以在Rails 4+中寻找解决这个问题的方法。开发人员添加了ActiveSupport :: NumberHelper,可以在不使用以下方式访问与视图相关的模块/类的情况下使用:

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

答案 3 :(得分:25)

您还需要包含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

答案 4 :(得分:6)

抄袭@fguillen的回复,我想覆盖number_to_currency模块中的ApplicationHelper方法,以便价值为0或{{1}时它会输出一个破折号。

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

blank

答案 5 :(得分:4)

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

答案 6 :(得分:3)

@ 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

答案 7 :(得分:2)

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

导入包括控制器中的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

希望它可以帮到你!

答案 8 :(得分:2)

真的很惊讶没有人谈过使用装饰器。他们的目的是解决您面临的问题,以及更多。

https://github.com/drapergem/draper

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

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

P.S。 - @ excid3我接受免费会员月LOL

答案 9 :(得分:0)

如果您不需要include ActiveSupport::NumberHelper定义的其他功能,则可以仅使用ActionView模块。

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

答案 10 :(得分:-4)

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