A simple approach to currency formatting in Rails - ruby-on-rails

I spent most part of today trying to figure out how to give my integer fields a currency format and was bombarded with a myriad of different approaches from using Money, Rails-Money gems to using custom helper methods or locales.
You can achieve this by using the number_to_currency method. Say you
have the following view:
view.html.erb
<ul>
<li><%= client.payment %></li>
</ul>
Assuming the column payments is of type integer, this will print out just that, a number with no formatting. Ex: 5523
Now add the number_to_currency method and specify which currency unit
you'd like to use (can be any)
<ul>
<li><%= number_to_currency(client.payment, :unit => "$") %></li>
</ul>
Now we get something like this: 5.523.00$
The number_to_currency method helper has some options by default, one
of those is that it inverts (as opposed to common practice) the usage
of comma and period. These can be modified by adding the options
:separator and :delimiter and give them the values you'd like as is
below.
<ul>
<li><%= number_to_currency(client.payment, :unit => "$", :separator => ".", :delimiter => ",") %></li>
</ul>
These are the available options for the number_to_currency method helper (RubyOnRailsAPI):
:locale - Sets the locale to be used for formatting (defaults to current locale).
:precision - Sets the level of precision (defaults to 2).
:unit - Sets the denomination of the currency (defaults to “$”).
:separator - Sets the separator between the units (defaults to “.”).
:delimiter - Sets the thousands delimiter (defaults to “,”).
:format - Sets the format for non-negative numbers (defaults to “%u%n”). Fields are %u for the currency, and %n for the number.
:negative_format - Sets the format for negative numbers (defaults to prepending a hyphen to the formatted number given by :format). Accepts the same fields than :format, except %n is here the absolute value of the number.
:raise - If true, raises InvalidNumberError when the argument is invalid.

