จะใช้เมธอดตัวช่วย“ number_to_currency” ในโมเดลแทนการดูได้อย่างไร


94

ฉันต้องการใช้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ไม่รู้จักวิธีการที่นี่:

วิธีที่ไม่ได้กำหนด `number_to_currency 'สำหรับ # <Job: 0x311eb00>

มีความคิดอย่างไรที่จะทำให้มันใช้งานได้?

คำตอบ:


103

ไม่สามารถใช้งานได้เนื่องจากการใช้งานในโมเดล (โดยทั่วไป) ละเมิด MVC (และดูเหมือนว่าในกรณีของคุณ) คุณกำลังรับข้อมูลและจัดการข้อมูลเพื่อนำเสนอ ตามความหมายแล้วสิ่งนี้อยู่ในมุมมองไม่ใช่โมเดล

วิธีแก้ไขมีดังนี้

  • ใช้ผู้นำเสนอหรือดูโมเดลวัตถุเพื่อเป็นสื่อกลางระหว่างโมเดลและมุมมอง เกือบจะแน่นอนว่าต้องใช้งานเริ่มต้นมากกว่าโซลูชันอื่น ๆ แต่มักจะเป็นการออกแบบที่ดีกว่า การใช้ตัวช่วยในผู้นำเสนอ / โมเดลมุมมองไม่ได้ละเมิด MVC เนื่องจากพวกเขาอยู่ในเลเยอร์มุมมองแทนที่ตัวช่วย Rails แบบกำหนดเองแบบเดิมและมุมมองที่เต็มไปด้วยตรรกะ

  • อย่างชัดเจนinclude ActionView::Helpers::NumberHelperในJobsHelperแทนขึ้นอยู่กับทางรถไฟที่จะมีการโหลดอย่างน่าอัศจรรย์สำหรับคุณ สิ่งนี้ยังไม่ดีนักเนื่องจากคุณไม่ควรเข้าถึงตัวช่วยจากแบบจำลอง

  • ละเมิด MVC และSRP ดูคำตอบของ fguillenสำหรับวิธีการทำเช่นนี้ ฉันจะไม่สะท้อนมันที่นี่เพราะฉันไม่เห็นด้วยกับมัน มากยิ่งขึ้นดังนั้นแม้ว่าฉันจะไม่เห็นด้วยกับมลพิษรูปแบบของคุณด้วยวิธีการนำเสนอในขณะที่คำตอบของแซม

หากคุณคิดว่า“ แต่ฉันต้องการสิ่งนี้จริงๆเพื่อเขียนto_csv& to_pdfวิธีการในแบบจำลองของฉัน!” แสดงว่าหลักฐานทั้งหมดของคุณไม่ถูกต้องเพราะคุณไม่มีto_htmlวิธีใช่ไหม แต่วัตถุของคุณมักแสดงผลเป็น HTML ลองสร้างคลาสใหม่สำหรับสร้างผลลัพธ์ของคุณแทนที่จะทำให้โมเดลข้อมูลของคุณรู้ว่า CSV คืออะไร ( เพราะไม่ควรทำ )

สำหรับการใช้ตัวช่วยสำหรับข้อผิดพลาดการตรวจสอบความถูกต้องของ ActiveModel ในโมเดลฉันขอโทษ แต่ ActiveModel / Rails ทำให้เราทุกคนพลาดที่นั่นด้วยการบังคับให้ข้อความแสดงข้อผิดพลาดรับรู้ในชั้นข้อมูลแทนที่จะส่งกลับแนวคิดเชิงความหมายของข้อผิดพลาดให้เป็น ตระหนัก later- ถอนหายใจ คุณสามารถแก้ไขสิ่งนี้ได้ แต่โดยพื้นฐานแล้วหมายความว่าจะไม่ใช้ 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

44
นี่เป็นคำแนะนำที่ดี แต่เป็นคำตอบที่ไม่ดีเพราะไม่สามารถแก้ปัญหาได้
Jaryl

21
มีหลายกรณีที่นี่ไม่ใช่คำตอบที่ดีเช่นตอนนี้ฉันกำลังซื้อรายงาน csv และจำเป็นต้องใช้วิธีนี้ในวิธี to_csv ในชั้นเรียนที่จะไม่เห็นมุมมอง เพียงแค่การแตกหน่ออุดมคติการเขียนโปรแกรมไม่ได้เป็นประโยชน์เสมอไป
nitecoder

1
ใช่สิ่งที่ nitecoder พูด ฉันพบปัญหาเดียวกัน ฉันกำลังสร้างรายงาน PDF และต้องการจัดรูปแบบหมายเลขโทรศัพท์ให้สวยงาม
James Adam

3
@maurice มันเป็นทางลาดที่ลื่นตั้งแต่“ ดีแค่นี้” ไปจนถึงแบบป่อง ตัวช่วยแอพใน Rails เป็นลิ้นชักเก็บขยะผู้นำเสนอ / มุมมองโมเดลจะจัดการได้ง่ายกว่า ฉันไม่เห็นการสร้างข้อมูลสำหรับการรายงานและการสร้าง (HTML | รูปแบบไฟล์ PDF | CSV. | ฯลฯ ) มุมมองของข้อมูลที่เป็นความรับผิดชอบใด ๆ เดียวมากกว่าที่ฉันทำเช่นบุคคลและหน้าคนแสดง HTML
Andrew Marshall

184

ฉันเห็นด้วยกับพวกคุณทุกคนว่านี่อาจเป็นการทำลายรูปแบบ MVC แต่มีเหตุผลเสมอที่จะทำลายรูปแบบในกรณีของฉันฉันต้องการวิธีการจัดรูปแบบสกุลเงินเหล่านี้เพื่อใช้ในตัวกรองเทมเพลต ( Liquidในกรณีของฉัน)

ในตอนท้ายฉันพบว่าฉันสามารถเข้าถึงวิธีการจัดรูปแบบสกุลเงินเหล่านี้ได้โดยใช้สิ่งต่างๆดังนี้:

ActionController::Base.helpers.number_to_currency

6
นี่เป็นสิ่งที่ดีแม้ว่าจะมีวิธีที่สะอาดกว่าเล็กน้อย ดูhttp://railscasts.com/episodes/132-helpers-outside-views
user664833

4
ติดตามความคิดเห็น Yay ใน RailsCasts: ใน Rails 3 ในปี 2013 การใช้ View helper ใน Controller จะทำเช่น view_context.number_to_currency (จำนวน)
olleolleolle

3
คุณเคยคิดเกี่ยวกับการใช้อัญมณี "เงิน" หรือไม่? เนื่องจากอ็อบเจ็กต์ money จัดเตรียมเมธอด format () และคุณสามารถเรียกใช้ในโมเดลคอนโทรลเลอร์หรือมุมมอง
Zack Xu

74

ฉันรู้ว่ากระทู้นี้เก่ามาก แต่มีใครบางคนสามารถหาวิธีแก้ปัญหานี้ได้ใน Rails 4+ นักพัฒนาได้เพิ่ม ActiveSupport :: NumberHelper ซึ่งสามารถใช้งานได้โดยไม่ต้องเข้าถึงดูโมดูล / คลาสที่เกี่ยวข้องโดยใช้:

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

วิธีนี้ใช้ได้ผลสำหรับฉันเมื่อฉันต้องการทดลองกับพฤติกรรมของnumber_to_percentageในคอนโซล Rails ขอบคุณ!
Jon Schneider

28

คุณต้องรวม 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

สิ่งนี้ได้ผลสำหรับฉัน ฉันไม่คิดว่ามันสมเหตุสมผลที่จะปฏิบัติตาม MVC (หรือหลักการใด ๆ ) เสมอไปหากวิธีการแก้ปัญหาที่ละเมิดหลักการนั้นดีกว่าวิธีที่ปฏิบัติตามอย่างชัดเจน
Jason Swett

2
ไม่แนะนำให้ใช้แนวทางนี้ มันเพิ่มวิธีการมากมายที่คุณไม่ต้องการและมันทำให้เนมสเปซของคุณกระจัดกระจายมันอาจจะเขียนทับวิธีการบางอย่างและโมดูลตัวช่วยบางตัวต้องอาศัยโมดูลตัวช่วยอื่น ๆ (ดังนั้นคุณอาจต้องรวมหลายโมดูล) จึงทำให้ปัญหานี้ ยิ่งเลวร้ายลง. สำหรับคำอธิบายและแนวทางที่ดีกว่าโปรดดู: http://railscasts.com/episodes/132-helpers-outside-views
user664833

6

เมื่อปิดการ@fguillenตอบสนองของ Piggyback แล้วฉันต้องการแทนที่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 นั้นดีแม้ว่านี่จะเป็นวิธีการที่สะอาดกว่าเล็กน้อยโดยเฉพาะอย่างยิ่งเนื่องจากคำถามดังกล่าวมีการอ้างอิงถึงสอง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

โดยทั่วไปเมธอด Helper จะใช้สำหรับ View files การใช้วิธีการเหล่านี้ในคลาส Model ไม่ใช่แนวทางปฏิบัติที่ดี แต่ถ้าคุณต้องการใช้แล้วคำตอบของ Sam ก็โอเค หรือฉันขอแนะนำให้คุณเขียนวิธีการของคุณเอง


2
นี่ไม่ใช่คำตอบ
Bonifacio2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.