rails 3.1 multiple currencies - ruby-on-rails

I have to add more currencies to the app like on this web app http://www.designconnected.com/
as you can see, it converts the price in whatever currency you select and keep it this way.
I've been looking for gems that are out of date already, tutorials couldn't find any and in stackoverflow there are a few questions about it but none of them got what I need.
if any of you know a better gem, recently released... please let me know.
or if there is no gem for it, should I add a currency_id to current_user so the app will show the proper currency for this user.. but then where do I take the currency rates from.. I've been looking for a solution for 3 days now and nothing.
Thank you for any given advice..
this urls have been checked:
https://stackoverflow.com/questions/1368010/rails-currency-gem-or-plugin
Rails 3 - Multiple Currencies
https://github.com/RubyMoney/money
the last one in combination with https://github.com/RubyMoney/google_currency looks like it's the one I need.. but now would be the right time to get a tutorial on how to use this.
Please help with some ideas on how to get it started if there is no way to find/get a full tutorial about this.
Thank you.

https://github.com/RubyMoney/money-rails and https://github.com/RubyMoney/google_currency is the way to go. It's not what I asked or in the question, but anyway it's the closest answer I have right now. These are a few steps I did to get this working:
In gem file:
gem "json" #if you don't have it
gem "money"
gem "google_currency"
Create a file money.rb in config/initializers
require 'money'
require 'money/bank/google_currency'
require 'json'
MultiJson.engine = :json_gem
Money.default_bank = Money::Bank::GoogleCurrency.new
In product.rb (or whatever model you have that needs the price to be converted)
composed_of :price,
:class_name => "Money",
:mapping => [%w(price price), %w(currency currency_as_string)],
:constructor => Proc.new { |price, currency| Money.new(price || 0, currency || Money.default_currency) },
:converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }
And in the view file:
<%= number_to_currency(product.price.exchange_to(:EUR)) %>
For exemple, I have an IT locale (Italian language) - Italy currency now is Euro:
You'll have the prices converted in EUR.. It worked for me really nice, money gem convert the price from USD in EUR using Google_currency, and the locale yml file changes the Currency for this locale so you'll have the price looking like XXX,XX EUR and not $XXX,XX.
To display the right currency for each locale you need to add:
it:
number:
currency:
format:
format: "%n %u"
unit: "EUR"
In it.yml file or for other language you have with currency for that country.

You probably don't need a gem for this. You can make a call directly to google's currency API at any point in your code using the URL's explained here. This could be done in your model or via AJAX directly in the view.
http://motyar.blogspot.com/2011/12/googles-currency-converter-and-json-api.html

Related

Mongoid + RubyMoney

I'm using RubyMoney (money-rails) https://github.com/RubyMoney/money-rails with Mongoid. Something like this:
class Order
include Mongoid::Document
field :order_amount, :type => Money, :default => Money.new(0)
...
end
How can I aggregate on a Money field? For example, when I run Order.sum(:order_amount), I get 0 even if the field has a value like #<Money fractional:5500 currency:USD>. I've also tried Order.sum(:order_amount_cents) and that also returns 0.
This is the generated Ruby MongoDB command:
command={:aggregate=>"orders", :pipeline=>[{"$match"=>{"order_amount"=>{"$nin"=>[nil]}}}, {"$group"=>{"_id"=>"order_amount", "count"=>{"$sum"=>1}, "max"=>{"$max"=>"$order_amount"}, "min"=>{"$min"=>"$order_amount"}, "sum"=>{"$sum"=>"$order_amount"}, "avg"=>{"$avg"=>"$order_amount"}}}]} (2.4292ms)
I've also tried using the moped aggregate command with Order.collection.aggregate and that doesn't seem to work either.
Order.collection.aggregate(
{
'$group' => {
'_id' => '$order_amount',
'totalOrder' => { '$sum' => '$order_amount' }
}
}
)
Any help please?
I just addded an answer to this here but just in case someone else comes to this post as I did, I was able to sum money-rails values using something like this:
Order.sum("order_amount.cents")
This will return just the cents values from the Money-rails structure:
{ cents: < value >, currency_iso: < value > }
So in order to get the sum in money-rails format you would use:
Money.new(Order.sum("order_amount.cents"))
Please note that this is when using mongoid v3, because of how money-rails is serialized.
I believe that with previous versions of mongoid, money-rails objects were serialized using "fractional" instead of cents, so you would have to use:
Money.new(Order.sum("order_amount.fractional"))
Hope that helps!

Is there a Rails way to translate model names for usage in title (i.e. titleize?)

I18n provided a convenient way to translate model names. Take the example from Rails Guide
I18n.backend.store_translations :en, inbox: {
one: 'one message',
other: '%{count} messages'
}
I18n.translate :inbox, count: 2
# => '2 messages'
I18n.translate :inbox, count: 1
# => 'one message'
This is good enough if I want to show "one message", "2 messages", and etc.
But what if I also want to show "Message" (for example, in the menu)? Do I have to create another totally different entry in the I18n files? Is there some Rails way to do this? like:
I18n.backend.store_translations :en, inbox: {
title: 'Message'
zero: 'no messages'
one: 'one message',
other: '%{count} messages'
}
Check out titleize for making it look like a title, and pluralize for making it pluralize.
I think Rails mb_chars will do this with unicode rules.
I was surprised to see you needed this, because I'm used to thinking of mb_chars in Rails as being a leftover from ruby 1.8 days which no longer has any use. But it looks like this is one place where default Rails titleize does not do the right i18n thing, but it will if you transform the string to an mb_chars proxy first.
irb(main):019:0> "\u01C9ab".titlecase # "ljab"
=> "ljab" # wrong
irb(main):021:0> "\u01C9ab".mb_chars.titlecase
=> LJab
# right. Or at least closer and as close to right as Rails is gonna get, I'm not
# sure if it should really be "Ljab", which is actually different.
# I'm out of my league here.
I'm not really sure if the Rails devs would consider this a bug or not.
The pluralize helper accepts the plural form as third parameter.
pluralize 3, 'person', 'people'
You can build a helper for your edge cases.

Where should I store a list of select options in Rails 3?

Using Rails 3.1.3 and Ruby 1.9.3.
I want to give the user a list of possible date/time formats. The user's selection will be stored in the Users table. Date/time values are then formatted using the I18n.localize function. I actually have 10 formats; here by way of example are the first two:
config/locales/datetime.en.yml
en:
time:
format_labels:
mdyslash12: mm/dd/yyyy - hh:mm am (12-hour)
mdyslash24: mm/dd/yyyy - hh:mm (24-hour)
formats:
mdyslash12: ! '%m/%d/%Y %I:%M%p'
mdyslash24: ! '%m/%d/%Y %H:%M'
My question is where to store the list of possible date/time formats. I've identified three possibilities.
1. List options as a CONSTANT in model:
app/models/user.rb
DATETIME_FORMATS = %w[mdyslash12 mdyslash24]
validates :datetime_format, :presence => true,
:inclusion => { :in => DATETIME_FORMATS }
2. Create an application constant and validate against that:
config/initializers/constants.rb
Rails.configuration.datetime_formats = "mdyslash12 mdyslash24"
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => Rails.application.config.datetime_formats.split(" ") }
3. Validate directly against the locale file:
app/models/user.rb
validates :datetime_format, :presence => true,
:inclusion => { :in => (I18n.t 'time.format_labels').stringify_keys.keys }
This option uses a feature that is new to me: I18n.t 'time.format_labels' returns a hash of ALL keys and values from that branch of the locale file. The hash keys are symbols, so to get a string array, I call stringify_keys to convert the symbols to strings, then keys to give me only the keys (no values).
Option #3 is the DRYest in that I don't have to list the possible values in two places. But it doesn't feel quite right to depend on the locale file for the discreet list of possible date/time formats.
What would you recommend? One of these options? Something else?
I'd go with option 1 to start with, since it's simple, clear, and fairly DRY. I might refactor to option 2 if I ended up needing that constant in another model.
Option 3 has the potential to behave differently based on the locale, so I don't like that. If you end up forgetting to specify your format labels in a new locale, your selection list might end up being empty (or if there's a typo in one locale, it might take longer to notice, since the typo would be treated as valid for that locale). Regardless it's probably a good idea to unittest this in all your supported locales.

