如何在模型而不是视图中使用“ number_to_currency”帮助方法?


93

我想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>的未定义方法`number_to_currency'

有什么想法使它起作用吗?

Answers:


103

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

以下是一些解决方案:

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

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

  • 违反MVC和SRP。有关如何执行此操作,请参见fguillen的答案。我不会在这里回应它,因为我不同意它。但是,即使如此,我还是不同意像Sam的回答那样用表示方法污染您的模型。

如果您认为“但是在模型中编写我的to_csvto_pdf方法,我真的需要这个!”,那么您的整个前提都是错误的-毕竟,您没有to_html方法,对吗?然而,您的对象却经常被渲染为HTML。考虑创建一个新类来生成输出,而不是让数据模型知道CSV是什么(因为它不应该)。

至于在模型中使用的助手加载ActiveModel验证错误,那么,我很抱歉,但加载ActiveModel / Rails已经迫使错误信息在数据层得以实现,而不是返回语义拧我们所有有想法的错误的是实现later- 叹息。您可以解决此问题,但这基本上意味着不再使用ActiveModel :: Errors。我已经做到了,它运作良好。

顺便说一句,这是将助手包含在presenter / view-model中的一种有用的方法,而不会污染其方法集(因为能够做到例如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

5
我通常遵循此规则,但是当我需要视图助手来格式化模型中定义的验证错误消息时,会打破该规则。
Florent2 2011年

43
这是个很好的建议,但答案很差,因为它不能解决问题。
贾里尔

21
在某些情况下,这不是一个很好的答案,例如,现在我正在构建一个csv报告,并且需要在永远不会看到视图的类的to_csv方法中使用类似的内容。仅仅萌芽编程理想并不总是有帮助的。
nitecoder

1
是的,nitecoder说了什么。我遇到了同样的问题。我正在生成PDF报告,只是想很好地格式化电话号码。
詹姆斯·亚当

3
@maurice从“好这件事”到to肿的模型,这是一个滑溜溜的斜坡。Rails中的应用程序助手是一个垃圾抽屉,演示者/视图模型更易于管理。我看不到为报告创建数据并生成该数据的(html | pdf | csv | etc。)视图作为单一职责,这比我对一个人和一个HTML人的显示页面所做的更多。
Andrew Marshall

185

我与所有人都同意,这可能会破坏MVC模式,但总有理由要破坏模式,就我而言,我需要这些货币格式化程序方法才能在模板过滤器中使用它们(在我的情况下为Liquid)。

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

ActionController::Base.helpers.number_to_currency

6
这样做很好,尽管有一种更简洁的方法。参见http://railscasts.com/episodes/132-helpers-outside-views
user664833'4

4
RailsCasts中的Yay评论轨道:在2013年的Rails 3中,在Controller中使用View帮助器的过程类似于view_context.number_to_currency(amount)
olleolleolle

3
您是否考虑过使用“金钱”宝石?由于money对象提供了format()方法,因此您可以在模型,控制器或视图中调用它。
Zack Xu

71

我知道这个线程很旧,但是有人可以在Rails 4+中寻找解决此问题的方法。开发人员添加了ActiveSupport :: NumberHelper,可以使用它而无需使用以下命令访问与视图相关的模块/类:

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

当我想尝试number_to_percentage在Rails控制台中的行为时,这种方法对我有用。谢谢!
乔恩·施耐德

27

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

2
谢谢,看起来不错,但是我必须同意其他人说我违反了MVC。我将放入details助手。
Misha Moroshko'3

1
如果您像Florent2一样需要将其作为验证消息的一部分,则将很有帮助。谢谢山姆。
RyanJM

这对我有用。如果违反该原则的解决方案明显优于坚持该原则的解决方案,我认为始终遵循MVC(或任何原则)是没有意义的。
詹森·斯威特

2
不推荐这种方法。它添加了许多不需要的方法,并且使您的命名空间变得混乱,它可能会覆盖某些方法,并且某些帮助程序模块依赖于其他帮助程序模块(因此您可能需要包括多个模块),从而造成问题甚至更糟。有关说明和更好的方法,请参见:http
//railscasts.com/episodes/132-helpers-outside-views

6

的捎带关闭@fguillen的反应,我想重写number_to_currency方法我的ApplicationHelper模块,这样,如果该值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


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.pluralize是的缩写ActionController::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

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

希望对您有帮助!


2

没有一个人谈论使用装饰器,真让我感到惊讶。他们的目的是解决您面临的问题,以及更多。

https://github.com/drapergem/draper

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

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

PS-@ excid3我接受免费会员月份,哈哈



-5

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


2
这不是答案。
Bonifacio2
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.