You can do it a few different ways. Personally I would recommend doing this in a decorator (https://github.com/drapergem/draper) to remove some logic and formatting out of the view.
As mentioned in a prior answer you can use number_to_currency and not use other options to get the correct formatting you are looking for. It can take an integer, float, or string.
number_to_currency 10000
=> "$10,000.00"
Another way is to use money-rails (https://github.com/RubyMoney/money-rails) if you want to deal with a Money object:
money = Money.new(10000)
=> #<Money fractional:10000 currency:USD>
humanized_money_with_symbol money
=> "$10,000.00"

Have you tried using it without any options?
number_to_currency(client.payment)
It should default to $ and has worked fine for me without any options.

Related

Formatting credit card number in a number_field_tag [duplicate]

I would like to make editing form fields as user-friendly as possible. For example, for numeric values, I would like the field to be displayed with commas (like number_with_precision).
This is easy enough on the display side, but what about editing? Is there a good way to do this?
I am using the Rails FormBuilder. Upon investigation, I found that it uses InstanceTag, which gets the values for fields by using <attribute>_value_before_type_cast which means overriding <attribute> won't get called.
The best I have come up with so far is something like this:
<%= f.text_field :my_attribute, :value => number_with_precision(f.object.my_attribute) %>
Or my_attribute could return the formatted value, like this:
def my_attribute
ApplicationController.helpers.number_with_precision(read_attribute(:my_attribute))
end
But you still have to use :value
<%= f.text_field :my_attribute, :value => f.object.my_attribute %>
This seems like a lot of work.
I prefer your first answer, with the formatting being done in the view. However, if you want to perform the formatting in the model, you can use wrapper methods for the getter and setter, and avoid having to use the :value option entirely.
You'd end up with something like this.
def my_attribute_string
foo_formatter(myattribute)
end
def my_attribute_string=(s)
# Parse "s" or do whatever you need to with it, then set your real attribute.
end
<%= f.text_field :my_attribute_string %>
Railscasts covered this with a Time object in a text_field in episode #32. The really clever part of this is how they handle validation errors. It's worth watching the episode for that alone.
This is an old question, but in case anyone comes across this you could use the number_to_X helpers. They have all of the attributes you could ever want for displaying your edit value:
<%= f.text_field :my_number, :value => number_to_human(f.object.my_number, :separator => '', :unit => '', :delimiter => '', :precision => 0) %>
There are more attributes available too: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
If you want a format to be created or maintained during editing, you will need to add Javascript to implement "masks." Here is a demo.
It was the first hit in these results.
You can use the number_format plugin. By specifying a number_format for an existing numeric attribute inside your model, the attribute will now appear as formatted to Rails in all forms and views. It will also be parsed back from that format (when assigned via forms) prior to insertion into the database. (The plugin also creates purely numeric unformatted_<attribute-name> accessors which can continue to be used for arithmetic, or for direct numerical assignment or retrieval by you for seamless integration.)
class MyModel < ActiveRecord::Base
# this model has the balance attribute, which we
# want to display using formatting in views,
# although it is stored as a numeric in the database
number_format :balance,
:precision => 2,
:delimiter => ',',
:strip_trailing_zeros => false
def increment_balance
unformatted_balance += 10
end
You can also combine the above with a Javascript solution, which can force the user to maintain the decimal point and thousands separators in place while editing, although this is really not necessary.
I have done something similar. We format times and lengths using a custom form builder. It makes use of the existing text_field, but wraps it so the value can be customized:
class SuperFormBuilder < ActionView::Helpers::FormBuilder
include ApplicationHelper
include FormHelper
include ActionView::Helpers::TagHelper
include ActionView::Helpers::FormTagHelper
def length_field(label,*args)
scale = 'medium'
args.each do |v|
if v.has_key?(:scale)
scale = v[:scale]
v.delete(:scale)
end
end
value = length_conversion(#object.send(label.to_sym),scale)
options = (args.length > 0) ? args.pop : {}
return has_error(label, text_field_tag(field_name(label),value,*args) + ' ' + length_unit(scale))
end
private
def field_name(label)
return #object_name + "[#{label}]"
end
def has_error(label, output)
return "<div class='fieldWithErrors'>#{output}</div>" if #object.errors[label]
return output
end
And it is used like this:
<%= form_for( #section, {:action => 'save', :id => #section.id}, :builder => SuperFormBuilder) do |sf| %>
<%= sf.length_field :feed_size_min_w, :size => 3, :scale => 'small' %>
<% end %>
The end result is a value in the appropriate unit based off their choice on system (Metric, Imperial) and scale IE small = inches or millimeters.
I basically copied the text_field method from the existing form builder, which uses the text_field_tag itself.
There are two gotchas: 1) Knowing the name of the object field and how to access the object to get the value which you want to format. 2) Getting the name right so when the form is submitted it is part of the correct params hash.
The form builder is given a class variable #object. You can get the value of the field using the .send method. In my case I send the label :feed_size_min_w to the #object and get its length back. I then convert it to my desired format, and give it to the text_field_tag.
The name of the field is key to having it end up in the params hash, in my instance the params[:sections] one. I made a little helper function called field_name that takes care of this.
Finally the has_error wraps the field in an error div if there are errors on that label.
I needed "nicer" format on some specified text fields, resolved it by adding this to my initializers. Seems to work nicely on Rails ~= 5.2 and it should be easy to customize.
class ActionView::Helpers::Tags::TextField
private
def value_before_type_cast # override method in ActionView::Helpers::Tags::Base
v = super
# format as you like, when you like
if #options.delete(:nice_decimal)
v = v.to_s.gsub('.', ',') if v.is_a?(BigDecimal)
end
v
end
end
Usage in form f
<%= f.text_field :foo, nice_decimal: true %>

Rails saving german price to database

i want to save a price like this "199,99" as a float or decimal to the database.
i have tried to replace the "," with "." in the model with a before filter.
but the price is passed to the before filter as "199.00".
is there a way to convert that in the model class?
I know how to do this in the controller, but how can i do it in the model?
thanks for your help.
I believe the correct way of doing this is keep the db value as it is, and in your view layer create a helper method to display the amount with your preferred formatting
so basic idea is, in your application helper
module ApplicationHelper
#p for price, just to keep things simple
def p(amount)
amount.gsub(".",",")
end
end
and in your view
<%= p(object.price) %>
and one more disadvantage of your approach is , if you save the price as 123,34, you will not be able to do any calculations later.
HTH
You should use number_to_currency helper and appropriate locales
Or if you don't want to mess with locales declare separator explicitly.
number_to_currency(value_from_db, :unit => "$;", :separator => ".", :delimiter => " ", :format => "%u %n")

Price gets multiplied by 100 when saving product in Spree (RoR)

I installed the online shopping framework Spree on top of Rails 3.1.3 and Ruby 1.9.3. I also use the Spree_i18n gem to localize the shop. Now, whenever I save a product, the price is multiplied by 100.
For example, in the admin area, I type the price 3.20. That results in the value 320. If I save again, it changes to 32000 and so on.
Here is my localized de_numbers.yml for reference:
---
de:
number:
currency:
format:
format: "%u%n"
unit: "€"
precision: 2
separator: '.'
delimiter: ','
I can not think of anything unusual in my setup so I wonder why this is not a common problem. Any help will be appreciated.
EDIT:
spree-core: the Product form is not handling displaying product.price and product.cost_price with regards to I18n / localization. To fix this to work for you, you'll need to modify the core. I'm going to post to the Spree Core team about this, but in the interim, I've tested this fix and it should work.
In /gems/spree_core-1.0.0/app/views/spree/admin/products/_form.html.erb, you will need to modify these lines:
<%= f.text_field :price, :value => number_with_precision(#product.price, :precision => 2) %>
to be this:
<%= f.text_field :price, :value => number_with_precision(#product.price, :precision => I18n.t('number.currency.format.precision'), :separator => I18n.t('number.currency.format.separator'), :delimiter => I18n.t('number.currency.format.delimiter')) %>
and this:
<%= f.text_field :cost_price, :value => number_with_precision(#product.cost_price, :precision => 2) %>
to be this:
<%= f.text_field :cost_price, :value => number_with_precision(#product.cost_price, :precision => I18n.t('number.currency.format.precision'), :separator => I18n.t('number.currency.format.separator'), :delimiter => I18n.t('number.currency.format.delimiter')) %>
Essentially, we're making it handle I18n potential values.
ORIGINAL:
I've duplicated your file exactly, and tried a few tests to recreate this (create a new product, new product variant, change the product price, cost price, etc.). To re-create this, you need to create a de_numbers.yml, and flip your localization to be 'de' in the Spree initializer with "config.default_locale = 'de'"
Here are some suggested fixes:
make sure you run bundle install
in your Gemfile, make sure you're using the latest version of i18n (
gem 'spree_i18n', :git => 'git://github.com/spree/spree_i18n.git')
fix your whitespace to be 2 spaces, instead of tabs (this is potentially a white-space issue where it can't read your i18n values)
Go into the rails console, and log out the values (i.e.
I18n.t('number.currency.format.unit')
Try getting this to work in "en" locale first and then "de".
Put your values into the "de.yml" or "en.yml" first and see if they work before putting into a "de_currency.yml" file.
I'm guessing you've transposed the meanings of your separator and delimiter characters. The setup looks correct, so my thinking is that the price needs to be entered as
3,20
Rather than
3.20
This discussion of currency formatting, although not specific to Ruby development, may provide additional insight.

How can I limit the amount of words displayed from a Ruby string?

the Ruby code is below:
<%= product.advert_text -%>
What script can limit the amount of words?
Since you are working with Rails, you can use the truncate method.
So, something like:
<%= truncate(product.advert_text, :length => 20) -%>
The :length option sets the # of characters to truncate at & will add ellipses at the end (you can override the default ellipses with the :omission option - see the documentation on my link).
If you want to limit to a certain number of words, the easiest way is going to be to split on space and then join the desired number of words back into a string.
<%=product.advert_text.split.slice(0, limit).join(" ") -%>

How can I format the value shown in a Rails edit field?

I would like to make editing form fields as user-friendly as possible. For example, for numeric values, I would like the field to be displayed with commas (like number_with_precision).
This is easy enough on the display side, but what about editing? Is there a good way to do this?
I am using the Rails FormBuilder. Upon investigation, I found that it uses InstanceTag, which gets the values for fields by using <attribute>_value_before_type_cast which means overriding <attribute> won't get called.
The best I have come up with so far is something like this:
<%= f.text_field :my_attribute, :value => number_with_precision(f.object.my_attribute) %>
Or my_attribute could return the formatted value, like this:
def my_attribute
ApplicationController.helpers.number_with_precision(read_attribute(:my_attribute))
end
But you still have to use :value
<%= f.text_field :my_attribute, :value => f.object.my_attribute %>
This seems like a lot of work.
I prefer your first answer, with the formatting being done in the view. However, if you want to perform the formatting in the model, you can use wrapper methods for the getter and setter, and avoid having to use the :value option entirely.
You'd end up with something like this.
def my_attribute_string
foo_formatter(myattribute)
end
def my_attribute_string=(s)
# Parse "s" or do whatever you need to with it, then set your real attribute.
end
<%= f.text_field :my_attribute_string %>
Railscasts covered this with a Time object in a text_field in episode #32. The really clever part of this is how they handle validation errors. It's worth watching the episode for that alone.
This is an old question, but in case anyone comes across this you could use the number_to_X helpers. They have all of the attributes you could ever want for displaying your edit value:
<%= f.text_field :my_number, :value => number_to_human(f.object.my_number, :separator => '', :unit => '', :delimiter => '', :precision => 0) %>
There are more attributes available too: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
If you want a format to be created or maintained during editing, you will need to add Javascript to implement "masks." Here is a demo.
It was the first hit in these results.
You can use the number_format plugin. By specifying a number_format for an existing numeric attribute inside your model, the attribute will now appear as formatted to Rails in all forms and views. It will also be parsed back from that format (when assigned via forms) prior to insertion into the database. (The plugin also creates purely numeric unformatted_<attribute-name> accessors which can continue to be used for arithmetic, or for direct numerical assignment or retrieval by you for seamless integration.)
class MyModel < ActiveRecord::Base
# this model has the balance attribute, which we
# want to display using formatting in views,
# although it is stored as a numeric in the database
number_format :balance,
:precision => 2,
:delimiter => ',',
:strip_trailing_zeros => false
def increment_balance
unformatted_balance += 10
end
You can also combine the above with a Javascript solution, which can force the user to maintain the decimal point and thousands separators in place while editing, although this is really not necessary.
I have done something similar. We format times and lengths using a custom form builder. It makes use of the existing text_field, but wraps it so the value can be customized:
class SuperFormBuilder < ActionView::Helpers::FormBuilder
include ApplicationHelper
include FormHelper
include ActionView::Helpers::TagHelper
include ActionView::Helpers::FormTagHelper
def length_field(label,*args)
scale = 'medium'
args.each do |v|
if v.has_key?(:scale)
scale = v[:scale]
v.delete(:scale)
end
end
value = length_conversion(#object.send(label.to_sym),scale)
options = (args.length > 0) ? args.pop : {}
return has_error(label, text_field_tag(field_name(label),value,*args) + ' ' + length_unit(scale))
end
private
def field_name(label)
return #object_name + "[#{label}]"
end
def has_error(label, output)
return "<div class='fieldWithErrors'>#{output}</div>" if #object.errors[label]
return output
end
And it is used like this:
<%= form_for( #section, {:action => 'save', :id => #section.id}, :builder => SuperFormBuilder) do |sf| %>
<%= sf.length_field :feed_size_min_w, :size => 3, :scale => 'small' %>
<% end %>
The end result is a value in the appropriate unit based off their choice on system (Metric, Imperial) and scale IE small = inches or millimeters.
I basically copied the text_field method from the existing form builder, which uses the text_field_tag itself.
There are two gotchas: 1) Knowing the name of the object field and how to access the object to get the value which you want to format. 2) Getting the name right so when the form is submitted it is part of the correct params hash.
The form builder is given a class variable #object. You can get the value of the field using the .send method. In my case I send the label :feed_size_min_w to the #object and get its length back. I then convert it to my desired format, and give it to the text_field_tag.
The name of the field is key to having it end up in the params hash, in my instance the params[:sections] one. I made a little helper function called field_name that takes care of this.
Finally the has_error wraps the field in an error div if there are errors on that label.
I needed "nicer" format on some specified text fields, resolved it by adding this to my initializers. Seems to work nicely on Rails ~= 5.2 and it should be easy to customize.
class ActionView::Helpers::Tags::TextField
private
def value_before_type_cast # override method in ActionView::Helpers::Tags::Base
v = super
# format as you like, when you like
if #options.delete(:nice_decimal)
v = v.to_s.gsub('.', ',') if v.is_a?(BigDecimal)
end
v
end
end
Usage in form f
<%= f.text_field :foo, nice_decimal: true %>

Resources