class Extenso

Constants

BRL
CENTENAS

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_ORDINAL
CENTENA_EXATA
DE11A19
DEZENAS
DEZENAS_ORDINAL
GENERO_FEM
GENERO_MASC
MILHAR

'Mil' é invariável, seja em gênero, seja em número

MILHAR_ORDINAL
MILHOES
NUM_PLURAL
NUM_SING
POS_GENERO
UNIDADES

As unidades 1 e 2 variam em gênero, pelo que precisamos de dois conjuntos de strings (masculinas e femininas) para as unidades

UNIDADES_ORDINAL
VALOR_MAXIMO

Public Class Methods

is_float?(str) click to toggle source
# File lib/extensobr.rb, line 179
def self.is_float?(str)
  !!Float(str) rescue false
end
is_int(s) click to toggle source
# File lib/extensobr.rb, line 175
def self.is_int(s)
  Integer(s) != nil rescue false
end
moeda( valor, casas_decimais = 2, info_unidade = ['Real', 'Reais', GENERO_MASC], info_fracao = ['Centavo', 'Centavos', GENERO_MASC] ) click to toggle source
# File lib/extensobr.rb, line 290
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
numero(valor, genero = GENERO_MASC) click to toggle source
# File lib/extensobr.rb, line 185
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
ordinal(valor, genero = GENERO_MASC) click to toggle source
# File lib/extensobr.rb, line 381
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
real_formatado(valor) click to toggle source

Gera o valor em formato de Real

Exemplo:

Extenso.real_formatado(10) - R$ 10,00
Extenso.real_formatado(1.55) - R$ 1,55

@params

# File lib/extensobr.rb, line 432
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