Come utilizzare il metodo di supporto "number_to_currency" nel modello anziché nella visualizzazione?


93

Vorrei utilizzare il to_dollarmetodo nel mio modello in questo modo:

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

Sfortunatamente, il number_to_currencymetodo non è riconosciuto qui:

metodo non definito `number_to_currency 'per # <Job: 0x311eb00>

Qualche idea su come farlo funzionare?

Risposte:


103

Non è disponibile perché il suo utilizzo in un modello (in genere) viola MVC (e sembra che lo sia nel tuo caso). Stai prendendo dati e li manipoli per la presentazione. Questo, per definizione, appartiene alla vista, non al modello.

Ecco alcune soluzioni:

  • Utilizzare un presentatore o un oggetto modello di visualizzazione per mediare tra il modello e la vista. Questo richiede quasi sicuramente più lavoro iniziale rispetto ad altre soluzioni, ma è quasi sempre un design migliore. L'uso di helper in un presenter / view-model non viola MVC, poiché risiedono nel livello di visualizzazione, sostituendo i tradizionali helper Rails personalizzati e le visualizzazioni piene di logica.

  • Esplicitamente include ActionView::Helpers::NumberHelperin JobsHelperinvece di dipendere da Rails essersi magicamente caricati per voi. Questo non è ancora eccezionale, poiché non dovresti accedere a un aiutante da un modello.

  • Violare MVC e SRP . Vedi la risposta di fguillen per sapere come farlo. Non lo farò eco qui perché non sono d'accordo con esso. Ancora di più, però, non sono d'accordo con l'inquinamento del tuo modello con metodi di presentazione come nella risposta di Sam .

Se pensi "ma ho davvero bisogno di questo per scrivere i miei metodi to_csv& to_pdfnel mio modello!", Allora tutta la tua premessa è sbagliata - dopo tutto, non hai un to_htmlmetodo, vero? Eppure il tuo oggetto è molto spesso reso come HTML. Prendi in considerazione la creazione di una nuova classe per generare l'output invece di far sapere al tuo modello di dati cos'è un CSV ( perché non dovrebbe ).

Per quanto riguarda l'utilizzo di helper per gli errori di convalida di ActiveModel nel modello, beh, mi dispiace ma ActiveModel / Rails ci ha fregati tutti costringendo i messaggi di errore a essere realizzati nel livello dati, piuttosto che restituire l' idea semantica di un errore da essere realizzato più tardi - sigh . Puoi aggirare questo problema, ma fondamentalmente significa non usare più ActiveModel :: Errors. L'ho fatto, funziona bene.

Per inciso, ecco un modo utile per includere gli helper in un presentatore / modello di visualizzazione senza inquinare il suo insieme di metodi (perché essere in grado di fare ad esempio MyPresenterOrViewModel.new.link_to(...)non ha senso):

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
Di solito seguo questa regola ma la infrango quando ho bisogno di un assistente di visualizzazione per formattare un messaggio di errore di convalida definito nel modello.
Florent2

43
Questo è un buon consiglio, ma è una cattiva risposta perché non risolve la domanda.
Jaryl

21
Ci sono casi in cui questa non è un'ottima risposta, ad esempio in questo momento dove sto creando un rapporto csv e ho bisogno di usare qualcosa di simile in un metodo to_csv in una classe che non vedrà mai una vista. Il solo germogliare degli ideali di programmazione non è sempre utile.
nitecoder

1
Sì, quello che ha detto il nitecoder. Sto riscontrando lo stesso problema. Sto generando report PDF e desidero semplicemente formattare in modo corretto un numero di telefono.
James Adam

3
@maurice È un pendio scivoloso da "beh, solo questa cosa" a un modello gonfio. Gli helper delle app in Rails sono un cassetto della spazzatura, i presentatori / modelli di visualizzazione sono più facili da gestire. Non vedo la creazione dei dati per un report e generare la (html | pdf | csv |. Ecc) vista di questi dati come una singola responsabilità non più di quanto faccio per, ad esempio, una persona e una pagina HTML persona spettacolo.
Andrew Marshall

185

Sono d'accordo con tutti voi sul fatto che questo potrebbe rompere il pattern MVC ma ci sono sempre ragioni per rompere un pattern, nel mio caso avevo bisogno di questi metodi di formattatore di valuta per usarli in un filtro di template ( Liquid nel mio caso).

Alla fine ho scoperto che potevo accedere a questi metodi di formattatore di valuta usando cose come questa:

ActionController::Base.helpers.number_to_currency

6
Questo va bene, anche se c'è un modo leggermente più pulito per farlo. Vedi http://railscasts.com/episodes/132-helpers-outside-views
user664833

4
Yay comment track in RailsCasts: In Rails 3 in 2013, using a View helper in a Controller is done like view_context.number_to_currency (amount)
olleolleolle

3
Hai mai pensato di utilizzare la gemma "soldi"? Poiché l'oggetto money fornisce un metodo format () e puoi invocarlo nel modello, nel controller o nella vista.
Zack Xu

71

So che questo thread è molto vecchio, ma qualcuno può cercare una soluzione per questo problema in Rails 4+. Gli sviluppatori hanno aggiunto ActiveSupport :: NumberHelper, che può essere utilizzato senza accedere ai moduli / classi correlati alla visualizzazione utilizzando:

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

Questo approccio ha funzionato per me quando volevo sperimentare il comportamento di number_to_percentagenella console di Rails. Grazie!
Jon Schneider

27

È necessario includere anche 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
Grazie, sembra buono, ma devo essere d'accordo con altri che dicono di violare l'MVC. Metterò detailsl'aiutante.
Misha Moroshko

1
Utile se sei come Florent2 e hai bisogno di inserirlo come parte di un messaggio di convalida. Grazie Sam.
RyanJM

Questo ha funzionato per me. Non penso abbia senso seguire sempre MVC (o qualsiasi principio) se una soluzione che viola quel principio è chiaramente migliore di una che lo aderisce.
Jason Swett

2
Questo approccio non è consigliato. Aggiunge molti metodi che non ti servono e ingombra il tuo spazio dei nomi, potrebbe sovrascrivere alcuni metodi e alcuni moduli di supporto si basano su altri moduli di supporto (quindi potrebbe essere necessario includere più moduli), rendendo così il problema persino peggio. Per una spiegazione e un approccio migliore, vedere: http://railscasts.com/episodes/132-helpers-outside-views
user664833

6

Eseguendo il backup della @fguillenrisposta di, volevo sovrascrivere il number_to_currencymetodo nel mio ApplicationHelpermodulo in modo che se il valore fosse 0o blanksarebbe invece emesso un trattino.

Ecco il mio codice nel caso in cui voi ragazzi trovaste utile qualcosa di simile:

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

4

Puoi usare view_context.number_to_currencydirettamente dal tuo controller o modello.


3

Il modo di @ fguillen è buono, anche se questo è un approccio leggermente più pulito, in particolare dato che la domanda fa due riferimenti to_dollar. Per prima cosa dimostrerò l'utilizzo del codice di 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

Nota la chiamata helpers.pluralize. Ciò è possibile grazie alla definizione del metodo ( def helpers), che restituisce semplicemente ActionController::Base.helpers. Quindi helpers.pluralizeè breve per ActionController::Base.helpers.pluralize. Ora puoi usare helpers.pluralizepiù volte, senza ripetere i lunghi percorsi del modulo.

Quindi suppongo che la risposta a questa particolare domanda potrebbe essere:

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

Non è una buona pratica ma per me funziona!

per importare includere ActionView :: Helpers :: NumberHelper nel controller. Per esempio:

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

Spero ti aiuti!


2

Davvero sorpreso che nessuna persona abbia parlato di usare un decoratore. Il loro scopo è risolvere il problema che stai affrontando e altro ancora.

https://github.com/drapergem/draper

EDIT: Sembra che la risposta accettata fondamentalmente suggerisse di fare qualcosa di simile. Ma sì, vuoi usare decoratori. Ecco una fantastica serie di tutorial per aiutarti a capire di più:

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

PS - @ excid3 Accetto mesi di iscrizione gratuita LOL



-5

I metodi di supporto vengono generalmente utilizzati per visualizzare i file. Non è una buona pratica utilizzare questi metodi nella classe Model. Ma se vuoi usare, la risposta di Sam è ok. O ti suggerisco di scrivere il tuo metodo personalizzato.


2
Questa non è una risposta.
Bonifacio2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.