# frozen_string_literal: true
require 'money'
##
# Module for creating currency drop-downs.
#
module CurrencySelect
class << self
##
# Money::Currency::table is a hash of this format for each entry:
# id (lowercase) => {
# priority: 2,
# iso_code: "EUR",
# name: "Euro",
# symbol: "$",
# alternate_symbols: [],
# subunit: "Cent",
# subunit_to_unit: 100,
# symbol_first: true,
# html_entity: "€"
# decimal_mark: ",",
# thousands_separator: ".",
# iso_numeric: "978"
# smallest_denomination: 1
# }
unless const_defined?('CURRENCIES')
CURRENCIES = Money::Currency.table.inject([]) do |array, (_, currency)|
array << [
"#{currency[:name]} - #{currency[:iso_code]}", currency[:iso_code]
]
end
# sort by the label (not by the ISO code)
CURRENCIES.sort_by(&:first)
end
##
# Returns a two-dimensional array with ISO codes and currency names for
# option tags.
#
# In the outer array, there will be one element for each currency. Each
# element looks like this, containing a label and the ISO code:
# ["Afghan Afghani - AFN", "AFN"]
#
# @return [Array]
#
def currencies_array
CURRENCIES
end
##
# Returns an array with ISO codes and currency names for currency ISO codes
# passed as an argument
# == Example
# priority_currencies_array([ "USD", "NOK" ])
# # => [
# # ['United States Dollar - USD', 'USD' ],
# # ['Norwegian Kroner - NOK', 'NOK']
# # ]
#
# @return [Array]
#
def priority_currencies_array(currency_codes = [])
currency_codes.flat_map do |code|
currencies_array.select { |currency| currency.last.to_s == code }
end
end
end
end
# CurrencySelect
module ActionView
module Helpers
##
# Module for the form options.
#
module FormOptionsHelper
# Return select and option tags for the given object and method, using
# currency_options_for_select to generate the list of option tags.
def currency_select(
object, method, priority_currencies = nil, options = {},
html_options = {}
)
tag = CurrencySelectTag.new(object, method, self, options)
tag.to_currency_select_tag(priority_currencies, options, html_options)
end
# Returns a string of option tags for all available currencies. Supply
# a currency ISO code as +selected+ to have it marked as the selected
# option tag. You can also supply an array of currencies as
# +priority_currencies+, so that they will be listed above the rest of
# the list.
def currency_options_for_select(selected = nil, priority_currencies = nil)
currency_options = ''.html_safe
if priority_currencies
currency_options += options_for_select(
::CurrencySelect.priority_currencies_array(priority_currencies),
selected
)
raw = ''
currency_options += raw.html_safe + "\n"
# prevents selected from being included twice in the HTML which causes
# some browsers to select the second selected option (not priority)
# which makes it harder to select an alternative priority country
selected = nil if priority_currencies.include?(selected)
end
# All the countries included in the country_options output.
currency_options + options_for_select(
::CurrencySelect.currencies_array, selected
)
end
end
##
# Tag.
#
module ToCurrencySelectTag
def to_currency_select_tag(priority_currencies, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
value = if method(:value).arity.zero?
value()
else
value(object)
end
content_tag(
'select',
add_options(
currency_options_for_select(value, priority_currencies),
options,
value
),
html_options
)
end
end
##
# Tag.
#
class CurrencySelectTag < Tags::Base
include ToCurrencySelectTag
end
##
# Form builder.
#
class FormBuilder
def currency_select(
method, priority_currencies = nil, options = {}, html_options = {}
)
@template.currency_select(
@object_name,
method,
priority_currencies,
options.merge(object: @object),
html_options
)
end
end
end
end