Skip to content

Instantly share code, notes, and snippets.

@sigra
Created January 2, 2015 15:10
Show Gist options
  • Save sigra/632561139e7e245c4a78 to your computer and use it in GitHub Desktop.
Save sigra/632561139e7e245c4a78 to your computer and use it in GitHub Desktop.
Rails multiple currency support
class ApplicationController < ActionController::Base
include HasCurrency
end
config.currencies = %w(EUR GBP SEK USD CZK)
config.country_currencies = { 'DK' => 'DKK', 'SE' => 'SEK', 'UK' => 'GBP', 'US' => 'USD', 'CZ' => 'CZK' }
config.country_locales = { 'SE' => :se, 'DE' => :de, 'ES' => :es, 'CS' => :cs }
module HasCurrency
extend ActiveSupport::Concern
included do
before_action :set_conversion_rates
before_action :add_user_currency, if: -> { defined?(current_user) && current_user.present? }
def current_currency
@current_currency ||= get_current_currency
end
helper_method :current_currency
def currency_info
@currency_info ||= get_current_currency_info
end
helper_method :currency_info
end
private
def set_conversion_rates
rates = Rails.cache.read('money:exchange_rates')
Money.default_bank.update_rates_from_s(rates) if rates.present?
end
def get_current_currency
if currency_exists?(params[:currency])
cookies[:currency] = params[:currency]
elsif current_user.present? && currency_exists?(current_user.currency)
current_user.currency
elsif currency_exists?(cookies[:currency])
cookies[:currency]
else
cookies[:currency] = currency_by_ip(request.remote_ip)
end
end
def get_current_currency_info
current = Money::Currency.find(get_current_currency)
default = Money.default_currency
{
rate: Money.default_bank.get_rate(default, current),
symbol_first: current.symbol_first?,
name: current.name,
symbol: current.symbol,
code: current.iso_code,
subunit_to_unit: current.subunit_to_unit
}
end
def currency_exists?(currency)
currency.present? && EuCentralBank::CURRENCIES.select { |c|
c.downcase == currency.downcase
}.size == 1
end
def add_user_currency
unless current_user.currency?
current_user.update_attribute :currency, currency_by_ip(current_user.current_sign_in_ip)
end
end
def currency_by_ip(ip)
geo = Geocoder.search(ip).try(:first)
if geo.present?
Country.new(geo.country_code).try(:currency_code) || Money.default_currency.to_s
else
Money.default_currency.to_s
end
end
end
gem 'eu_central_bank'
gem 'geocoder'
gem 'money-rails'
# for rate worker
gem 'sidekiq'
gem 'sidetiq'
gem 'sinatra', require: false
module MoneyHelper
def localized_price(price)
currency = converted_price(price).currency
number_to_currency(converted_price(price).to_f,
precision: currency.exponent.to_i,
strip_insignificant_zeros: true,
separator: currency.decimal_mark,
delimiter: currency.thousands_separator,
unit: currency.symbol,
format: currency.symbol_first ? "%u%n" : "%n%u"
)
end
def converted_price(price)
price = Money.new(price) unless price.is_a?(Money)
converted = Money.new(price).exchange_to(current_currency).to_f
ceiled = if %w(EUR GBP USD).include?(current_currency)
sub = currency_info[:subunit_to_unit] / 10
(converted * sub).ceil / sub.to_f
else
converted.ceil
end
Money.new(ceiled * currency_info[:subunit_to_unit], current_currency)
rescue
price
end
end
rails g migration AddCurrencyToUsers currency:string
class CacheExchangeRatesWorker
include Sidekiq::Worker
include Sidetiq::Schedulable
sidekiq_options retry: false, unique: true
recurrence { minutely(5) }
def perform
rates = Money.default_bank.save_rates_to_s
Rails.cache.write('money:exchange_rates', rates)
rates
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment