Làm cách nào để sử dụng phương thức trợ giúp “number_to_currency” trong mô hình thay vì xem?


93

Tôi muốn sử dụng to_dollarphương thức trong mô hình của mình như sau:

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

Rất tiếc, number_to_currencyphương pháp này không được công nhận ở đây:

phương thức không xác định `number_to_currency 'cho # <Job: 0x311eb00>

Bất kỳ ý tưởng làm thế nào để làm cho nó hoạt động?

Câu trả lời:


103

Nó không khả dụng vì việc sử dụng nó trong một mô hình (thường) vi phạm MVC (và nó dường như trong trường hợp của bạn). Bạn đang lấy dữ liệu và thao tác với nó để trình bày. Điều này, theo định nghĩa, thuộc về khung nhìn, không phải mô hình.

Dưới đây là một số giải pháp:

  • Sử dụng người trình bày hoặc đối tượng mô hình dạng xem để dàn xếp giữa mô hình và dạng xem. Điều này gần như chắc chắn đòi hỏi nhiều công việc ban đầu hơn các giải pháp khác, nhưng hầu như luôn luôn là một thiết kế tốt hơn. Việc sử dụng trình trợ giúp trong mô hình người trình bày / chế độ xem không vi phạm MVC, vì chúng nằm trong lớp chế độ xem, thay thế trình trợ giúp Rails tùy chỉnh truyền thống và chế độ xem đầy logic.

  • Rõ ràng include ActionView::Helpers::NumberHelpertrong JobsHelperthay vì phụ thuộc vào Rails để tải nó một cách kỳ diệu cho bạn. Điều này vẫn chưa tuyệt vời, vì bạn không nên truy cập trình trợ giúp từ một mô hình.

  • Vi phạm MVC & SRP . Xem câu trả lời của fguillen để biết cách thực hiện việc này. Tôi sẽ không lặp lại nó ở đây vì tôi không đồng ý với nó. Tuy nhiên, thậm chí nhiều hơn thế, tôi không đồng ý với việc làm ô nhiễm mô hình của bạn với các phương pháp trình bày như trong câu trả lời của Sam .

Nếu bạn nghĩ “nhưng tôi thực sự cần cái này để viết to_csv& to_pdfphương pháp của tôi trong mô hình của tôi!”, Thì toàn bộ tiền đề của bạn là sai — sau cùng, bạn không có to_htmlphương pháp, phải không? Và đối tượng của bạn thường được hiển thị dưới dạng HTML. Cân nhắc tạo một lớp mới để tạo đầu ra của bạn thay vì làm cho mô hình dữ liệu của bạn biết CSV là gì ( vì nó không nên ).

Đối với việc sử dụng trợ giúp cho các lỗi xác thực ActiveModel trong mô hình, tôi xin lỗi nhưng ActiveModel / Rails đã khiến tất cả chúng tôi gặp khó khăn bằng cách buộc các thông báo lỗi được nhận ra trong lớp dữ liệu, thay vì trả về ý tưởng ngữ nghĩa của lỗi. nhận ra sau— thở dài . Bạn có thể khắc phục điều này, nhưng về cơ bản nó có nghĩa là không sử dụng ActiveModel :: Errors nữa. Tôi đã làm nó, nó hoạt động tốt.

Ngoài ra, đây là một cách hữu ích để đưa người trợ giúp vào mô hình người trình bày / chế độ xem mà không làm ô nhiễm tập hợp các phương pháp của nó (vì có thể làm được điều đó chẳng MyPresenterOrViewModel.new.link_to(...)có nghĩa lý gì):

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
Tôi thường làm theo quy tắc này nhưng phá vỡ nó khi tôi cần trình trợ giúp chế độ xem để định dạng thông báo lỗi xác thực được xác định trong mô hình.
Florent2 ngày

43
Đây là một lời khuyên tốt, nhưng đó là một câu trả lời tồi vì nó không giải quyết được câu hỏi.
Jaryl

21
Có những trường hợp đây không phải là một câu trả lời tuyệt vời, chẳng hạn như ngay bây giờ khi tôi đang gửi báo cáo csv và cần sử dụng một cái gì đó như thế này trong phương thức to_csv trong một lớp sẽ không bao giờ thấy một chế độ xem. Không phải lúc nào các lý tưởng lập trình cũng nảy mầm.
nitecoder

1
Yea, nitecoder đã nói gì. Tôi đang gặp phải vấn đề tương tự. Tôi đang tạo báo cáo PDF và chỉ muốn định dạng số điện thoại một cách độc đáo.
James Adam

3
@maurice Đó là một con dốc trơn trượt từ “chỉ một thứ này” đến một mô hình cồng kềnh. Trình trợ giúp ứng dụng trong Rails là một ngăn kéo rác, người trình bày / mô hình chế độ xem dễ quản lý hơn. Tôi không thấy việc tạo dữ liệu cho báo cáo và tạo chế độ xem (html | pdf | csv | etc.) Của dữ liệu đó là một trách nhiệm duy nhất mà tôi phải làm, ví dụ: một người và một người HTML hiển thị trang.
Andrew Marshall

185

Tôi đồng ý với tất cả các bạn rằng điều này có thể phá vỡ mẫu MVC nhưng luôn có lý do để phá vỡ một mẫu, trong trường hợp của tôi, tôi cần các phương pháp định dạng tiền tệ này để sử dụng chúng trong bộ lọc mẫu ( trong trường hợp của tôi là Liquid ).

Cuối cùng, tôi phát hiện ra rằng tôi có thể truy cập vào các phương pháp định dạng tiền tệ này bằng cách sử dụng những thứ như sau:

ActionController::Base.helpers.number_to_currency

6
Điều này là tốt, mặc dù có một cách làm sạch hơn một chút. Xem http://railscasts.com/episodes/132-helpers-outside-views
user664833

4
Yay bình luận theo dõi trong RailsCasts: Trong Rails 3 vào năm 2013, sử dụng một Xem helper trong một điều khiển được thực hiện như view_context.number_to_currency (số tiền)
olleolleolle

3
Bạn đã nghĩ đến việc sử dụng đá quý "tiền" chưa? Vì đối tượng money cung cấp phương thức format () và bạn có thể gọi nó trong mô hình, bộ điều khiển hoặc dạng xem.
Zack Xu

71

Tôi biết chủ đề này rất cũ, nhưng ai đó có thể tìm giải pháp cho vấn đề này trong Rails 4+. Các nhà phát triển đã thêm ActiveSupport :: NumberHelper, có thể được sử dụng mà không cần truy cập xem các mô-đun / lớp liên quan bằng cách sử dụng:

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

Cách tiếp cận này phù hợp với tôi khi tôi muốn thử nghiệm hành vi của number_to_percentagebảng điều khiển Rails. Cảm ơn!
Jon Schneider

27

Bạn cũng cần bao gồm 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
Cảm ơn, có vẻ tốt, nhưng tôi phải đồng ý với những người khác nói rằng tôi vi phạm MVC. Tôi sẽ đưa detailsngười trợ giúp vào.
Misha Moroshko

1
Hữu ích nếu bạn giống Florent2 và cần đặt nó như một phần của thông báo xác thực. Cảm ơn Sam.
RyanJM

Điều này đã làm việc cho tôi. Tôi không nghĩ rằng việc luôn tuân theo MVC (hoặc bất kỳ nguyên tắc nào) là hợp lý nếu một giải pháp vi phạm nguyên tắc đó rõ ràng là tốt hơn một giải pháp tuân thủ nó.
Jason Swett

2
Cách tiếp cận này không được khuyến khích. Nó thêm rất nhiều phương thức mà bạn không cần, và nó làm lộn xộn không gian tên của bạn, nó có thể ghi đè một số phương thức và một số mô-đun trợ giúp dựa vào các mô-đun trợ giúp khác (vì vậy bạn có thể cần bao gồm nhiều mô-đun), do đó gây ra sự cố thậm chí còn tệ hơn. Để có lời giải thích và cách tiếp cận tốt hơn, hãy xem: http://railscasts.com/episodes/132-helpers-outside-views
user664833

6

Sau khi thực @fguillenhiện phản hồi của, tôi muốn ghi đè number_to_currencyphương thức trong ApplicationHelpermô-đun của mình để nếu giá trị là 0hoặc blanknó sẽ xuất ra một dấu gạch ngang.

Đây là mã của tôi trong trường hợp các bạn thấy thứ gì đó hữu ích như thế này:

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

Bạn có thể sử dụng view_context.number_to_currencytrực tiếp từ bộ điều khiển hoặc mô hình của mình.


3

Cách của @ fguillen là tốt, mặc dù đây là một cách tiếp cận gọn gàng hơn một chút, cụ thể là câu hỏi có hai tham chiếu đến to_dollar. Đầu tiên tôi sẽ chứng minh bằng cách sử dụng mã của 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

Chú ý cuộc gọi helpers.pluralize. Điều này có thể xảy ra do định nghĩa phương thức ( def helpers), chỉ trả về ActionController::Base.helpers. Do đó helpers.pluralizelà viết tắt của ActionController::Base.helpers.pluralize. Bây giờ bạn có thể sử dụnghelpers.pluralize nhiều lần mà không cần lặp lại các đường dẫn mô-đun dài.

Vì vậy, tôi cho rằng câu trả lời cho câu hỏi cụ thể này có thể là:

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

Nó không phải là một thực hành tốt nhưng nó có hiệu quả đối với tôi!

để nhập bao gồm ActionView :: Helpers :: NumberHelper trong bộ điều khiển. Ví dụ:

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

Hy vọng nó sẽ giúp bạn!


2

Thực sự ngạc nhiên khi chưa có ai nói về việc sử dụng Decorator. Mục đích của họ là giải quyết vấn đề bạn đang gặp phải và hơn thế nữa.

https://github.com/drapergem/draper

CHỈNH SỬA: Có vẻ như câu trả lời được chấp nhận về cơ bản đã gợi ý làm điều gì đó như thế này. Nhưng vâng, bạn muốn sử dụng trình trang trí. Dưới đây là một loạt hướng dẫn tuyệt vời để giúp bạn hiểu thêm:

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

PS - @ excid3 Tôi chấp nhận số tháng thành viên miễn phí LOL



-5

Các phương pháp của trình trợ giúp thường được sử dụng cho Xem tệp. Nó không phải là một thực hành tốt để sử dụng các phương thức này trong lớp Model. Nhưng nếu bạn muốn sử dụng thì câu trả lời của Sam là ok. HOẶC tôi đề nghị bạn có thể viết phương pháp tùy chỉnh của riêng bạn.


2
Đây không phải là một câu trả lời.
Bonifacio2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.