Как использовать вспомогательный метод number_to_currency в модели, а не просмотр?

Я хотел бы использовать метод 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 здесь не распознается:

undefined метод `number_to_currency 'для # < Job: 0x311eb00 >

Любые идеи, как заставить его работать?

Ответ 1

Его недоступно, потому что его использование в модели (как правило) нарушает MVC (и это похоже на ваш случай). Вы берете данные и манипулируете им для презентации. Это, по определению, принадлежит мнению, а не модели.

Вот несколько решений:

  • Используйте объект презентатора или объекта модели представления для посредничества между моделью и представлением. Это почти определенно требует большей начальной работы, чем другие решения, но почти всегда является лучшим дизайном. Использование помощников в презентационной /view -model не нарушает MVC, поскольку они находятся на уровне представления, заменяя традиционные пользовательские помощники Rails и заполненные логикой представления.

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

  • Нарушить MVC и SRP. См. fguillens answer, как это сделать. Я не буду повторять это здесь, потому что я не согласен с этим. Тем не менее, не согласен ли я с загрязнением вашей модели с помощью методов представления, как в Ответ Sams.

Если вы думаете: "Но мне действительно нужно это, чтобы написать мои методы to_csv и to_pdf в моей модели!", тогда вся ваша предпосылка неверна. В конце концов, у вас нет метода to_html, вы? И все же ваш объект очень часто отображается как HTML. Подумайте о создании нового класса для генерации вывода вместо того, чтобы ваша модель данных знала, что такое CSV (потому что он не должен).

Что касается использования помощников для ошибок проверки ActiveModel в модели, ну, извините, но ActiveModel/Rails прикололи нас всех туда, заставляя сообщения об ошибках реализовываться в слое данных, вместо того, чтобы возвращать семантическую идею ошибки для быть осознанным позже - вздох. Вы можете обойти это, но в основном это означает, что больше не использовать ActiveModel:: Errors. Ive сделал это, он работает хорошо.

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

Ответ 2

Я согласен с вами в том, что это может сломать шаблон MVC, но всегда есть причины разбить шаблон, в моем случае мне нужны эти методы форматирования валют для использовать их в фильтре шаблона (Жидкость в моем случае).

В конце я узнал, что могу получить доступ к этим методам форматирования валют, используя такие вещи:

ActionController::Base.helpers.number_to_currency

Ответ 3

Я знаю, что эта ветка очень старая, но кто-то может найти решение этой проблемы в Rails 4+. Разработчики добавили ActiveSupport :: NumberHelper, который можно использовать без доступа к связанным с просмотром модулям/классам, используя:

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

Ответ 4

Вам также необходимо включить 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

Ответ 5

Отказываясь от ответа @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

Ответ 6

Вы можете использовать view_context.number_to_currency непосредственно от вашего контроллера или модели.

Ответ 7

@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

Ответ 8

Это не очень хорошая практика, но она работает для меня!

для импорта включают 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

Надеюсь, это поможет вам!

Ответ 9

Действительно удивил, что никто не говорил об использовании Decorator. Их цель - решить проблему, с которой вы столкнулись, и многое другое.

https://github.com/drapergem/draper

EDIT: Похоже, что принятый ответ в основном предлагал сделать что-то подобное. Но да, вы хотите использовать декораторы. Вот отличный сериал, который поможет вам понять больше:

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

P.S. - @excid3 Я принимаю бесплатные месяцы членства LOL

Ответ 11

Вспомогательные методы обычно используются для просмотра файлов. Использовать эти методы в классе модели не рекомендуется. Но если вы хотите использовать, то ответ Сэма в порядке. ИЛИ Я предлагаю вам написать собственный собственный метод.