store utf8 strings in db, but validate and parameterize transliteriated

In my app there's a number of routes like :user_login/resource/resource_name
example:
:vlad/photo_albums/my-first-album
Users should be able to create an albums with their native language namings ( in my case its russian ). But if user named his album as, for example, "Привет, Мир!" ( which is "Hello World!" in English), I want to use a string where all letters of the Russian alphabet are replaced by similar-sounding Latin in a resource link. E.g, user provides album title "Привет Мир!" and the corresponding link looks like 'vlad/photo_albums/privet-mir'.
I've made all necessary methods to transform russian to latin, but now I'm trying to find the best way to arrange all this.
First issue is that I need to find album by it's title:
#album = #user.albums.
where( :title => params[:album_title] ).first
redirect_to user_albums_path(#user) unless #album
I would really want to avoid using anything but latin in my sql statements.
Second issue is that I don't want to run validations on non-latin string ( should I be? ) so I want to latinize and parameterize it before validation, but still save the original string if it's latinized version passed the validation:
validates :title, :presence => true, :length => { :within => (2..25) },
:allow_blank => false, :uniqueness => { :scope => :user_id }
What I was thinking about to accomplish this were hash serialization like {:latin_version => ..., :original_version => ..} or separate yaml file concepts.
I need your thoughts on how to arrange this properly and what would be the most elegant way. Or am I to pedantic about it? Would it be fine to just store/search for/validate/display non-latin characters?
It's completely fine to store, validate and search non-latin characters. Most Ruby on Rails companies that provide multilingual and international versions of their applications use UTF-8 in the application and database layers. UTF-8 can be properly parameterized, displayed and validated in Ruby on Rails and all major browsers so you should not see any issues there. The best way to handle this is to set your database encoding and/or table string encoding to UTF-8 and then your Ruby on Rails encoding in application.rb:
config.encoding = "UTF-8"
If you are using MySQL or Postgres you will probably also want to be explicit about the database encoding in your database.yml file:
development:
adapter: mysql2
database: development_db
user: root
password:
encoding: utf8

What is the best method of handling currency/money?

I'm working on a very basic shopping cart system.
I have a table items that has a column price of type integer.
I'm having trouble displaying the price value in my views for prices that include both Euros and cents. Am I missing something obvious as far as handling currency in the Rails framework is concerned?
You'll probably want to use a DECIMAL type in your database. In your migration, do something like this:
# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, :precision => 8, :scale => 2
In Rails, the :decimal type is returned as BigDecimal, which is great for price calculation.
If you insist on using integers, you will have to manually convert to and from BigDecimals everywhere, which will probably just become a pain.
As pointed out by mcl, to print the price, use:
number_to_currency(price, :unit => "€")
#=> €1,234.01
Here's a fine, simple approach that leverages composed_of (part of ActiveRecord, using the ValueObject pattern) and the Money gem
You'll need
The Money gem (version 4.1.0)
A model, for example Product
An integer column in your model (and database), for example :price
Write this in your product.rb file:
class Product > ActiveRecord::Base
composed_of :price,
:class_name => 'Money',
:mapping => %w(price cents),
:converter => Proc.new { |value| Money.new(value) }
# ...
What you'll get:
Without any extra changes, all of your forms will show dollars and cents, but the internal representation is still just cents. The forms will accept values like "$12,034.95" and convert it for you. There's no need to add extra handlers or attributes to your model, or helpers in your view.
product.price = "$12.00" automatically converts to the Money class
product.price.to_s displays a decimal formatted number ("1234.00")
product.price.format displays a properly formatted string for the currency
If you need to send cents (to a payment gateway that wants pennies), product.price.cents.to_s
Currency conversion for free
Common practice for handling currency is to use decimal type.
Here is a simple example from "Agile Web Development with Rails"
add_column :products, :price, :decimal, :precision => 8, :scale => 2
This will allow you to handle prices from -999,999.99 to 999,999.99
You may also want to include a validation in your items like
def validate
errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01
end
to sanity-check your values.
Just a little update and a cohesion of all the answers for some aspiring juniors/beginners in RoR development that will surely come here for some explanations.
Working with money
Use :decimal to store money in the DB, as #molf suggested (and what my company uses as a golden standard when working with money).
# precision is the total number of digits
# scale is the number of digits to the right of the decimal point
add_column :items, :price, :decimal, precision: 8, scale: 2
Few points:
:decimal is going to be used as BigDecimal which solves a lot of issues.
precision and scale should be adjusted, depending on what you are representing
If you work with receiving and sending payments, precision: 8 and scale: 2 gives you 999,999.99 as the highest amount, which is fine in 90% of cases.
If you need to represent the value of a property or a rare car, you should use a higher precision.
If you work with coordinates (longitude and latitude), you will surely need a higher scale.
How to generate a migration
To generate the migration with the above content, run in terminal:
bin/rails g migration AddPriceToItems price:decimal{8-2}
or
bin/rails g migration AddPriceToItems 'price:decimal{5,2}'
as explained in this blog post.
Currency formatting
KISS the extra libraries goodbye and use built-in helpers. Use number_to_currency as #molf and #facundofarias suggested.
To play with number_to_currency helper in Rails console, send a call to the ActiveSupport's NumberHelper class in order to access the helper.
For example:
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
gives the following output
2500000,61€
Check the other options of number_to_currency helper.
Where to put it
You can put it in an application helper and use it inside views for any amount.
module ApplicationHelper
def format_currency(amount)
number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Or you can put it in the Item model as an instance method, and call it where you need to format the price (in views or helpers).
class Item < ActiveRecord::Base
def format_price
number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
And, an example how I use the number_to_currency inside a contrroler (notice the negative_format option, used to represent refunds)
def refund_information
amount_formatted =
ActionController::Base.helpers.number_to_currency(#refund.amount, negative_format: '(%u%n)')
{
# ...
amount_formatted: amount_formatted,
# ...
}
end
If you are using Postgres (and since we're in 2017 now) you might want to give their :money column type a try.
add_column :products, :price, :money, default: 0
Use money-rails gem. It nicely handles money and currencies in your model and also has a bunch of helpers to format your prices.
Using Virtual Attributes (Link to revised(paid) Railscast) you can store your price_in_cents in an integer column and add a virtual attribute price_in_dollars in your product model as a getter and setter.
# Add a price_in_cents integer column
$ rails g migration add_price_in_cents_to_products price_in_cents:integer
# Use virtual attributes in your Product model
# app/models/product.rb
def price_in_dollars
price_in_cents.to_d/100 if price_in_cents
end
def price_in_dollars=(dollars)
self.price_in_cents = dollars.to_d*100 if dollars.present?
end
Source: RailsCasts #016: Virtual Attributes: Virtual attributes are a clean way to add form fields that do not map directly to the database. Here I show how to handle validations, associations, and more.
Definitely integers.
And even though BigDecimal technically exists 1.5 will still give you a pure Float in Ruby.
If someone is using Sequel the migration would look something like:
add_column :products, :price, "decimal(8,2)"
somehow Sequel ignores :precision and :scale
(Sequel Version: sequel (3.39.0, 3.38.0))
My underlying APIs were all using cents to represent money, and I didn't want to change that. Nor was I working with large amounts of money. So I just put this in a helper method:
sprintf("%03d", amount).insert(-3, ".")
That converts the integer to a string with at least three digits (adding leading zeroes if necessary), then inserts a decimal point before the last two digits, never using a Float. From there you can add whatever currency symbols are appropriate for your use case.
It's definitely quick and dirty, but sometimes that's just fine!
I am using it on this way:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
Of course that the currency symbol, precision, format and so on depends on each currency.
You can pass some options to number_to_currency (a standard Rails 4 view helper):
number_to_currency(12.0, :precision => 2)
# => "$12.00"
As posted by Dylan Markow
Simple code for Ruby & Rails
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50

Resources