ビューではなくモデルで「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方法はここでは認識されません。

#<Job:0x311eb00>の未定義のメソッド `number_to_currency '

それを機能させる方法はありますか?

回答:


103

モデルでの使用は(通常)MVCに違反しているため使用できません(あなたの場合はそうです)。データを取得して、プレゼンテーション用に操作しています。これは、定義上、モデルではなくビューに属しています。

ここにいくつかの解決策があります:

  • プレゼンターまたはビューモデルオブジェクトを使用して、モデルとビューを仲介します。これはほぼ間違いなく他のソリューションよりも多くの初期作業を必要としますが、ほとんどの場合、より優れた設計です。プレゼンター/ビューモデルでヘルパーを使用しても、ビューレイヤーに常駐するため、従来のカスタムRailsヘルパーやロジックで満たされたビューを置き換えるため、MVCに違反しません。

  • Railsに魔法のように読み込ませるのでinclude ActionView::Helpers::NumberHelperJobsHelperなく、明示的に読み込みます。モデルからヘルパーにアクセスすべきではないため、これはまだ素晴らしいことではありません。

  • MVCおよびSRPに違反します。これを行う方法については、fguillenの回答を参照してください。同意しないので、ここではエコーしません。ただし、サムの回答のように、プレゼンテーション方法でモデルを汚染することに同意しません

「でも、モデルにto_csvto_pdfメソッドを書くためにこれが本当に必要なのです!」と思う場合は、全体の前提が間違ってto_htmlいます。結局のところ、メソッドがないのですか?それでも、オブジェクトはHTMLとしてレンダリングされることが非常によくあります。データモデルにCSVが何であるかを認識させるのではなく、出力を生成するための新しいクラスを作成することを検討してください(CSVが認識できないため)。

モデルで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

5
通常はこのルールに従いますが、モデルで定義された検証エラーメッセージをフォーマットするビューヘルパーが必要な場合は、ルールを破ります。
Florent2

43
これは良いアドバイスですが、質問を解決しないので悪い答えです。
Jaryl、2012年

21
これが良い答えではない場合があります。たとえば、現在csvレポートを作成していて、ビューを決して表示しないクラスのto_csvメソッドでこのようなものを使用する必要がある場合などです。プログラミングの理想を発芽させるだけでは、必ずしも役立つとは限りません。
nitecoder、2012

1
いや、Nitecoderが言ったこと。同じ問題が発生しています。私はPDFレポートを生成していて、電話番号を適切にフォーマットしたいだけです。
ジェームスアダム

3
@mauriceこれは、「この1つだけ」から膨らんだモデルへの滑りやすい坂道です。Railsのアプリヘルパーはジャンクドロワーであり、プレゼンター/ビューモデルは管理が簡単です。レポートのデータを作成し、そのデータの(html | pdf | csv | etc。)ビューを単一の責任として、たとえば個人やHTML個人の表示ページのように生成することはありません。
アンドリューマーシャル

185

私はこれがMVCパターンを壊す可能性があることをすべての人に同意しますが、パターンを壊す理由は常にあります。私の場合、これらの通貨フォーマッターメソッドテンプレートフィルター(私の場合はLiquid)使用する必要がありました

最後に、次のようなものを使用してこれらの通貨フォーマッタメソッドにアクセスできることがわかりました。

ActionController::Base.helpers.number_to_currency

6
これは良い方法ですが、ややクリーンな方法があります。http://railscasts.com/episodes/132-helpers-outside-views
user664833を

4
RailsCastsでイェーイコメントトラック:ではRailsのは2013年に、コントローラーのViewヘルパーを用いたものをview_context.number_to_currency(量)のように行われている3
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

1
あなたがFlorent2のようで、検証メッセージの一部として置く必要がある場合に役立ちます。サムありがとう
RyanJM 2011年

これでうまくいきました。その原則に違反するソリューションが、それを遵守するソリューションより明らかに優れている場合は、常にMVC(または任意の原則)に従うことは意味がないと思います。
Jason Swett、2011

2
このアプローチは推奨されません。それはあなたが必要としない多くのメソッドを追加します、そしてそれはあなたの名前空間を乱雑にします、それはいくつかのメソッドを上書きするかもしれません、そしていくつかのヘルパーモジュールは他のヘルパーモジュールに依存します(それで複数のモジュールを含める必要があるかもしれません)それにより問題を作りますさらに悪い。説明とより良いアプローチについては、http
//railscasts.com/episodes/132-helpers-outside-views

6

@fguillenの応答を便乗させて、自分のnumber_to_currencyメソッドをオーバーライドしたかったApplicationHelperその値がということであった場合、モジュール0またはblankその代わりに出力ダッシュであろうと。

これがあなたがこのようなものを便利だと思う場合の私のコードです:

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の方法は良いですが、質問が2つの参照をしていることを考えると、ここでは少しクリーンなアプローチ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私は無料のメンバーシップ月を受け入れますLOL



-5

ヘルパーメソッドは通常、Viewファイルに使用されます。これらのメソッドをModelクラスで使用することはお勧めできません。しかし、使用したい場合は、Samの答えは問題ありません。または、独自のカスタムメソッドを記述できることをお勧めします。


2
これは答えではありません。
Bonifacio2 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.