Як використовувати допоміжний метод “number_to_currency” у моделі, а не подання?


93

Я хотів би використовувати 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 'для # <Завдання: 0x311eb00>

Будь-які ідеї, як змусити це працювати?

Відповіді:


103

Він недоступний, оскільки його використання в моделі (як правило) порушує MVC (і, здається, у вашому випадку). Ви берете дані та маніпулюєте ними для презентації. Це, за визначенням, належить на думку, а не моделі.

Ось кілька рішень:

  • Використовуйте презентатор або об’єкт перегляду моделі для посередництва між моделлю та видом. Це майже напевно вимагає більше початкової роботи, ніж інші рішення, але майже завжди є кращим дизайном. Використання помічників у презентаторі / моделі перегляду не порушує MVC, оскільки вони перебувають у шарі перегляду, замінюючи традиційні власні помічники Rails та заповнені логікою подання.

  • Явно include ActionView::Helpers::NumberHelperв JobsHelperзамість того, щоб залежати від Rails, щоб магічно завантажив його для вас. Це все ще не чудово, оскільки вам не слід отримувати доступ до помічника від моделі.

  • Порушити MVC та SRP . Дивіться відповідь fguillen, як це зробити. Я не буду повторювати це тут, бо я з цим не згоден. Навіть більше того, я не погоджуюсь із забрудненням вашої моделі методами презентації, як у відповіді Сема .

Якщо ви думаєте "але мені це дійсно потрібно, щоб написати мої to_csv& to_pdfметоди в моїй моделі!", То вся ваша передумова помилкова - зрештою, у вас немає to_htmlметоду, правда? І все ж ваш об'єкт дуже часто відображається як HTML. Подумайте про створення нового класу для генерування результатів, а не про те, щоб ваша модель даних знала, що таке CSV ( оскільки вона не повинна ).

Що стосується використання помічників для помилок валідації ActiveModel в моделі, ну, вибачте, але ActiveModel / Rails нас усіх зіпсувало, змусивши реалізувати повідомлення про помилки на рівні даних, а не повертаючи семантичну ідею помилки. усвідомив пізніше— зітхання . Ви можете обійти це, але це в основному означає більше не використовувати ActiveModel :: Помилки. Я це зробив, це працює добре.

Окрім того, ось корисний спосіб включити помічників у презентатор / модель перегляду без забруднення її набору методів (оскільки можливість робити, наприклад, 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

43
Це хороша порада, але це погана відповідь, оскільки це не вирішує питання.
Jaryl

21
Бувають випадки, коли це не найкраща відповідь, наприклад зараз, коли я купую звіт csv і мені потрібно щось подібне використовувати у методі to_csv у класі, який ніколи не побачить подання. Просто проростання ідеалів програмування не завжди корисно.
nitecoder

1
Так, що сказав nitecoder. Я стикаюся з тим самим питанням. Я створюю звіти у форматі PDF і просто хочу гарно відформатувати номер телефону.
James Adam

3
@maurice Це слизький схил від "ну лише цього" до роздутої моделі. Помічники додатків у Rails - це смітник, презентаторами / моделями перегляду легше керувати. Я не бачу створення даних для звіту та створення (html | pdf | csv | і т. Д.) Подання цих даних як єдину відповідальність, ніж я, наприклад, для особи та сторінки HTML-сторінки.
Ендрю Маршалл,

185

Я погоджуюся з усіма вами, що це може порушити шаблон 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_context.number_to_currency (сума)
olleolleolle

3
Ви задумувались над використанням самоцвіту "гроші"? Оскільки грошовий об’єкт надає метод format (), ви можете викликати його в моделі, контролері чи поданні.
Zack Xu

71

Я знаю, що ця тема дуже стара, але хтось може шукати рішення цієї проблеми в Rails 4+. Розробники додали ActiveSupport :: NumberHelper, який можна використовувати без доступу до модулів / класів, що стосуються перегляду, використовуючи:

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

Цей підхід спрацював для мене, коли я хотів поекспериментувати з поведінкою number_to_percentageконсолі Rails. Дякую!
Джон Шнайдер

27

Вам також потрібно включити 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помічника.
Міша Moroshko

1
Корисно, якщо ви схожий на Florent2 і вам потрібно додати його як частину повідомлення про перевірку. Дякую Сем.
RyanJM

Це спрацювало для мене. Не думаю, що має сенс завжди дотримуватися MVC (або будь-якого принципу), якщо рішення, яке порушує цей принцип, явно краще, ніж рішення, яке його дотримується.
Jason Swett

2
Такий підхід не рекомендується. Він додає багато методів, які вам не потрібні, і захаращує ваш простір імен, може перезаписати деякі методи, а деякі допоміжні модулі покладаються на інші допоміжні модулі (тому вам може знадобитися включити декілька модулів), тим самим створюючи проблему навіть гірше. Пояснення та кращий підхід див .: http://railscasts.com/episodes/132-helpers-outside-views
user664833

6

Відхилившись від @fguillenвідповіді, я хотів замінити 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. Спочатку я продемонструю, використовуючи код Райана Бейтса ( 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

EDIT: Схоже, прийнята відповідь в основному пропонувала зробити щось подібне. Але так, ви хочете використовувати декоратори. Ось чудова серія підручників, яка допоможе вам зрозуміти більше:

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

PS - @ excid3 Я приймаю місяці безкоштовного членства LOL



-5

Допоміжні методи зазвичай використовуються для перегляду файлів. Використовувати ці методи в класі Model не є доброю практикою. Але якщо ви хочете скористатися, тоді відповідь Сема - це нормально. АБО я пропоную вам написати власний власний метод.


2
Це не відповідь.
Bonifacio2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.