lib/extensobr.rb in extensobr-1.0.0 vs lib/extensobr.rb in extensobr-1.2.0
- old
+ new
@@ -1,447 +1,479 @@
-require "extensobr/version"
-class Extenso
-
- BRL = {:delimiter => ".", :separator => ",", :unit => "R$", :precision => 2, :position => "before"}
-
- NUM_SING = 0
- NUM_PLURAL = 1
- POS_GENERO = 2
- GENERO_MASC = 0
- GENERO_FEM = 1
-
- VALOR_MAXIMO = 999999999
-
- # As unidades 1 e 2 variam em gênero, pelo que precisamos de dois conjuntos de strings (masculinas e femininas) para as unidades
- UNIDADES = {
- GENERO_MASC => {
- 1 => 'Um',
- 2 => 'Dois',
- 3 => 'Três',
- 4 => 'Quatro',
- 5 => 'Cinco',
- 6 => 'Seis',
- 7 => 'Sete',
- 8 => 'Oito',
- 9 => 'Nove'
- },
- GENERO_FEM => {
- 1 => 'Uma',
- 2 => 'Duas',
- 3 => 'Três',
- 4 => 'Quatro',
- 5 => 'Cinco',
- 6 => 'Seis',
- 7 => 'Sete',
- 8 => 'Oito',
- 9 => 'Nove'
- }
- }
-
- DE11A19 = {
- 11 => 'Onze',
- 12 => 'Doze',
- 13 => 'Treze',
- 14 => 'Quatorze',
- 15 => 'Quinze',
- 16 => 'Dezesseis',
- 17 => 'Dezessete',
- 18 => 'Dezoito',
- 19 => 'Dezenove'
- }
-
- DEZENAS = {
- 10 => 'Dez',
- 20 => 'Vinte',
- 30 => 'Trinta',
- 40 => 'Quarenta',
- 50 => 'Cinquenta',
- 60 => 'Sessenta',
- 70 => 'Setenta',
- 80 => 'Oitenta',
- 90 => 'Noventa'
- }
-
- CENTENA_EXATA = 'Cem'
-
- # As centenas, com exceção de 'cento', também variam em gênero. Aqui também se faz
- # necessário dois conjuntos de strings (masculinas e femininas).
-
- CENTENAS = {
- GENERO_MASC => {
- 100 => 'Cento',
- 200 => 'Duzentos',
- 300 => 'Trezentos',
- 400 => 'Quatrocentos',
- 500 => 'Quinhentos',
- 600 => 'Seiscentos',
- 700 => 'Setecentos',
- 800 => 'Oitocentos',
- 900 => 'Novecentos'
- },
- GENERO_FEM => {
- 100 => 'Cento',
- 200 => 'Duzentas',
- 300 => 'Trezentas',
- 400 => 'Quatrocentas',
- 500 => 'Quinhentas',
- 600 => 'Seiscentas',
- 700 => 'Setecentas',
- 800 => 'Oitocentas',
- 900 => 'Novecentas'
- }
- }
-
- #'Mil' é invariável, seja em gênero, seja em número
- MILHAR = 'mil'
-
- MILHOES = {
- NUM_SING => 'milhão',
- NUM_PLURAL => 'milhões'
- }
-
- UNIDADES_ORDINAL = {
- GENERO_MASC => {
- 1 => 'Primeiro',
- 2 => 'Segundo',
- 3 => 'Terceiro',
- 4 => 'Quarto',
- 5 => 'Quinto',
- 6 => 'Sexto',
- 7 => 'Sétimo',
- 8 => 'Oitavo',
- 9 => 'Nono'},
- GENERO_FEM => {
- 1 => 'Primeira',
- 2 => 'Segunda',
- 3 => 'Terceira',
- 4 => 'Quarta',
- 5 => 'Quinta',
- 6 => 'Sexta',
- 7 => 'Sétima',
- 8 => 'Oitava',
- 9 => 'Nona'}}
-
- DEZENAS_ORDINAL = {
- GENERO_MASC => {
- 10 => 'Décimo',
- 20 => 'Vigésimo',
- 30 => 'Trigésimo',
- 40 => 'Quadragésimo',
- 50 => 'Quinquagésimo',
- 60 => 'Sexagésimo',
- 70 => 'Septuagésimo',
- 80 => 'Octogésimo',
- 90 => 'Nonagésimo'},
- GENERO_FEM => {
- 10 => 'Décima',
- 20 => 'Vigésima',
- 30 => 'Trigésima',
- 40 => 'Quadragésima',
- 50 => 'Quinquagésima',
- 60 => 'Sexagésima',
- 70 => 'Septuagésima',
- 80 => 'Octogésima',
- 90 => 'Nonagésima'}}
-
- CENTENAS_ORDINAL = {
- GENERO_MASC => {
- 100 => 'Centésimo',
- 200 => 'Ducentésimo',
- 300 => 'Trecentésimo',
- 400 => 'Quadringentésimo',
- 500 => 'Quingentésimo',
- 600 => 'Seiscentésimo',
- 700 => 'Septingentésimo',
- 800 => 'Octingentésimo',
- 900 => 'Noningentésimo'},
- GENERO_FEM => {
- 100 => 'Centésima',
- 200 => 'Ducentésima',
- 300 => 'Trecentésima',
- 400 => 'Quadringentésima',
- 500 => 'Quingentésima',
- 600 => 'Seiscentésima',
- 700 => 'Septingentésima',
- 800 => 'Octingentésima',
- 900 => 'Noningentésima'}}
-
-
- MILHAR_ORDINAL = {
- GENERO_MASC => {
- 1000 => 'Milésimo'},
- GENERO_FEM =>{
- 1000 => 'Milésima'}}
-
- def self.is_int(s)
- Integer(s) != nil rescue false
- end
-
- def self.is_float?(str)
- !!Float(str) rescue false
- end
-
- #######################################################################################################################################
-
- def self.numero (valor, genero = GENERO_MASC)
-
- # Gera a representação por extenso de um número inteiro, maior que zero e menor ou igual a VALOR_MAXIMO.
- #
- # PARÂMETROS:
- # valor (Integer) O valor numérico cujo extenso se deseja gerar
- #
- # genero (Integer) [Opcional; valor padrão: Extenso::GENERO_MASC] O gênero gramatical (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
- # do extenso a ser gerado. Isso possibilita distinguir, por exemplo, entre 'duzentos e dois homens' e 'duzentas e duas mulheres'.
- #
- # VALOR DE RETORNO:
- # (String) O número por extenso
-
- # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
-
- if !is_int(valor)
- raise "[Exceção em Extenso.numero] Parâmetro 'valor' não é numérico (recebido: '#{valor}')"
- elsif valor <= 0
- 'Zero'
- elsif valor > VALOR_MAXIMO
- raise "[Exceção em Extenso.numero] Parâmetro '#{valor} deve ser um inteiro entre 1 e #{VALOR_MAXIMO.to_s} (recebido: '#{valor}')"
- elsif genero != GENERO_MASC && genero != GENERO_FEM
- raise "Exceção em Extenso: valor incorreto para o parâmetro 'genero' (recebido: '#{genero}')"
-
- # ------------------------------------------------
-
- elsif valor >= 1 && valor <= 9
- UNIDADES[genero][valor]
-
- elsif valor == 10
- DEZENAS[valor]
-
- elsif valor >= 11 && valor <= 19
- DE11A19[valor]
-
- elsif valor >= 20 && valor <= 99
- dezena = valor - (valor % 10)
- ret = DEZENAS[dezena]
- # Chamada recursiva à função para processar resto se este for maior que zero.
- # O conectivo 'e' é utilizado entre dezenas e unidades.
- resto = valor - dezena
- if resto > 0
- ret += ' e ' + self.numero(resto, genero)
- end
- ret
-
- elsif valor == 100
- CENTENA_EXATA
-
- elsif valor >= 101 && valor <= 999
- centena = valor - (valor % 100)
- ret = CENTENAS[genero][centena] # As centenas (exceto 'cento') variam em gênero
- # Chamada recursiva à função para processar resto se este for maior que zero.
- # O conectivo 'e' é utilizado entre centenas e dezenas.
- resto = valor - centena
- if resto > 0
- ret += ' e ' + self.numero(resto, genero)
- end
- ret
-
- elsif valor >= 1000 && valor <= 999999
- # A função 'floor' é utilizada para encontrar o inteiro da divisão de valor por 1000,
- # assim determinando a quantidade de milhares. O resultado é enviado a uma chamada recursiva
- # da função. A palavra 'mil' não se flexiona.
- milhar = (valor / 1000).floor
- ret = self.numero(milhar, GENERO_MASC) + ' ' + MILHAR # 'Mil' é do gênero masculino
- resto = valor % 1000
- # Chamada recursiva à função para processar resto se este for maior que zero.
- # O conectivo 'e' é utilizado entre milhares e números entre 1 e 99, bem como antes de centenas exatas.
- if resto > 0 && ((resto >= 1 && resto <= 99) || resto % 100 == 0)
- ret += ' e ' + self.numero(resto, genero)
- # Nos demais casos, após o milhar é utilizada a vírgula.
- elsif (resto > 0)
- ret += ', ' + self.numero(resto, genero)
- end
- ret
-
- elsif valor >= 100000 && valor <= VALOR_MAXIMO
- # A função 'floor' é utilizada para encontrar o inteiro da divisão de valor por 1000000,
- # assim determinando a quantidade de milhões. O resultado é enviado a uma chamada recursiva
- # da função. A palavra 'milhão' flexiona-se no plural.
- milhoes = (valor / 1000000).floor
- ret = self.numero(milhoes, GENERO_MASC) + ' ' # Milhão e milhões são do gênero masculino
-
- # Se a o número de milhões for maior que 1, deve-se utilizar a forma flexionada no plural
- ret += milhoes == 1 ? MILHOES[NUM_SING] : MILHOES[NUM_PLURAL]
-
- resto = valor % 1000000
-
- # Chamada recursiva à função para processar resto se este for maior que zero.
- # O conectivo 'e' é utilizado entre milhões e números entre 1 e 99, bem como antes de centenas exatas.
- if resto && (resto >= 1 && resto <= 99)
- ret += ' e ' + self.numero(resto, genero)
- # Nos demais casos, após o milhão é utilizada a vírgula.
- elsif resto > 0
- ret += ', ' + self.numero(resto, genero)
- end
- ret
-
- end
-
- end
-
- #######################################################################################################################################
-
- def self.moeda(
- valor,
- casas_decimais = 2,
- info_unidade = ['Real', 'Reais', GENERO_MASC],
- info_fracao = ['Centavo', 'Centavos', GENERO_MASC]
- )
-
- # Gera a representação por extenso de um valor monetário, maior que zero e menor ou igual a Extenso::VALOR_MAXIMO.
- #
- #
- # PARÂMETROS:
- # valor (Float) O valor monetário cujo extenso se deseja gerar.
- # casas_decimais (Integer) [Opcional; valor padrão: 2] Número de casas decimais a serem consideradas como parte fracionária (centavos)
- #
- # info_unidade (Array) [Opcional; valor padrão: ['real', 'reais', Extenso::GENERO_MASC]] Fornece informações sobre a moeda a ser
- # utilizada. O primeiro valor da matriz corresponde ao nome da moeda no singular, o segundo ao nome da moeda no plural e o terceiro
- # ao gênero gramatical do nome da moeda (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
- #
- # info_fracao (Array) [Opcional; valor padrão: ['centavo', 'centavos', Extenso::GENERO_MASC]] Provê informações sobre a parte fracionária
- # da moeda. O primeiro valor da matriz corresponde ao nome da parte fracionária no singular, o segundo ao nome da parte fracionária no plural
- # e o terceiro ao gênero gramatical da parte fracionária (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
- #
- # VALOR DE RETORNO:
- # (String) O valor monetário por extenso
-
- # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
-
- if ! self.is_float?(valor.to_f.round(casas_decimais).to_s)
- raise "[Exceção em Extenso.moeda] Parâmetro 'valor' não é numérico (recebido: '#{valor}')"
-
- elsif valor <= 0
- "Zero"
-
- elsif ! self.is_int(casas_decimais) || casas_decimais < 0
- raise "[Exceção em Extenso.moeda] Parâmetro 'casas_decimais' não é numérico ou é menor que zero (recebido: '#{casas_decimais}')"
-
- elsif info_unidade.class != Array || info_unidade.length < 3
- temp = info_unidade.class == Array ? '[' + info_unidade.join(', ') + ']' : "'#{info_unidade}'"
- raise "[Exceção em Extenso.moeda] Parâmetro 'info_unidade' não é uma matriz com 3 (três) elementos (recebido: #{temp})"
-
- elsif info_unidade[POS_GENERO] != GENERO_MASC && info_unidade[POS_GENERO] != GENERO_FEM
- raise "Exceção em Extenso: valor incorreto para o parâmetro 'info_unidade[POS_GENERO]' (recebido: '#{info_unidade[POS_GENERO]}')"
-
- elsif info_fracao.class != Array || info_fracao.length < 3
- temp = info_fracao.class == Array ? '[' + info_fracao.join(', ') + ']' : "'#{info_fracao}'"
- raise "[Exceção em Extenso.moeda] Parâmetro 'info_fracao' não é uma matriz com 3 (três) elementos (recebido: #{temp})"
-
- elsif info_fracao[POS_GENERO] != GENERO_MASC && info_fracao[POS_GENERO] != GENERO_FEM
- raise "[Exceção em Extenso.moeda] valor incorreto para o parâmetro 'info_fracao[POS_GENERO]' (recebido: '#{info_fracao[POS_GENERO]}')."
-
- end
-
- # -----------------------------------------------
-
- ret = ''
-
- valor = sprintf("%#{casas_decimais.to_f / 100}f", valor)
- # A parte inteira do valor monetário corresponde ao valor passado antes do '.' no tipo float.
- parte_inteira = valor.split('.')[0].to_i
-
- # A parte fracionária ('centavos'), por seu turno, corresponderá ao valor passado depois do '.'
- fracao = valor.to_s.split('.')[1].to_i
-
- # os préstimos do método Extenso::numero().
- if parte_inteira > 0
- ret = self.numero(parte_inteira, info_unidade[POS_GENERO]) + ((parte_inteira >= 1000000 && (parte_inteira.to_s.chars.reverse[5] == "0" ) ) ? ' de ' : ' ')
- ret += parte_inteira == 1 ? info_unidade[NUM_SING] : info_unidade[NUM_PLURAL]
- ret
- end
-
- # De forma semelhante, o extenso da fracao somente será gerado se esta for maior que zero. */
- if fracao > 0
- # Se a parte_inteira for maior que zero, o extenso para ela já terá sido gerado. Antes de juntar os
- # centavos, precisamos colocar o conectivo 'e'.
- if parte_inteira > 0
- ret += ' e '
- end
- ret += self.numero(fracao, info_fracao[POS_GENERO]) + ' '
- ret += fracao == 1 ? info_fracao[NUM_SING] : info_fracao[NUM_PLURAL]
- end
-
- if valor.to_f == 0
- ret += self.numero(fracao, info_fracao[POS_GENERO]) + ' '
- ret += parte_inteira == 1 ? info_fracao[NUM_SING] : info_fracao[NUM_PLURAL]
- end
-
- ret
-
- end
-
- ######################################################################################################################################################
- def self.ordinal (valor, genero = GENERO_MASC)
-
- # Gera a representação ordinal de um número inteiro de 1 à 1000
-
- # PARÂMETROS:
- # valor (Integer) O valor numérico cujo extenso se deseja gerar
- #
- # genero (Integer) [Opcional; valor padrão: Extenso::GENERO_MASC] O gênero gramatical (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
- # do extenso a ser gerado. Isso possibilita distinguir, por exemplo, entre 'duzentos e dois homens' e 'duzentas e duas mulheres'.
- #
- # VALOR DE RETORNO:
- # (String) O número por extenso
-
- # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
-
- if !is_int(valor)
- raise "[Exceção em Extenso.numero] Parâmetro 'valor' não é numérico (recebido: '#{valor}')"
- elsif valor <= 0
- 'Zero'
- # raise "[Exceção em Extenso.numero] Parâmetro 'valor' igual a ou menor que zero (recebido: '#{valor}')"
- elsif valor > VALOR_MAXIMO
- raise '[Exceção em Extenso::numero] Parâmetro ''valor'' deve ser um inteiro entre 1 e ' + VALOR_MAXIMO.to_s + " (recebido: '#{valor}')"
- elsif genero != GENERO_MASC && genero != GENERO_FEM
- raise "Exceção em Extenso: valor incorreto para o parâmetro 'genero' (recebido: '#{genero}')"
- # ------------------------------------------------
- elsif valor >= 1 && valor <= 9
- return UNIDADES_ORDINAL[genero][valor]
- elsif valor >= 10 && valor <= 99
- dezena = valor - (valor % 10)
- resto = valor - dezena
- ret = DEZENAS_ORDINAL[genero][dezena]+" "
- if resto > 0 then ret+= self.ordinal(resto,genero); end
- return ret
- elsif valor >= 100 && valor <= 999
- centena = valor - (valor % 100)
- resto = valor - centena
- ret = CENTENAS_ORDINAL[genero][centena]+" "
- if resto > 0 then ret += self.ordinal(resto, genero); end
- return ret
- elsif valor == 1000
- return MILHAR_ORDINAL[genero][valor]+" "
- end
- end
-
- # Gera o valor em formato de Real
- #
- # Exemplo:
- # Extenso.real_formatado(10) - R$ 10,00
- # Extenso.real_formatado(1.55) - R$ 1,55
- #
- # @params[Object]
- def self.real_formatado(valor)
- float_valor = sprintf("%#0.02f", valor)
- if float_valor.chars.count >= 7
- float_valor = float_valor.chars.reverse.insert(6, '.').reverse.join
- end
-
- if float_valor.chars.count >= 11
- float_valor = float_valor.chars.reverse.insert(10, '.').reverse.join
- end
-
- float_valor = float_valor.chars.reverse
- float_valor[2] = ','
-
- "R$ #{float_valor.reverse.join}"
- end
-end
\ No newline at end of file
+# frozen_string_literal: true
+
+require 'extensobr/version'
+require 'settings'
+
+# ----- CARREGA CONFIGURAÇÃO -----
+Settings.load_extensobr_settings
+
+# ----- CARREGA CORE EXTENSÕES -----
+if Settings.extensobr_settings[:use_core_exts] == 'true'
+ require 'core_exts/float'
+ require 'core_exts/integer'
+ require 'core_exts/string'
+end
+
+class Extenso
+ BRL = { delimiter: '.', separator: ',', unit: 'R$', precision: 2, position: 'before' }.freeze unless Extenso.const_defined?('BRL')
+
+ NUM_SING = 0 unless Extenso.const_defined?('NUM_SING')
+ NUM_PLURAL = 1 unless Extenso.const_defined?('NUM_PLURAL')
+ POS_GENERO = 2 unless Extenso.const_defined?('POS_GENERO')
+ GENERO_MASC = 0 unless Extenso.const_defined?('GENERO_MASC')
+ GENERO_FEM = 1 unless Extenso.const_defined?('GENERO_FEM')
+
+ VALOR_MAXIMO = 999_999_999 unless Extenso.const_defined?('VALOR_MAXIMO')
+
+ # As unidades 1 e 2 variam em gênero, pelo que precisamos de dois conjuntos de strings (masculinas e femininas) para as unidades
+ UNIDADES = {
+ GENERO_MASC => {
+ 1 => 'Um',
+ 2 => 'Dois',
+ 3 => 'Três',
+ 4 => 'Quatro',
+ 5 => 'Cinco',
+ 6 => 'Seis',
+ 7 => 'Sete',
+ 8 => 'Oito',
+ 9 => 'Nove'
+ },
+ GENERO_FEM => {
+ 1 => 'Uma',
+ 2 => 'Duas',
+ 3 => 'Três',
+ 4 => 'Quatro',
+ 5 => 'Cinco',
+ 6 => 'Seis',
+ 7 => 'Sete',
+ 8 => 'Oito',
+ 9 => 'Nove'
+ }
+ }.freeze unless Extenso.const_defined?('UNIDADES')
+
+ DE11A19 = {
+ 11 => 'Onze',
+ 12 => 'Doze',
+ 13 => 'Treze',
+ 14 => 'Quatorze',
+ 15 => 'Quinze',
+ 16 => 'Dezesseis',
+ 17 => 'Dezessete',
+ 18 => 'Dezoito',
+ 19 => 'Dezenove'
+ }.freeze unless Extenso.const_defined?('DE11A19')
+
+ DEZENAS = {
+ 10 => 'Dez',
+ 20 => 'Vinte',
+ 30 => 'Trinta',
+ 40 => 'Quarenta',
+ 50 => 'Cinquenta',
+ 60 => 'Sessenta',
+ 70 => 'Setenta',
+ 80 => 'Oitenta',
+ 90 => 'Noventa'
+ }.freeze unless Extenso.const_defined?('DEZENAS')
+
+ CENTENA_EXATA = 'Cem' unless Extenso.const_defined?('CENTENA_EXATA')
+
+ # As centenas, com exceção de 'cento', também variam em gênero. Aqui também se faz
+ # necessário dois conjuntos de strings (masculinas e femininas).
+
+ CENTENAS = {
+ GENERO_MASC => {
+ 100 => 'Cento',
+ 200 => 'Duzentos',
+ 300 => 'Trezentos',
+ 400 => 'Quatrocentos',
+ 500 => 'Quinhentos',
+ 600 => 'Seiscentos',
+ 700 => 'Setecentos',
+ 800 => 'Oitocentos',
+ 900 => 'Novecentos'
+ },
+ GENERO_FEM => {
+ 100 => 'Cento',
+ 200 => 'Duzentas',
+ 300 => 'Trezentas',
+ 400 => 'Quatrocentas',
+ 500 => 'Quinhentas',
+ 600 => 'Seiscentas',
+ 700 => 'Setecentas',
+ 800 => 'Oitocentas',
+ 900 => 'Novecentas'
+ }
+ }.freeze unless Extenso.const_defined?('CENTENAS')
+
+ # 'Mil' é invariável, seja em gênero, seja em número
+ MILHAR = 'mil' unless Extenso.const_defined?('MILHAR')
+
+ MILHOES = {
+ NUM_SING => 'milhão',
+ NUM_PLURAL => 'milhões'
+ }.freeze unless Extenso.const_defined?('MILHOES')
+
+ UNIDADES_ORDINAL = {
+ GENERO_MASC => {
+ 1 => 'Primeiro',
+ 2 => 'Segundo',
+ 3 => 'Terceiro',
+ 4 => 'Quarto',
+ 5 => 'Quinto',
+ 6 => 'Sexto',
+ 7 => 'Sétimo',
+ 8 => 'Oitavo',
+ 9 => 'Nono'
+ },
+ GENERO_FEM => {
+ 1 => 'Primeira',
+ 2 => 'Segunda',
+ 3 => 'Terceira',
+ 4 => 'Quarta',
+ 5 => 'Quinta',
+ 6 => 'Sexta',
+ 7 => 'Sétima',
+ 8 => 'Oitava',
+ 9 => 'Nona'
+ }
+ }.freeze unless Extenso.const_defined?('UNIDADES_ORDINAL')
+
+ DEZENAS_ORDINAL = {
+ GENERO_MASC => {
+ 10 => 'Décimo',
+ 20 => 'Vigésimo',
+ 30 => 'Trigésimo',
+ 40 => 'Quadragésimo',
+ 50 => 'Quinquagésimo',
+ 60 => 'Sexagésimo',
+ 70 => 'Septuagésimo',
+ 80 => 'Octogésimo',
+ 90 => 'Nonagésimo'
+ },
+ GENERO_FEM => {
+ 10 => 'Décima',
+ 20 => 'Vigésima',
+ 30 => 'Trigésima',
+ 40 => 'Quadragésima',
+ 50 => 'Quinquagésima',
+ 60 => 'Sexagésima',
+ 70 => 'Septuagésima',
+ 80 => 'Octogésima',
+ 90 => 'Nonagésima'
+ }
+ }.freeze unless Extenso.const_defined?('DEZENAS_ORDINAL')
+
+ CENTENAS_ORDINAL = {
+ GENERO_MASC => {
+ 100 => 'Centésimo',
+ 200 => 'Ducentésimo',
+ 300 => 'Trecentésimo',
+ 400 => 'Quadringentésimo',
+ 500 => 'Quingentésimo',
+ 600 => 'Seiscentésimo',
+ 700 => 'Septingentésimo',
+ 800 => 'Octingentésimo',
+ 900 => 'Noningentésimo'
+ },
+ GENERO_FEM => {
+ 100 => 'Centésima',
+ 200 => 'Ducentésima',
+ 300 => 'Trecentésima',
+ 400 => 'Quadringentésima',
+ 500 => 'Quingentésima',
+ 600 => 'Seiscentésima',
+ 700 => 'Septingentésima',
+ 800 => 'Octingentésima',
+ 900 => 'Noningentésima'
+ }
+ }.freeze unless Extenso.const_defined?('CENTENAS_ORDINAL')
+
+ MILHAR_ORDINAL = {
+ GENERO_MASC => {
+ 1000 => 'Milésimo'
+ },
+ GENERO_FEM => {
+ 1000 => 'Milésima'
+ }
+ }.freeze unless Extenso.const_defined?('MILHAR_ORDINAL')
+
+ def self.int?(payload)
+ !Integer(payload).nil?
+ rescue StandardError
+ false
+ end
+
+ def self.float?(payload)
+ !!Float(payload)
+ rescue StandardError
+ false
+ end
+
+ #######################################################################################################################################
+
+ def self.numero(valor, genero = GENERO_MASC)
+ # Gera a representação por extenso de um número inteiro, maior que zero e menor ou igual a VALOR_MAXIMO.
+ #
+ # PARÂMETROS:
+ # valor (Integer) O valor numérico cujo extenso se deseja gerar
+ #
+ # genero (Integer) [Opcional; valor padrão: Extenso::GENERO_MASC] O gênero gramatical (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
+ # do extenso a ser gerado. Isso possibilita distinguir, por exemplo, entre 'duzentos e dois homens' e 'duzentas e duas mulheres'.
+ #
+ # VALOR DE RETORNO:
+ # (String) O número por extenso
+
+ # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
+ if valor.nil? || valor == ''
+ raise "[Exceção em Extenso.numero] Parâmetro 'valor' é nulo" if Settings&.extensobr_settings&.dig(:raise_for_nil) == 'true'
+
+ return 'Zero'
+ end
+
+ unless int?(valor) && !valor.nil?
+ "[Exceção em Extenso.numero] Parâmetro 'valor' não é numérico (recebido: '#{valor}')"
+ end
+
+ if valor <= 0
+ 'Zero'
+ elsif valor > VALOR_MAXIMO
+ raise "[Exceção em Extenso.numero] Parâmetro '#{valor} deve ser um inteiro entre 1 e #{VALOR_MAXIMO} (recebido: '#{valor}')"
+ elsif genero != GENERO_MASC && genero != GENERO_FEM
+ raise "Exceção em Extenso: valor incorreto para o parâmetro 'genero' (recebido: '#{genero}')"
+
+ # ------------------------------------------------
+
+ elsif valor >= 1 && valor <= 9
+ UNIDADES[genero][valor]
+
+ elsif valor == 10
+ DEZENAS[valor]
+
+ elsif valor >= 11 && valor <= 19
+ DE11A19[valor]
+
+ elsif valor >= 20 && valor <= 99
+ dezena = valor - (valor % 10)
+ ret = DEZENAS[dezena]
+ # Chamada recursiva à função para processar resto se este for maior que zero.
+ # O conectivo 'e' é utilizado entre dezenas e unidades.
+ resto = valor - dezena
+ ret += " e #{numero(resto, genero)}" if resto.positive?
+ ret
+
+ elsif valor == 100
+ CENTENA_EXATA
+
+ elsif valor >= 101 && valor <= 999
+ centena = valor - (valor % 100)
+ ret = CENTENAS[genero][centena] # As centenas (exceto 'cento') variam em gênero
+ # Chamada recursiva à função para processar resto se este for maior que zero.
+ # O conectivo 'e' é utilizado entre centenas e dezenas.
+ resto = valor - centena
+ ret += " e #{numero(resto, genero)}" if resto.positive?
+ ret
+
+ elsif valor >= 1000 && valor <= 999_999
+ # A função 'floor' é utilizada para encontrar o inteiro da divisão de valor por 1000,
+ # assim determinando a quantidade de milhares. O resultado é enviado a uma chamada recursiva
+ # da função. A palavra 'mil' não se flexiona.
+ milhar = (valor / 1000).floor
+ ret = "#{numero(milhar, GENERO_MASC)} #{MILHAR}" # 'Mil' é do gênero masculino
+ resto = valor % 1000
+ # Chamada recursiva à função para processar resto se este for maior que zero.
+ # O conectivo 'e' é utilizado entre milhares e números entre 1 e 99, bem como antes de centenas exatas.
+ if resto.positive? && ((resto >= 1 && resto <= 99) || (resto % 100).zero?)
+ ret += " e #{numero(resto, genero)}"
+ # Nos demais casos, após o milhar é utilizada a vírgula.
+ elsif resto.positive?
+ ret += ", #{numero(resto, genero)}"
+ end
+ ret
+
+ elsif valor >= 100_000 && valor <= VALOR_MAXIMO
+ # A função 'floor' é utilizada para encontrar o inteiro da divisão de valor por 1000000,
+ # assim determinando a quantidade de milhões. O resultado é enviado a uma chamada recursiva
+ # da função. A palavra 'milhão' flexiona-se no plural.
+ milhoes = (valor / 1_000_000).floor
+ ret = "#{numero(milhoes, GENERO_MASC)} " # Milhão e milhões são do gênero masculino
+
+ # Se a o número de milhões for maior que 1, deve-se utilizar a forma flexionada no plural
+ ret += milhoes == 1 ? MILHOES[NUM_SING] : MILHOES[NUM_PLURAL]
+
+ resto = valor % 1_000_000
+
+ # Chamada recursiva à função para processar resto se este for maior que zero.
+ # O conectivo 'e' é utilizado entre milhões e números entre 1 e 99, bem como antes de centenas exatas.
+ if resto && (resto >= 1 && resto <= 99)
+ ret += " e #{numero(resto, genero)}"
+ # Nos demais casos, após o milhão é utilizada a vírgula.
+ elsif resto.positive?
+ ret += ", #{numero(resto, genero)}"
+ end
+ ret
+
+ end
+ end
+
+ #######################################################################################################################################
+
+ def self.moeda(
+ valor,
+ casas_decimais = 2,
+ info_unidade = ['Real', 'Reais', GENERO_MASC],
+ info_fracao = ['Centavo', 'Centavos', GENERO_MASC]
+ )
+ # Gera a representação por extenso de um valor monetário, maior que zero e menor ou igual a Extenso::VALOR_MAXIMO.
+ #
+ #
+ # PARÂMETROS:
+ # valor (Float) O valor monetário cujo extenso se deseja gerar.
+ # casas_decimais (Integer) [Opcional; valor padrão: 2] Número de casas decimais a serem consideradas como parte fracionária (centavos)
+ #
+ # info_unidade (Array) [Opcional; valor padrão: ['real', 'reais', Extenso::GENERO_MASC]] Fornece informações sobre a moeda a ser
+ # utilizada. O primeiro valor da matriz corresponde ao nome da moeda no singular, o segundo ao nome da moeda no plural e o terceiro
+ # ao gênero gramatical do nome da moeda (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
+ #
+ # info_fracao (Array) [Opcional; valor padrão: ['centavo', 'centavos', Extenso::GENERO_MASC]] Provê informações sobre a parte fracionária
+ # da moeda. O primeiro valor da matriz corresponde ao nome da parte fracionária no singular, o segundo ao nome da parte fracionária no plural
+ # e o terceiro ao gênero gramatical da parte fracionária (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
+ #
+ # VALOR DE RETORNO:
+ # (String) O valor monetário por extenso
+
+ # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
+ if valor.nil? || valor == ''
+ raise "[Exceção em Extenso.moeda] Parâmetro 'valor' é nulo" if Settings&.extensobr_settings&.dig(:raise_for_nil) == 'true'
+
+ return 'Zero Centavos'
+ end
+
+ unless float?(valor.to_f.round(casas_decimais).to_s)
+ raise "[Exceção em Extenso.moeda] Parâmetro 'valor' não é numérico (recebido: '#{valor}')"
+ end
+
+ if valor <= 0
+ 'Zero'
+
+ elsif !int?(casas_decimais) || casas_decimais.negative?
+ raise "[Exceção em Extenso.moeda] Parâmetro 'casas_decimais' não é numérico ou é menor que zero (recebido: '#{casas_decimais}')"
+
+ elsif info_unidade.class != Array || info_unidade.length < 3
+ temp = info_unidade.instance_of?(Array) ? "[#{info_unidade.join(', ')}]" : "'#{info_unidade}'"
+ raise "[Exceção em Extenso.moeda] Parâmetro 'info_unidade' não é uma matriz com 3 (três) elementos (recebido: #{temp})"
+
+ elsif info_unidade[POS_GENERO] != GENERO_MASC && info_unidade[POS_GENERO] != GENERO_FEM
+ raise "Exceção em Extenso: valor incorreto para o parâmetro 'info_unidade[POS_GENERO]' (recebido: '#{info_unidade[POS_GENERO]}')"
+
+ elsif info_fracao.class != Array || info_fracao.length < 3
+ temp = info_fracao.instance_of?(Array) ? "[#{info_fracao.join(', ')}]" : "'#{info_fracao}'"
+ raise "[Exceção em Extenso.moeda] Parâmetro 'info_fracao' não é uma matriz com 3 (três) elementos (recebido: #{temp})"
+
+ elsif info_fracao[POS_GENERO] != GENERO_MASC && info_fracao[POS_GENERO] != GENERO_FEM
+ raise "[Exceção em Extenso.moeda] valor incorreto para o parâmetro 'info_fracao[POS_GENERO]' (recebido: '#{info_fracao[POS_GENERO]}')."
+
+ end
+
+ # -----------------------------------------------
+
+ ret = ''
+
+ valor = format("%#{casas_decimais.to_f / 100}f", valor)
+ # A parte inteira do valor monetário corresponde ao valor passado antes do '.' no tipo float.
+ parte_inteira = valor.split('.')[0].to_i
+
+ # A parte fracionária ('centavos'), por seu turno, corresponderá ao valor passado depois do '.'
+ fracao = valor.to_s.split('.')[1].to_i
+
+ # os préstimos do método Extenso::numero().
+ if parte_inteira.positive?
+ ret = numero(parte_inteira,
+ info_unidade[POS_GENERO]) + (parte_inteira >= 1_000_000 && (parte_inteira.to_s.chars.reverse[5] == '0') ? ' de ' : ' ')
+ ret += parte_inteira == 1 ? info_unidade[NUM_SING] : info_unidade[NUM_PLURAL]
+ ret
+ end
+
+ # De forma semelhante, o extenso da fracao somente será gerado se esta for maior que zero. */
+ if fracao.positive?
+ # Se a parte_inteira for maior que zero, o extenso para ela já terá sido gerado. Antes de juntar os
+ # centavos, precisamos colocar o conectivo 'e'.
+ ret += ' e ' if parte_inteira.positive?
+ ret += "#{numero(fracao, info_fracao[POS_GENERO])} "
+ ret += fracao == 1 ? info_fracao[NUM_SING] : info_fracao[NUM_PLURAL]
+ end
+
+ if valor.to_f.zero?
+ ret += "#{numero(fracao, info_fracao[POS_GENERO])} "
+ ret += parte_inteira == 1 ? info_fracao[NUM_SING] : info_fracao[NUM_PLURAL]
+ end
+
+ ret
+ end
+
+ ######################################################################################################################################################
+ def self.ordinal(valor, genero = GENERO_MASC)
+ # Gera a representação ordinal de um número inteiro de 1 à 1000
+
+ # PARÂMETROS:
+ # valor (Integer) O valor numérico cujo extenso se deseja gerar
+ #
+ # genero (Integer) [Opcional; valor padrão: Extenso::GENERO_MASC] O gênero gramatical (Extenso::GENERO_MASC ou Extenso::GENERO_FEM)
+ # do extenso a ser gerado. Isso possibilita distinguir, por exemplo, entre 'duzentos e dois homens' e 'duzentas e duas mulheres'.
+ #
+ # VALOR DE RETORNO:
+ # (String) O número por extenso
+
+ # ----- VALIDAÇÃO DOS PARÂMETROS DE ENTRADA ----
+ if valor.nil? || valor == ''
+ raise "[Exceção em Extenso.ordinal] Parâmetro 'valor' é nulo" if Settings&.extensobr_settings&.dig(:raise_for_nil) == 'true'
+
+ return 'Zero'
+ end
+
+ raise "[Exceção em Extenso.numero] Parâmetro 'valor' não é numérico (recebido: '#{valor}')" unless int?(valor)
+
+ if valor <= 0
+ 'Zero'
+ # raise "[Exceção em Extenso.numero] Parâmetro 'valor' igual a ou menor que zero (recebido: '#{valor}')"
+ elsif valor > VALOR_MAXIMO
+ raise "[Exceção em Extenso::numero] Parâmetro ''valor'' deve ser um inteiro entre 1 e #{VALOR_MAXIMO} (recebido: '#{valor}')"
+ elsif genero != GENERO_MASC && genero != GENERO_FEM
+ raise "Exceção em Extenso: valor incorreto para o parâmetro 'genero' (recebido: '#{genero}')"
+ # ------------------------------------------------
+ elsif valor >= 1 && valor <= 9
+ UNIDADES_ORDINAL[genero][valor]
+ elsif valor >= 10 && valor <= 99
+ dezena = valor - (valor % 10)
+ resto = valor - dezena
+ ret = "#{DEZENAS_ORDINAL[genero][dezena]} "
+ ret += ordinal(resto, genero) if resto.positive?
+ ret.rstrip
+ elsif valor >= 100 && valor <= 999
+ centena = valor - (valor % 100)
+ resto = valor - centena
+ ret = "#{CENTENAS_ORDINAL[genero][centena]} "
+ ret += ordinal(resto, genero) if resto.positive?
+ ret.rstrip
+ elsif valor == 1000
+ MILHAR_ORDINAL[genero][valor]
+ end
+ end
+
+ # Gera o valor em formato de Real
+ #
+ # Exemplo:
+ # Extenso.real_formatado(10) - R$ 10,00
+ # Extenso.real_formatado(1.55) - R$ 1,55
+ #
+ # @params[Object]
+ def self.real_formatado(valor)
+ float_valor = format('%#0.02f', valor)
+ float_valor = float_valor.chars.reverse.insert(6, '.').reverse.join if float_valor.chars.count >= 7
+
+ float_valor = float_valor.chars.reverse.insert(10, '.').reverse.join if float_valor.chars.count >= 11
+
+ float_valor = float_valor.chars.reverse
+ float_valor[2] = ','
+
+ "R$ #{float_valor.reverse.join}"
+ end
+end