12

I have code similar to:

number_to_currency(line_item.price, :unit => "£")

littering my views in various models. Since my application deals only in GBP (£), should I not move this into each of my models so that line_item.price returns the string as it should be (i.e. number_to_currency(line_item.price, :unit => "£") and line_item.price are the same. I'm thinking that to do this I should:

def price
 number_to_currency(self.price, :unit => "£")
end

but this doesn't work. If price is already defined in the model, then Rails reports 'stack level too deep', when I change def price to def amount, then it complains that number_to_currency is not defined?

Gav
  • 11,062
  • 7
  • 33
  • 35

6 Answers6

42

If you want to change the default for your whole application, you can edit config/locales/en.yml

Mine looks like this:

# Sample localization file for English. Add more files in this directory for other locales.
# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
"en":
  number:
    currency:
        format:
            format: "%u%n"
            unit: "£"
            # These three are to override number.format and are optional
            separator: "."
            delimiter: ","
            precision: 2

Everything except the unit is optional and will fall back to the default, but I put it in so I know what values I can change. you could also use the £ sign instead of £.

Josh
  • 8,329
  • 4
  • 36
  • 33
  • Thanks for the tip; the Rails API documentation for `ActionView::Helpers::NumberHelper number_to_currency()` gives an example where setting `:locale => "fr"` changes the separator and delimiter and currency symbol position. This wasn't working for me, but by adding a `config/locales/fr.yml` with settings like the above, it started working. – bjnord Jun 26 '11 at 01:16
  • 2
    I'd always used a custom helper, similar to how Larry K's accepted answer describes, but this is a lot cleaner. – Paul Sturgess Aug 31 '11 at 07:31
13

number_to_currency is a view helper, so it is not available in models.

You could save some key strokes by defining your own helper in application_helper.rb (so it is available to all views). Eg

def quid(price)
  number_to_currency(price, :unit => "£")
end

Then call it in views:

quid(line_item.price)
Larry K
  • 47,808
  • 15
  • 87
  • 140
6

The reason for the stack level too deep error is that when you say self.price in the price method you are creating an infinite recursive call to your price method as you have now overridden the normal accessor method. To avoid this you would need to access the value of the price field using the attributes hash. e.g. something like:

def price
 number_to_currency(attributes['price'], :unit => "£")
end

except for the fact that number_to_currency is not available in model code for the reason Larry K describes.

mikej
  • 65,295
  • 17
  • 152
  • 131
2

Here was my approach to this problem ..

# /RAILS_ROOT/lib/app_name/currency_helper.rb
module AppName
  module CurrencyHelper    

    include ActionView::Helpers::NumberHelper

    def number_to_currency_with_pound(amount, options = {})
      options.reverse_merge!({ :unit => '£' })
      number_to_currency_without_pound(amount, options)
    end

    alias_method_chain :number_to_currency, :pound

  end
end

in your models you can do this (and you won't be polluting your model with methods you aren't going to use)

class Album < ActiveRecord::Base
  include AppName::CurrencyHelper

  def price
    currency_to_number(amount)
  end
end

then for your views to all be updated include the module in one of your app helpers

module ApplicationHelper
   # change default currency formatting to pounds..
   include AppName::CurrencyHelper
end

Now everywhere you use the number to currency helper it will be formatted with a pound symbol, but you also have all the flexiblity of the original rails method so you can pass in the options as you did before ..

number_to_currency(amount, :unit => '$')

will convert it back to a dollar symbol.

Jason Cale
  • 31
  • 1
1

The other answer regarding making another helper method quid(price) to simplify the repetition is probably the best approach.. however.. if you REALLY want to access view helpers in the model you can do something like:

# /RAILS_ROOT/lib/your_namespace/helper.rb
#
# Need to access helpers in the model?
# YourNamespace::Helper.instance.helper_method_name
module YourNamespace
  class Helper
    include Singleton
    include ActionView::Helpers
  end
end

then you should be able to do this in the model class:

def price
  helper = YourNamespace::Helper.instance
  helper.number_to_currency(read_attribute('price'), :unit => "£")
end
Mark Connell
  • 3,739
  • 1
  • 16
  • 11
1

As of Rails 3

As Larry K describes but with this edit:

def quid(price)
   number_to_currency(price, :unit => "&pound;")
 end
user512087
  • 11
  • 1