views:

726

answers:

4

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?

+3  A: 

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
+2  A: 

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
Thanks for explaining the recursive call bit to me.
Gav
A: 

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
+1  A: 

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