Bagaimana cara menggunakan metode pembantu "number_to_currency" dalam model daripada tampilan?

94

Saya ingin menggunakan to_dollarmetode dalam model saya seperti ini:

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

Sayangnya, number_to_currencymetode tersebut tidak dikenali di sini:

metode `number_to_currency 'yang tidak ditentukan untuk # <Job: 0x311eb00>

Ada ide bagaimana membuatnya bekerja?

Misha Moroshko
sumber

Jawaban:

103

Ini tidak tersedia karena penggunaannya dalam model (biasanya) melanggar MVC (dan tampaknya dalam kasus Anda). Anda mengambil data dan memanipulasinya untuk presentasi. Ini, menurut definisi, termasuk dalam tampilan, bukan modelnya.

Berikut beberapa solusinya:

  • Gunakan presenter atau objek model tampilan untuk menengahi antara model dan tampilan. Ini hampir pasti membutuhkan lebih banyak pekerjaan awal daripada solusi lain, tetapi hampir selalu merupakan desain yang lebih baik. Menggunakan pembantu dalam presenter / view-model tidak melanggar MVC, karena mereka berada di lapisan tampilan, menggantikan pembantu Rails kustom tradisional dan tampilan yang dipenuhi logika.

  • Secara eksplisit include ActionView::Helpers::NumberHelperdalam JobsHelperalih-alih bergantung pada Rails untuk memuatnya secara ajaib untuk Anda. Ini masih belum bagus, karena Anda seharusnya tidak mengakses helper dari model.

  • Melanggar MVC & SRP . Lihat jawaban fguillen untuk bagaimana melakukan ini. Saya tidak akan mengulanginya di sini karena saya tidak setuju dengannya. Terlebih lagi, apakah saya tidak setuju dengan mencemari model Anda dengan metode presentasi seperti dalam jawaban Sam .

Jika Anda berpikir "tetapi saya benar-benar membutuhkan ini untuk menulis metode to_csv& saya to_pdfdalam model saya!", Maka keseluruhan premis Anda salah — lagipula, Anda tidak memiliki to_htmlmetode, bukan? Namun objek Anda sangat sering dirender sebagai HTML. Pertimbangkan untuk membuat kelas baru untuk menghasilkan keluaran daripada membuat model data Anda mengetahui apa itu CSV ( karena seharusnya tidak ).

Adapun untuk menggunakan pembantu untuk kesalahan validasi ActiveModel dalam model, saya minta maaf tetapi ActiveModel / Rails telah mengacaukan kita semua di sana dengan memaksa pesan kesalahan untuk direalisasikan di lapisan data, daripada mengembalikan ide semantik dari kesalahan menjadi disadari kemudian— mendesah . Anda bisa menyiasati ini, tetapi pada dasarnya berarti tidak menggunakan ActiveModel :: Errors lagi. Saya telah melakukannya, itu bekerja dengan baik.

Selain itu, berikut adalah cara yang berguna untuk menyertakan pembantu dalam penyaji / model tampilan tanpa mencemari kumpulan metodenya (karena mampu melakukan misalnya MyPresenterOrViewModel.new.link_to(...)tidak masuk akal):

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
sumber
5
Saya biasanya mengikuti aturan ini tetapi melanggarnya ketika saya membutuhkan view helper untuk memformat pesan kesalahan validasi yang ditentukan dalam model.
Florent2
44
Ini adalah nasihat yang bagus, tetapi jawaban yang buruk karena tidak menjawab pertanyaan.
Jaryl
21
Ada beberapa kasus di mana ini bukan jawaban yang bagus, misalnya saat ini saya sedang membangun laporan csv dan perlu menggunakan sesuatu seperti ini dalam metode to_csv di kelas yang tidak akan pernah melihat tampilan. Hanya menumbuhkan cita-cita pemrograman tidak selalu membantu.
nitecoder
1
Ya, apa yang dikatakan nitecoder. Saya mengalami masalah yang sama. Saya membuat laporan PDF dan hanya ingin memformat nomor telepon dengan baik.
James Adam
3
@maurice Ini adalah lereng licin dari "yah, hanya satu ini" menjadi model yang membengkak. Pembantu aplikasi di Rails adalah laci sampah, presenter / model tampilan lebih mudah dikelola. Saya tidak melihat menciptakan data yang untuk laporan dan menghasilkan (html | pdf | csv |. Dll) tampilan data yang sebagai tanggung jawab tunggal lebih dari yang saya lakukan untuk, misalnya, orang dan halaman HTML orang acara.
Andrew Marshall
184

Saya setuju dengan Anda semua bahwa ini dapat merusak pola MVC tetapi selalu ada alasan untuk merusak pola, dalam kasus saya, saya memerlukan metode formatter mata uang ini untuk menggunakannya dalam filter template ( Liquid dalam kasus saya).

Pada akhirnya saya menemukan bahwa saya dapat mengakses metode formatter mata uang ini menggunakan hal-hal seperti ini:

ActionController::Base.helpers.number_to_currency
fguillen.dll
sumber
6
Ini bagus, meskipun ada cara yang sedikit lebih bersih untuk melakukannya. Lihat http://railscasts.com/episodes/132-helpers-outside-views
pengguna664833
4
Lagu komentar Yay di RailsCasts: Di Rails 3 pada tahun 2013, menggunakan bantuan View di Controller dilakukan seperti view_context.number_to_currency (jumlah)
olleolleolle
3
Pernahkah Anda berpikir untuk menggunakan permata "uang"? Karena objek uang menyediakan metode format (), dan Anda dapat memanggilnya dalam model, pengontrol, atau tampilan.
Zack Xu
74

Saya tahu utas ini sudah sangat tua, tetapi seseorang dapat mencari solusi untuk masalah ini di Rails 4+. Pengembang menambahkan ActiveSupport :: NumberHelper, yang dapat digunakan tanpa mengakses modul / kelas terkait tampilan menggunakan:

ActiveSupport::NumberHelper.number_to_currency(amount, precision: 0)
Michał Zalewski
sumber
Pendekatan ini berhasil untuk saya ketika saya ingin bereksperimen dengan perilaku number_to_percentagedi konsol Rails. Terima kasih!
Jon Schneider
28

Anda juga perlu menyertakan 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
Sam
sumber
2
Terima kasih, terlihat bagus, tapi saya harus setuju dengan orang lain yang mengatakan saya melanggar MVC. Aku akan memasukkan detailshelper.
Misha Moroshko
1
Bermanfaat jika Anda seperti Florent2 dan perlu meletakkannya sebagai bagian dari pesan validasi. Terima kasih Sam.
RyanJM
Ini berhasil untuk saya. Menurut saya tidak masuk akal untuk selalu mengikuti MVC (atau prinsip apa pun) jika solusi yang melanggar prinsip itu jelas lebih baik daripada solusi yang menganutnya.
Jason Swett
2
Pendekatan ini tidak disarankan. Itu menambahkan banyak metode yang tidak Anda perlukan, dan itu mengacaukan namespace Anda, itu mungkin menimpa beberapa metode, dan beberapa modul pembantu bergantung pada modul pembantu lain (jadi Anda mungkin perlu menyertakan beberapa modul), sehingga membuat masalah bahkan lebih buruk. Untuk penjelasan dan pendekatan yang lebih baik, lihat: http://railscasts.com/episodes/132-helpers-outside-views
user664833
6

Membonceng dari @fguillenrespon, saya ingin mengganti number_to_currencymetode dalam ApplicationHelpermodul saya sehingga jika nilainya adalah 0atau blankitu akan menghasilkan tanda hubung sebagai gantinya.

Inilah kode saya jika kalian menemukan sesuatu seperti ini berguna:

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
sumber
4

Anda dapat menggunakan view_context.number_to_currencylangsung dari pengontrol atau model Anda.

Felipe M Andrada
sumber
3

Cara @ fguillen bagus, meskipun ada pendekatan yang sedikit lebih bersih, khususnya mengingat bahwa pertanyaan tersebut membuat dua referensi to_dollar. Saya pertama-tama akan mendemonstrasikan menggunakan kode 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

Perhatikan panggilannya helpers.pluralize. Ini dimungkinkan karena metode definition ( def helpers), yang hanya mengembalikan ActionController::Base.helpers. Oleh karena itu helpers.pluralizeadalah kependekan dari ActionController::Base.helpers.pluralize. Sekarang Anda bisa menggunakanhelpers.pluralize beberapa kali, tanpa mengulangi jalur modul yang panjang.

Jadi saya kira jawaban untuk pertanyaan khusus ini bisa jadi:

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
pengguna664833
sumber
2

Ini bukan latihan yang baik tapi berhasil untuk saya!

untuk mengimpor include ActionView :: Helpers :: NumberHelper di controller. Sebagai contoh:

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

Semoga membantu Anda!

alexventuraio
sumber
2

Benar-benar terkejut tidak ada satu orang pun yang berbicara tentang menggunakan Dekorator. Tujuan mereka adalah untuk menyelesaikan masalah yang Anda hadapi, dan banyak lagi.

https://github.com/drapergem/draper

EDIT: Sepertinya jawaban yang diterima pada dasarnya menyarankan untuk melakukan sesuatu seperti ini. Tapi ya, Anda ingin menggunakan dekorator. Berikut adalah seri tutorial bagus untuk membantu Anda lebih memahami:

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

PS - @ excid3 Saya menerima bulan keanggotaan gratis LOL

Greg Blass
sumber
-5

Metode pembantu umumnya digunakan untuk Melihat file. Ini bukan praktik yang baik untuk menggunakan metode ini di kelas Model. Tetapi jika Anda ingin menggunakan maka jawaban Sam tidak apa-apa. ATAU saya sarankan Anda dapat menulis metode kustom Anda sendiri.

Ashish
sumber
2
Ini bukanlah jawaban.
Bonifacio2