Wie verwende ich die Hilfsmethode "number_to_currency" im Modell anstatt in der Ansicht?

93

Ich möchte to_dollarin meinem Modell folgende Methode verwenden:

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

Leider wird die number_to_currencyMethode hier nicht erkannt:

undefinierte Methode `number_to_currency 'für # <Job: 0x311eb00>

Irgendwelche Ideen, wie es funktioniert?

Mischa Moroshko
quelle

Antworten:

103

Es ist nicht verfügbar, da seine Verwendung in einem Modell (normalerweise) gegen MVC verstößt (und dies scheint in Ihrem Fall der Fall zu sein). Sie nehmen Daten und bearbeiten sie zur Präsentation. Dies gehört per Definition in die Ansicht, nicht in das Modell.

Hier sind einige Lösungen:

  • Verwenden Sie einen Präsentator oder ein Ansichtsmodellobjekt, um zwischen dem Modell und der Ansicht zu vermitteln. Dies erfordert fast definitiv mehr Anfangsarbeit als andere Lösungen, ist aber fast immer ein besseres Design. Die Verwendung von Helfern in einem Präsentator- / Ansichtsmodell verstößt nicht gegen MVC, da sie sich in der Ansichtsebene befinden und herkömmliche benutzerdefinierte Rails-Helfer und logikgefüllte Ansichten ersetzen.

  • Explizit include ActionView::Helpers::NumberHelperin JobsHelperanstatt abhängig von Rails, um es magisch für Sie geladen zu haben. Dies ist immer noch nicht großartig, da Sie nicht von einem Modell aus auf einen Helfer zugreifen sollten.

  • MVC & SRP verletzen . Siehe dazu die Antwort von fguillen . Ich werde es hier nicht wiederholen, weil ich damit nicht einverstanden bin. Umso mehr bin ich jedoch nicht damit einverstanden, Ihr Modell mit Präsentationsmethoden wie in Sams Antwort zu verschmutzen .

Wenn Sie denken "aber ich brauche das wirklich, um meine to_csv& to_pdfMethoden in mein Modell zu schreiben !", Dann ist Ihre gesamte Prämisse falsch - schließlich haben Sie keine to_htmlMethode, oder? Und doch wird Ihr Objekt sehr oft als HTML gerendert. Erwägen Sie, eine neue Klasse zum Generieren Ihrer Ausgabe zu erstellen, anstatt Ihr Datenmodell wissen zu lassen, was eine CSV ist ( weil dies nicht der Fall sein sollte ).

Was die Verwendung von Hilfsprogrammen für ActiveModel-Validierungsfehler im Modell angeht, tut mir leid, aber ActiveModel / Rails hat uns alle dahin gebracht, indem er die Realisierung von Fehlermeldungen in der Datenschicht erzwungen hat, anstatt die semantische Idee eines Fehlers zurückzugeben später- realisiert Seufzer . Sie können dies umgehen, aber es bedeutet im Grunde, dass Sie ActiveModel :: Errors nicht mehr verwenden. Ich habe es geschafft, es funktioniert gut.

Abgesehen davon ist hier eine nützliche Möglichkeit, Helfer in ein Präsentations- / Ansichtsmodell aufzunehmen, ohne dessen Methoden zu verschmutzen (weil es z. B. MyPresenterOrViewModel.new.link_to(...)keinen Sinn macht , dies zu tun ):

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
Andrew Marshall
quelle
5
Normalerweise folge ich dieser Regel, breche sie jedoch, wenn ich einen Ansichtshelfer zum Formatieren einer im Modell definierten Validierungsfehlermeldung benötige.
Florent2
43
Dies ist ein guter Rat, aber eine schlechte Antwort, da er die Frage nicht löst.
Jaryl
21
Es gibt Fälle, in denen dies keine gute Antwort ist, zum Beispiel im Moment, in denen ich einen CSV-Bericht erstelle und so etwas in einer to_csv-Methode in einer Klasse verwenden muss, die niemals eine Ansicht sieht. Nur sprießende Programmierideale sind nicht immer hilfreich.
Nitecoder
1
Ja, was Nitecoder gesagt hat. Ich habe das gleiche Problem. Ich generiere PDF-Berichte und möchte einfach eine Telefonnummer schön formatieren.
James Adam
3
@maurice Es ist ein rutschiger Hang von "nur diese eine Sache" zu einem aufgeblähten Modell. App-Helfer in Rails sind eine Junk-Schublade, Moderatoren / Ansichtsmodelle sind einfacher zu verwalten. Ich sehe das Erstellen der Daten für einen Bericht und das Generieren der (html | pdf | csv | etc.) Ansicht dieser Daten nicht mehr als eine einzige Verantwortung als für eine Person und eine HTML-Personenseite.
Andrew Marshall
185

Ich stimme Ihnen allen zu, dass dies das MVC-Muster brechen könnte, aber es gibt immer Gründe, ein Muster zu brechen. In meinem Fall benötigte ich diese Währungsformatierungsmethoden , um sie in einem Vorlagenfilter zu verwenden ( in meinem Fall Liquid ).

Am Ende fand ich heraus, dass ich auf diese Währungsformatierungsmethoden zugreifen konnte :

ActionController::Base.helpers.number_to_currency
fguillen
quelle
6
Das ist gut, obwohl es eine etwas sauberere Art gibt, es zu tun. Siehe http://railscasts.com/episodes/132-helpers-outside-views
user664833
4
Yay Kommentartrack in RailsCasts: In Rails 3 im Jahr 2013 erfolgt die Verwendung eines View-Hilfsprogramms in einem Controller wie view_context.number_to_currency (Betrag)
olleolleolle
3
Haben Sie darüber nachgedacht, das Juwel "Geld" zu verwenden? Als Geldobjekt wird eine format () -Methode bereitgestellt, die Sie im Modell, im Controller oder in der Ansicht aufrufen können.
Zack Xu
70

Ich weiß, dass dieser Thread sehr alt ist, aber jemand kann in Rails 4+ nach einer Lösung für dieses Problem suchen. Entwickler haben ActiveSupport :: NumberHelper hinzugefügt, das verwendet werden kann, ohne auf ansichtsbezogene Module / Klassen zuzugreifen.

ActiveSupport::NumberHelper.number_to_currency(amount, precision: 0)
Michał Zalewski
quelle
Dieser Ansatz hat bei mir funktioniert, als ich mit dem Verhalten number_to_percentagein der Rails-Konsole experimentieren wollte . Vielen Dank!
Jon Schneider
27

Sie müssen auch ActionView :: Helpers :: NumberHelper einschließen

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
Sam
quelle
2
Danke, sieht gut aus, aber ich muss anderen zustimmen, die sagen, dass ich gegen die MVC verstoße. Ich werde detailsden Helfer einsetzen.
Mischa Moroshko
1
Hilfreich, wenn Sie wie Florent2 sind und es als Teil einer Validierungsnachricht einfügen müssen. Danke Sam.
RyanJM
Das hat bei mir funktioniert. Ich halte es nicht für sinnvoll, immer MVC (oder einem anderen Prinzip) zu folgen, wenn eine Lösung, die gegen dieses Prinzip verstößt, eindeutig besser ist als eine, die daran festhält.
Jason Swett
2
Dieser Ansatz wird nicht empfohlen. Es fügt viele Methoden hinzu, die Sie nicht benötigen, und es überfüllt Ihren Namespace, es kann einige Methoden überschreiben und einige Hilfsmodule basieren auf anderen Hilfsmodulen (daher müssen Sie möglicherweise mehrere Module einschließen), wodurch das Problem auftritt noch schlimmer. Eine Erklärung und einen besseren Ansatz finden Sie unter: http://railscasts.com/episodes/132-helpers-outside-views
user664833
6

Huckepack aus der @fguillenAntwort wollte ich die number_to_currencyMethode in meinem ApplicationHelperModul überschreiben, damit, wenn der Wert war 0oder blankstattdessen ein Bindestrich ausgegeben wird.

Hier ist mein Code für den Fall, dass ihr so ​​etwas nützlich findet:

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
aarona
quelle
4

Sie können view_context.number_to_currencydirekt von Ihrem Controller oder Modell verwenden.

Felipe M Andrada
quelle
3

@ fguillens Weg ist gut, obwohl hier ein etwas saubererer Ansatz ist, insbesondere angesichts der Tatsache, dass die Frage zwei Verweise enthält to_dollar. Ich werde zuerst den Code von Ryan Bates demonstrieren ( 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

Beachten Sie den Anruf helpers.pluralize. Dies ist aufgrund der Methodendefinition ( def helpers) möglich, die einfach zurückgibt ActionController::Base.helpers. Daher helpers.pluralizeist kurz für ActionController::Base.helpers.pluralize. Jetzt können Sie helpers.pluralizemehrere Male verwenden, ohne die langen Modulpfade zu wiederholen.

Ich nehme an, die Antwort auf diese spezielle Frage könnte sein:

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
user664833
quelle
2

Es ist keine gute Praxis, aber es funktioniert für mich!

Zum Importieren wird ActionView :: Helpers :: NumberHelper in den Controller aufgenommen. Beispielsweise:

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

Hoffe es hilft dir!

alexventuraio
quelle
2

Wirklich überrascht, dass nicht eine Person über die Verwendung eines Dekorateurs gesprochen hat. Ihr Zweck ist es, das Problem zu lösen, mit dem Sie konfrontiert sind, und vieles mehr.

https://github.com/drapergem/draper

EDIT: Sieht so aus, als hätte die akzeptierte Antwort im Grunde genommen vorgeschlagen, so etwas zu tun. Aber ja, Sie möchten Dekorateure verwenden. Hier ist eine großartige Tutorial-Serie, die Ihnen hilft, mehr zu verstehen:

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

PS - @ excid3 Ich akzeptiere kostenlose Mitgliedschaftsmonate LOL

Greg Blass
quelle
-5

Hilfsmethoden werden im Allgemeinen für View-Dateien verwendet. Es ist keine gute Praxis, diese Methoden in der Modellklasse zu verwenden. Aber wenn Sie verwenden möchten, ist Sams Antwort in Ordnung. ODER ich schlage vor, Sie können Ihre eigene benutzerdefinierte Methode schreiben.

Ashish
quelle
2
Dies ist keine Antwort.
Bonifacio2