How to use the helper method number_to_currency in a model rather than a view?

I would like to use the to_dollar method in my model as follows:

 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 

Unfortunately, the number_to_currency method is not recognized here:

undefined method `number_to_currency 'for # <Job: 0x311eb00>

Any ideas how to make it work?

+85
ruby-on-rails
Mar 03 2018-11-11T00:
source share
11 answers

Its not available because its use in the model (as a rule) violates MVC (and this seems to be the case for you). You take data and manipulate it for presentation. This, by definition, belongs to opinion, not to the model.

Here are some solutions:

  • Use the presenter or view model object to mediate between the model and the view. It almost certainly requires more initial work than other solutions, but it is almost always a better design. Using helpers in a presentation / view-model does not violate MVC, as they are at the presentation level, replacing traditional Rails user-defined helpers and filled with presentation logic.

  • Explicitly include ActionView::Helpers::NumberHelper in JobsHelper instead of Rails- JobsHelper so that it magically loads it for you. This is still not very convenient, since you do not need to contact the assistant from the model.

  • Violate MVC and SRP . See fguillens answer for how to do this. I will not repeat it here because I do not agree with this. However, do I disagree with the pollution of your model using presentation methods, as in Sams Answer .

If you think: β€œBut I really need this to write my to_csv and to_pdf in my model!”, Then your whole premise is wrong. After all, you don't have a to_html method, do you? And still your object is very often displayed as HTML. Consider creating a new class to generate output instead of your data model knowing what CSV is ( because it shouldn't ).

Regarding the use of helpers for ActiveModel validation errors in the model, well, I'm sorry, but ActiveModel / Rails pinned us all there, forcing error messages to be implemented in the data layer, instead of returning the semantic idea of ​​the error to be realized later - a sigh. You can get around this, but basically it means that you no longer use ActiveModel :: Errors. Ive done it, it works well.

Alternatively, this is a useful way to include helpers in a presentation / presentation model without polluting its set of methods (because the ability to make, for example, MyPresenterOrViewModel.new.link_to(...) does not make sense):

 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 
+92
Mar 03 2018-11-11T00:
source share

I agree with you that this can break the MVC pattern, but there are always reasons to break the pattern, in my case I need these currency formatting methods to use them in the pattern filter (Liquid in my case).

In the end, I found out that I can access these currency formatting methods using things like this:

 ActionController::Base.helpers.number_to_currency 
+171
May 03 '11 at 8:49
source share

I know this thread is very old, but someone can find a solution to this problem in Rails 4+. The developers added ActiveSupport :: NumberHelper, which can be used without access to view-related modules / classes, using:

 ActiveSupport::NumberHelper.number_to_currency(amount, precision: 0) 
+58
Sep 13 '15 at 10:52
source share

You also need to enable 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 
+25
03 Mar. '11 at 4:45
source share

Refusing the answer from @fguillen , I wanted to override the number_to_currency method in my ApplicationHelper module so that if the value was 0 or blank , it would display a dash instead.

Here is my code if you guys find something like this useful:

 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
Oct 24 '13 at 7:40
source share

You can use view_context.number_to_currency directly from your controller or model.

+4
May 18, '15 at 13:35
source share

@fguillen's path is good, although here's a slightly cleaner approach, especially considering that the question makes two references to to_dollar . I will first demonstrate the Ryan Bates code ( 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 

Pay attention to the helpers.pluralize call. This is possible due to the definition of a method ( def helpers ) that simply returns ActionController::Base.helpers . Therefore helpers.pluralize is short for ActionController::Base.helpers.pluralize . Now you can use helpers.pluralize several times without repeating the paths of the long module.

So, I believe that the answer to this particular question may be:

 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 
+3
Apr 04 '12 at 18:24
source share

This is not a good practice, but it works for me!

for import include ActionView :: Helpers :: NumberHelper in the controller. For example:

 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 

Hope this helps you!

+2
Nov 22 '14 at 3:26
source share

Really surprised that no one talked about using Decorator. Their goal is to solve the problem you are facing, and much more.

https://github.com/drapergem/draper

EDIT: It seems that the accepted answer basically suggested doing something similar. But yes, you want to use decorators. Here is a great series to help you understand more:

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

PS - @ excid3 I accept free months of membership LOL

+2
Jul 13 '17 at 19:15
source share

You can simply include ActiveSupport::NumberHelper if you do not need additional functions defined by ActionView .

https://github.com/rails/rails/blob/44260581bec06e4ce05f3dd838c8b4736fc7eb1d/actionview/lib/action_view/helpers/number_helper.rb#L383

0
Jun 05 '19 at 11:42 on
source share

Helper methods are commonly used to view files. Using these methods in a model class is not recommended. But if you want to use, then Sam’s answer is fine. OR I suggest you write your own custom method.

-four
Mar 03 '11 at 5:11
source share



All Articles