unpack brazilian-rails

This commit is contained in:
2013-07-14 11:09:25 -04:00
parent 7d287fe530
commit e563725dc5
131 changed files with 5496 additions and 0 deletions

View File

@@ -0,0 +1 @@
Version 0.0.1 -> Versão beta da gem.

View File

@@ -0,0 +1,20 @@
Copyright (c) 2008 [name of plugin creator]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,52 @@
== Como usar o Dinheiro em seu ActiveRecord?
* Arquivo 001_create_lancamentos.rb:
class CreateLancamentos < ActiveRecord::Migration
def self.up
create_table :lancamentos do |t|
t.column :descricao, :string, :null => false
t.column :valor, :decimal, :precision => 14, :scale => 2
t.column :mensalidade, :decimal, :precision => 14, :scale => 2
end
end
def self.down
drop_table :lancamentos
end
end
* Arquivo lancamento.rb:
class Lancamento < ActiveRecord::Base
usar_como_dinheiro :valor, :mensalidade
end
* No console (script/console):
Loading development environment.
>> lancamento = Lancamento.new
=> #<Lancamento:0x9652cd8 @attributes={"descricao"=>nil,
"valor"=>#<BigDecimal:9657008,'0.0',4(4)>,
"mensalidade"=>#<BigDecimal:9656e8c,'0.0',4(4)>},
@new_record=true>
>> lancamento.valor = 100
=> 100
>> lancamento.valor
=> #<Dinheiro:0x9650f3c @quantia=10000>
>> lancamento.valor.real
=> "R$ 100,00"
>> lancamento.valor = 100.50
=> 100.5
>> lancamento.valor.real
=> "R$ 100,50"
>> lancamento.valor = "250.50"
=> "250.50"
>> lancamento.valor.real
=> "R$ 250,50"
>> lancamento.valor = 354.58.reais
=> #<Dinheiro:0x9646384 @quantia=35458>
>> lancamento.valor.real
=> "R$ 354,58"
>> exit

View File

@@ -0,0 +1,73 @@
# encoding: UTF-8
require "rubygems"
require "rake"
require "rake/testtask"
require "rdoc/task"
require "rake/packagetask"
require "rake/gempackagetask"
require File.join(File.dirname(__FILE__), "lib", "brdinheiro", "version")
PKG_BUILD = ENV["PKG_BUILD"] ? "." + ENV["PKG_BUILD"] : ""
PKG_NAME = "brdinheiro"
PKG_VERSION = BrDinheiro::VERSION::STRING + PKG_BUILD
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
desc "Default Task"
task :default => [ :test ]
# Run the unit tests
Rake::TestTask.new { |t|
t.libs << "test"
t.pattern = "test/*_test.rb"
t.verbose = true
t.warning = false
}
#Generate the RDoc documentation
Rake::RDocTask.new { |rdoc|
rdoc.rdoc_dir = "doc"
rdoc.title = "Brazilian Rails -- Dinheiro"
rdoc.options << "--line-numbers" << "--inline-source" << "-A cattr_accessor=object"
rdoc.options << "--charset" << "utf-8"
rdoc.template = "#{ENV["template"]}.rb" if ENV["template"]
rdoc.rdoc_files.include("README", "CHANGELOG")
rdoc.rdoc_files.include("lib/**/*")
}
# Create compressed packages
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = PKG_NAME
s.summary = "brdinheiro é uma das gems que compoem o Brazilian Rails"
s.description = %q{brdinheiro é uma das gems que compoem o Brazilian Rails}
s.version = PKG_VERSION
s.authors = ["Marcos Tapajós", "Celestino Gomes", "Andre Kupkosvki", "Vinícius Teles", "Felipe Barreto", "Rafael Walter", "Cassio Marques"]
s.email = %w"tapajos@gmail.com tinorj@gmail.com kupkovski@gmail.com vinicius.m.teles@gmail.com felipebarreto@gmail.com rafawalter@gmail.com cassiommc@gmail.com"
s.rubyforge_project = "brdinheiro"
s.homepage = "http://www.improveit.com.br/software_livre/brazilian_rails"
s.add_dependency("actionpack", ">= 3.0.0")
s.add_dependency("activerecord", ">= 3.0.0")
s.add_dependency("brnumeros", "= #{PKG_VERSION}")
s.add_development_dependency "rake"
s.has_rdoc = true
s.requirements << "brnumeros"
s.require_path = "lib"
s.files = [ "Rakefile", "README", "CHANGELOG", "MIT-LICENSE" ]
s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
`gem push pkg/#{PKG_FILE_NAME}.gem`
end

View File

@@ -0,0 +1,22 @@
$:.unshift(File.dirname(__FILE__)) unless
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
%w(dinheiro
dinheiro_util
dinheiro_active_record
excecoes
nil_class).each {|req| require File.dirname(__FILE__) + "/brdinheiro/#{req}"}
%w(bigdecimal
rubygems
active_record
active_support/all).each {|req| require req }
require 'brnumeros'
String.send(:include, DinheiroUtil)
ActiveRecord::Base.send :include, DinheiroActiveRecord
module BrDinheiro
end

View File

@@ -0,0 +1,296 @@
class Dinheiro
include Comparable
attr_reader :quantia
FORMATO_VALIDO_BR = /^([R|r]\$\s*)?(([+-]?\d{1,3}(\.?\d{3})*))?(\,\d{0,2})?$/
FORMATO_VALIDO_EUA = /^([R|r]\$\s*)?(([+-]?\d{1,3}(\,?\d{3})*))?(\.\d{0,2})?$/
SEPARADOR_MILHAR = "."
SEPARADOR_FRACIONARIO = ","
QUANTIDADE_DIGITOS = 3
PRECISAO_DECIMAL = 100
def initialize(quantia)
self.quantia = quantia
end
# Retorna o valor em Float quando uma coleção
# ou objeto é convertido para JSON
#
# Exemplo:
# produto = Produto.find 1
# produto.to_json // {"nome": "MacBook", "valor": 3500.0}
def as_json
self.to_f
end
# Retorna o valor armazenado em string.
#
# Exemplo:
# 1000.to_s ==> '1.000,00'
def to_s
inteiro_com_milhar(parte_inteira) + parte_decimal
end
# Compara com outro dinheiro se eh igual.
#
# Exemplo:
# um_real = Dinheiro.new(1)
# um_real == Dinheiro.new(1) ==> true
# um_real == Dinheiro.new(2) ==> false
def ==(outro_dinheiro)
begin
outro_dinheiro = Dinheiro.new(outro_dinheiro) unless outro_dinheiro.kind_of?(Dinheiro)
rescue
return false
end
@quantia == outro_dinheiro.quantia
end
# Compara com outro dinheiro se eh maior ou menor.
#
# Exemplo:
# 1.real < 2.reais ==> true
# 1.real > 2.reais ==> false
# 2.real < 1.reais ==> false
# 2.real > 1.reais ==> true
def <=>(outro_dinheiro)
outro_dinheiro = Dinheiro.new(outro_dinheiro) unless outro_dinheiro.kind_of?(Dinheiro)
@quantia <=> outro_dinheiro.quantia
end
# Retorna a adicao entre dinheiros.
#
# Exemplo:
# 1.real + 1.real == 2.reais
# 1.real + 1 == 2.reais
def +(outro)
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia + quantia_de(outro)))
end
# Retorna a subtracao entre dinheiros.
#
# Exemplo:
# 10.reais - 2.reais == 8.reais
# 10.reais - 2 == 8.reais
def -(outro)
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia - quantia_de(outro)))
end
# Retorna a multiplicacao entre dinheiros.
#
# Exemplo:
# 5.reais * 2 == 10.reais
# 5.reais * 2.reais == 10.reais
def *(outro)
return Dinheiro.new(to_f * outro) unless outro.kind_of? Dinheiro
outro * to_f
end
# Retorna a divisao entre dinheiros.
#
# Exemplo:
# 5.reais / 2 == (2.5).reais
# 5.reais / 2.reais == DivisaPorNaoEscalarError
# 5.reais / 0 == ZeroDivisionError
#
# Veja também o método parcelar
def /(outro)
raise DivisaPorNaoEscalarError unless outro.kind_of?(Numeric)
return @quantia/outro if outro == 0
Dinheiro.new(to_f / outro.to_f)
end
# Retorna um array de dinheiro com as parcelas
#
# Exemplo:
# 6.reais.parcelar(2) == [3.reais, 3.reais]
# 6.reais.parcelar(2.reais) == DisivaPorNaoEscalarError
# 6.reais.parcelar(0) == ZeroDivisionError
def parcelar(numero_de_parcelar)
raise DivisaPorNaoEscalarError unless numero_de_parcelar.kind_of?(Integer)
resto = @quantia % numero_de_parcelar
valor_menor = Dinheiro.new((@quantia/numero_de_parcelar)/100.0)
valor_maior = Dinheiro.new((@quantia/numero_de_parcelar+1)/100.0)
[valor_menor] * (numero_de_parcelar - resto) + [valor_maior] * resto
end
# Escreve o valor por extenso.
#
# Exemplo:
# 1.real.to_extenso ==> 'um real'
# (100.58).to_extenso ==> 'cem reais e cinquenta e oito centavos'
def to_extenso
(@quantia/100.0).por_extenso_em_reais
end
# Alias para o metodo to_extenso.
alias_method :por_extenso, :to_extenso
# Alias para o metodo to_extenso.
alias_method :por_extenso_em_reais, :to_extenso
# Verifica se o valor é zero.
def zero?
to_f.zero?
end
# Retorna um Float.
def to_f
to_s.gsub('.', '').gsub(',', '.').to_f
end
def coerce(outro)#:nodoc:
[ Dinheiro.new(outro), self ]
end
# Retorna a própria instância/
def real
self
end
# Alias para real.
alias_method :reais, :real
# Retorna uma string formatada com sigla em valor monetário.
# Exemplo:
# Dinheiro.new(1).real_formatado ==> 'R$ 1,00'
# Dinheiro.new(-1).real_formatado ==> 'R$ -1,00'
def real_formatado
"R$ #{to_s}"
end
# Alias para real_formatado.
alias_method :reais_formatado, :real_formatado
# Retorna uma string formatada com sigla em valor contábil.
#
# Exemplo:
# Dinheiro.new(1).real_contabil ==> 'R$ 1,00'
# Dinheiro.new(-1).real_contabil ==> 'R$ (1,00)'
def real_contabil
"R$ " + contabil
end
# Alias para real_contabil.
alias_method :reais_contabeis, :real_contabil
# Retorna uma string formatada sem sigla.
#
# Exemplo:
# Dinheiro.new(1).contabil ==> '1,00'
# Dinheiro.new(-1).contabil ==> '(1,00)'
def contabil
if @quantia >= 0
to_s
else
"(" + to_s[1..-1] + ")"
end
end
# Method missing para retorna um BigDecinal quando chamada .
def method_missing(symbol, *args) #:nodoc:
#Ex: Chama ao método valor_decimal()
if (symbol.to_s =~ /^(.*)_decimal$/) && args.size == 0
BigDecimal.new quantia_sem_separacao_milhares.gsub(',','.')
else
super.method_missing(symbol, *args)
end
end
private
def quantia_de(outro)
outro = outro.to_f if outro.kind_of?(BigDecimal)
return outro.quantia if outro.kind_of?(Dinheiro)
(outro * 100).round
end
def transforma_em_string_que_represente_a_quantia(quantia)
if /^([+-]?)(\d)$/ =~ quantia.to_s
return "#{$1}0.0#{$2}"
end
/^([+-]?)(\d*)(\d\d)$/ =~ quantia.to_s
"#{$1}#{$2.to_i}.#{$3}"
end
def quantia=(quantia)
raise DinheiroInvalidoError unless quantia_valida?(quantia)
quantia = quantia.to_f if quantia.kind_of?(BigDecimal)
@quantia = (quantia * 100).round if quantia.kind_of?(Numeric)
@quantia = extrai_quantia_como_inteiro(quantia) if quantia.kind_of?(String)
end
def parte_inteira
quantia_sem_separacao_milhares[0,quantia_sem_separacao_milhares.length-QUANTIDADE_DIGITOS]
end
def parte_decimal
quantia_sem_separacao_milhares[-QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS]
end
def inteiro_com_milhar(inteiro)
return inteiro if quantidade_de_passos(inteiro) == 0
resultado = ""
quantidade_de_passos(inteiro).times do |passo|
resultado = "." + inteiro[-QUANTIDADE_DIGITOS + passo * -QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS] + resultado
end
resultado = inteiro[0, digitos_que_sobraram(inteiro)] + resultado
resultado.gsub(/^(-?)\./, '\1')
end
def quantia_sem_separacao_milhares
sprintf("%.2f", (@quantia.to_f / PRECISAO_DECIMAL)).gsub(SEPARADOR_MILHAR, SEPARADOR_FRACIONARIO)
end
def quantidade_de_passos(inteiro)
resultado = inteiro.length / QUANTIDADE_DIGITOS
resultado = (resultado - 1) if inteiro.length % QUANTIDADE_DIGITOS == 0
resultado
end
def digitos_que_sobraram(inteiro)
inteiro.length - (quantidade_de_passos(inteiro) * QUANTIDADE_DIGITOS)
end
def quantia_valida?(quantia)
return false if quantia.kind_of?(String) && !quantia_respeita_formato?(quantia)
quantia.kind_of?(String) || quantia.kind_of?(Numeric)
end
def extrai_quantia_como_inteiro(quantia)
if FORMATO_VALIDO_BR =~ quantia
return sem_milhar($2, $5, '.')
end
if FORMATO_VALIDO_EUA =~ quantia
return sem_milhar($2, $5, ',')
end
end
def sem_milhar(parte_inteira, parte_decimal, delimitador_de_milhar)
(inteiro(parte_inteira, delimitador_de_milhar) + decimal(parte_decimal)).to_i
end
def inteiro(inteiro_com_separador_milhar, separador)
return inteiro_com_separador_milhar.gsub(separador, '') unless inteiro_com_separador_milhar.blank?
""
end
def decimal(parte_fracionaria)
unless parte_fracionaria.blank?
return sem_delimitador_decimal(parte_fracionaria) if parte_fracionaria.length == 3
return sem_delimitador_decimal(parte_fracionaria) + "0" if parte_fracionaria.length == 2
end
"00"
end
def sem_delimitador_decimal(parte_fracionaria)
"#{parte_fracionaria}".gsub(/[.|,]/, '')
end
def quantia_respeita_formato?(quantia)
return true if FORMATO_VALIDO_BR.match(quantia) || FORMATO_VALIDO_EUA.match(quantia)
false
end
end

View File

@@ -0,0 +1,45 @@
module DinheiroActiveRecord#:nodoc:
def self.included(base)#:nodoc:
base.extend ClassMethods
end
module ClassMethods#:nodoc:
def usar_como_dinheiro(*args)#:nodoc:
unless args.size.zero?
args.each do |name|
composed_of name, :class_name => 'Dinheiro', :mapping => [name.to_s, "valor_decimal"], :allow_nil => true
name = name.to_s
module_eval <<-ADICIONANDO_METODO
validate :#{name}_valido?
def #{name}_valido?
begin
@#{name}.to_s.reais
rescue Exception => e
self.errors.add('#{name}', e.message)
end
end
def #{name}=(value)
if value.nil?
write_attribute('#{name}', nil)
elsif value.kind_of?(Dinheiro)
write_attribute('#{name}', value.valor_decimal)
else
begin
write_attribute('#{name}', value.reais.valor_decimal)
rescue
@#{name} = value
end
end
end
def #{name}
read_attribute(:#{name}).reais if read_attribute(:#{name})
end
ADICIONANDO_METODO
end
end
end
end
end

View File

@@ -0,0 +1,51 @@
module DinheiroUtil
# Ao incluir o modulo, cria aliases para_dinheiro
def self.included(base)
base.class_eval do
# Alias para para_dinheiro
alias_method :real, :para_dinheiro
# Alias para para_dinheiro
alias_method :reais, :para_dinheiro
end
end
# Transforma numero em dinheiro
#
# Exemplo:
# 1.para_dinheiro.class ==> Dinheiro
def para_dinheiro
Dinheiro.new(self)
end
# Retorna string formatada com simbolo monetario
#
# Exemplo:
# 1.real_contabil ==> 'R$ 1,00'
# -1.real_contabil ==> 'R$ (1,00)'
def real_contabil
Dinheiro.new(self).real_contabil
end
# Retorna string formatada com simbolo monetario
#
# Exemplo:
# 2.reais_contabeis ==> 'R$ 2,00'
# -2.reais_contabeis ==> 'R$ 2,00'
def reais_contabeis
Dinheiro.new(self).reais_contabeis
end
# Retorna string formatada com simbolo monetario
#
# Exemplo:
# 1.contabil ==> '1,00'
# -1.contabil ==> '(1,00)'
def contabil
Dinheiro.new(self).contabil
end
end
Numeric.send(:include, DinheiroUtil)
String.send(:include, DinheiroUtil)

View File

@@ -0,0 +1,6 @@
def cria_excecao(classe, mensagem)
eval "class #{classe}; def initialize; super('#{mensagem}'); end; end"
end
cria_excecao("DinheiroInvalidoError < ArgumentError", "O valor deve estar preenchido e no formato correto. Ex.: 100.00 .")
cria_excecao("DivisaPorNaoEscalarError < ArgumentError", "So eh possivel dividir dinheiro por numeros.")

View File

@@ -0,0 +1,24 @@
class NilClass
def valor
0.real
end
def contabil
0.contabil
end
def para_dinheiro
0.real
end
def reais
0.real
end
def real
0.real
end
end

View File

@@ -0,0 +1,10 @@
module BrDinheiro
module VERSION #:nodoc:
MAJOR = 3
MINOR = 3
TINY = 0
STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
end
end

View File

@@ -0,0 +1 @@
require File.dirname(__FILE__) + '/../lib/brdinheiro'

View File

@@ -0,0 +1,12 @@
class CreateLancamentos < ActiveRecord::Migration
def self.up
create_table :lancamentos do |t|
t.column :descricao, :string, :null => false
t.column :valor, :decimal, :precision => 14, :scale => 2
end
end
def self.down
drop_table :lancamentos
end
end

View File

@@ -0,0 +1,8 @@
* Como utilizar a classe Dinheiro em uma classe de modelo da aplicação?
Vamos imaginar um exemplo. Suponha uma classe Lancamento que possui dois atributos: descricao e valor.
Veja a migration utilizada no arquivo(em samples): 001_create_lancamentos.rb
Veja a classe de modelo Lancamento no arquivo(em samples): lancamento.rb

View File

@@ -0,0 +1,5 @@
class Lancamento < ActiveRecord::Base
usar_como_dinheiro :valor
end

View File

@@ -0,0 +1,59 @@
require 'active_record'
module ActiveRecord
class BaseWithoutTable < Base
self.abstract_class = true
def create_or_update
errors.empty?
end
def save
self.valid?
end
def column_for_attribute(name)
self.class.columns_hash[name.to_s]
end
class << self
def columns()
@columns ||= []
end
def column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
reset_column_information
end
# Do not reset @columns
def reset_column_information
undefine_attribute_methods
@column_names = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
end
def columns_hash
hash = {}
columns.each do |column|
hash[column.name] = column
end
hash
end
def column_defaults
defaults = {}
columns.each do |column|
defaults[column.name.to_sym] = nil
end
defaults
end
end
private
def self.attributes_protected_by_default
[]
end
end
end

View File

@@ -0,0 +1,12 @@
# encoding: UTF-8
require File.dirname(__FILE__) + '/test_helper.rb'
class TestBrDinheiro < Test::Unit::TestCase
def setup
end
def test_truth
assert true
end
end

View File

@@ -0,0 +1,60 @@
require File.dirname(__FILE__) + '/test_helper'
require File.dirname(__FILE__) + '/active_record/base_without_table'
class Carteira < ActiveRecord::BaseWithoutTable
column :saldo, :decimal
usar_como_dinheiro :saldo
end
class DinheiroActiveRecordTest < Test::Unit::TestCase
def setup
@carteira = Carteira.new
end
def teste_se_aceita_dinheiro
@carteira.saldo = 8.reais
assert @carteira.save
assert_equal 8.reais, @carteira.saldo
end
def teste_se_aceita_numero
@carteira.saldo = 30
assert @carteira.save
assert_equal 30.reais, @carteira.saldo
end
def teste_se_rejeita_valor_invalido
@carteira.saldo = 30
assert @carteira.save
@carteira.saldo = 'bla'
assert_false @carteira.save
assert_equal ["O valor deve estar preenchido e no formato correto. Ex.: 100.00 ."], @carteira.errors['saldo']
end
def teste_se_trata_nulo_corretamente
assert_nil @carteira.saldo
@carteira.saldo = nil
assert_nil @carteira.saldo
@carteira.save
assert_nil @carteira.saldo
end
def test_se_cria_carteira_corretamente_quando_recebe_parametros
carteira = Carteira.new(:saldo => "1")
assert_equal 1.real, carteira.saldo
end
def test_se_atualiza_valor_inicializado
carteira = Carteira.new(:saldo => 10.reais)
carteira.saldo += 5.reais
assert_equal 15.reais, carteira.saldo
end
def test_se_retorna_objeto_do_mesmo_tipo
@carteira.saldo = 10.reais
assert_equal 10.reais.class, @carteira.saldo.class
end
end

View File

@@ -0,0 +1,498 @@
require File.dirname(__FILE__) + '/test_helper'
class DinheiroTest < Test::Unit::TestCase
CONTABIL = { "(2,00)" => -2,
"2,00" => 2,
"0,00" => 0,
"0,32" => 0.32,
"(0,01)" => -0.01 }
REAL_CONTABIL = { "R$ (1,00)" => -1,
"R$ (0,12)" => -0.12,
"R$ 1,00" => 1,
"R$ 1,00" => 1,
"R$ 1,00" => 1,
"R$ 0,00" => 0 }
SOMA = { 0.real => 0.real + 0.real,
1.real => 0.real + 1.real,
1.real => 1.real + 0.real,
2.reais => 1.real + 1.real,
2.reais => 0.real + 2.reais,
2.reais => 2.reais + 0.real,
0.real => 2.real + -2.real,
0.real => 0.real + BigDecimal.new("0"),
0.3.real => 0.real + 0.3.real,
-0.3.real => 0.real + -0.3.real,
-0.03.real => 0 + -0.03.real,
-0.03.real => 0.real + -0.03,
-1.03.real => -1.real + -0.03,
-1.03.real => -1.real + BigDecimal.new("-0.03") }
SUBTRACAO = { 0.real => 0.real - 0.real,
-1.real => 0.real - 1.real,
1.real => 1.real - 0.real,
0.real => 1.real - 1.real,
-2.reais => 0.real - 2.reais,
2.reais => 2.reais - 0.real,
-4.reais => -2.reais - 2.reais,
0.3.real => 0.3.real - 0.real,
0.03.real => 0.03.real - 0.real,
0.03.real => 0.06.real - 0.03.real,
-0.03.real => 0 - 0.03.real,
-0.03.real => 0.real - 0.03,
-1.03.real => -1.real - 0.03,
-1.03.real => -1.real - BigDecimal.new("0.03") }
MULTIPLICACAO = { 0.real => 0.real * 0,
0.real => 0.real * 1,
0.real => 0.real * -1,
1.real => 1.real * 1,
10.real => 10.real * 1,
100.real => 100.real * 1,
1000.real => 1000.real * 1,
1.real => -1.real * -1,
-1.real => 1.real * -1,
-1.real => -1.real * 1,
0.1.real => 1.real * 0.1,
0.01.real => 1.real * 0.01,
0.01.real => 1.real * 0.009,
0.01.real => 1.real * 0.005,
0.00.real => 1.real * 0.0049,
0.1.real => 0.1.real * 1,
0.01.real => 0.01.real * 1,
0.01.real => 0.009.real * 1,
0.00.real => 0.00049.real * 1,
0.real => 0.real * 0.real,
0.real => 0.real * BigDecimal("0"),
1.real => 1.real * 1.real,
1.real => 1.real * BigDecimal("1"),
1.real => 0.5.real * 2.real,
1.real => 0.5.real * BigDecimal("2"),
1.real => 1 * 1.real,
-1.real => -1 * 1.real,
1.real => -1 * -1.real,
0.01.real => 0.01 * 1.real,
0.01.real => 1.real * BigDecimal("0.01"),
0.01.real => BigDecimal("0.01") * 1.real }
DIVISAO = {
Dinheiro.new(0.33) => Dinheiro.new(1) / 3,
Dinheiro.new(33.33) => Dinheiro.new(100) / 3,
Dinheiro.new(50.00) => Dinheiro.new(100) / 2,
Dinheiro.new(0.25) => Dinheiro.new(0.5) / 2,
Dinheiro.new(0.17) => Dinheiro.new(0.5) / 3,
Dinheiro.new(0.33) => Dinheiro.new(0.33) / 1
}
PARCELAS = {
[Dinheiro.new(0.33), Dinheiro.new(0.33), Dinheiro.new(0.34)] => Dinheiro.new(1).parcelar(3),
[Dinheiro.new(33.33), Dinheiro.new(33.33), Dinheiro.new(33.34)] => Dinheiro.new(100).parcelar(3),
[Dinheiro.new(50.00), Dinheiro.new(50)] => Dinheiro.new(100).parcelar(2),
[Dinheiro.new(0.25), Dinheiro.new(0.25)] => Dinheiro.new(0.5).parcelar(2),
[Dinheiro.new(0.16), Dinheiro.new(0.17),Dinheiro.new(0.17)] => Dinheiro.new(0.5).parcelar(3),
[Dinheiro.new(0.33)] => Dinheiro.new(0.33).parcelar(1),
[Dinheiro.new(0.33)] => Dinheiro.new(0.33).parcelar(1),
}
QUANTIA_COM_FORMATO_VALIDO = [ "1211",
"1211.",
"1211.0",
"1211.23",
"1211,23",
"1.211",
"1.211,00",
"1.211,01",
"1.211,1",
"1.211,",
"1,",
"12,",
"32349898989912,",
"32.349.898.989.912,",
"32.349.898.989.912,1",
"32.349.898.989.912,12",
"1",
"1.00",
"1.01",
"1.1",
"1.",
".1",
".12",
"0.12",
"1.12",
"12.12",
"12.12",
"123.12",
"1,234.12",
"12,234.12",
"123,234.12",
"2,123,234.12",
",1",
",11",
",01",
"0,01" ]
QUANTIA_COM_FORMATO_INVALIDO = [ 'teste',
'12,123,99',
'12.123.99',
'1,123,99',
'1212,39.90' ]
COMPARACAO = [ 1.real < 2.reais,
1.real <= 2.reais,
2.real > 1.real,
2.real >= 1.real,
1.real == 1.real,
1.real >= 1.real,
1.real <= 1.real ]
COMPARACAO_COM_ESCALAR = [ 1.real < 2.00,
1.real <= 2.00,
2.real > 1.00,
2.real >= 1.00,
1.real == 1.00,
1.real >= 1.00,
1.real <= 1.00 ]
ARREDONDAMENTO = { 23049 => 230.49,
23049 => 230.4949999999,
23050 => 230.495 }
PONTO_NO_MILHAR = { "234.175.211" => "234175211",
"" => "",
"1" => "1",
"12" => "12",
"123" => "123",
"1.234" => "1234",
"12.345" => "12345",
"123.456" => "123456",
"123.112.211" => "123112211",
"1.234.567" => "1234567" }
QUANTIA_VALIDA = { "0,00" => 0 ,
"0,00" => 0.0 ,
"0,00" => "0" ,
"0,00" => "0,00" ,
"1,00" => 1 ,
"1,03" => 1.03 ,
"1,03" => "1,03" ,
"0,03" => ",03" ,
"0,30" => ",3" ,
"0,03" => ".03" ,
"0,30" => ".3" ,
"-0,30" => -0.3 ,
"-0,03" => -0.03 ,
"1,00" => "1,00" ,
"-1,00" => -1 ,
"-1,00" => -1.0 ,
"-1,00" => "-1" ,
"-1,00" => "-1,00" ,
"-2,30" => "-2,30" ,
"2,30" => "2,30" ,
"2,30" => 2.30 ,
"2,30" => 2.3 ,
"1.211,00" => "1211,00" ,
"1.211,01" => "1211,01" ,
"1.211,50" => "1211,5" ,
"1.211,00" => "1211," ,
"1.211,00" => "1211" ,
"1.211,00" => "1211.00" ,
"1.211,01" => "1211.01" ,
"1.211,20" => "1211.2" ,
"1.211,00" => "1211." ,
"1.211,00" => "1211" ,
"1.211,00" => "1.211" ,
"123.112.211,35" => "123112211,35" ,
"-123.112.211,35" => "-123112211,35" ,
"123.112.211,35" => "+123112211,35" }
PARTE_INTEIRA = [ -1, -123112211, 0, 1, 12344545 ]
def setup
tornar_metodos_publicos Dinheiro
@dinheiro = 1.real
end
def testa_se_cria_dinheiro_a_partir_de_quantias_validos
QUANTIA_VALIDA.each do |esperado, quantia|
assert_equal esperado, Dinheiro.new(quantia).to_s, "Deveria ter vindo o quantia: #{esperado} quando recebeu #{quantia}"
end
end
# coloca_ponto_no_milhar
def testa_se_coloca_ponto_no_milhar
PONTO_NO_MILHAR.each do |esperado, quantia|
{ esperado => quantia,
"-#{esperado}" => "-#{quantia}" }.each do |esperado, quantia|
assert_equal esperado, @dinheiro.inteiro_com_milhar(quantia)
end
end
end
def testa_to_s
assert_equal "1,00", Dinheiro.new(1).to_s
assert_equal "1.000,00", Dinheiro.new(1000).to_s
end
# real
def testa_real_nao_eh_dinheiro
assert_kind_of(Dinheiro, 1.real)
end
def testa_real_eh_dinheiro
assert_kind_of(Dinheiro, Dinheiro.new(1).real)
end
def testa_real_contabil
REAL_CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).real_contabil }
end
def testa_reais_contabeis
REAL_CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).reais_contabeis }
end
# reais
def testa_reais
assert_equal Dinheiro.new("2,00"), Dinheiro.new(2).reais
end
def testa_contabil
CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).contabil }
end
# real_formatado
def test_real_formatado
assert_equal "R$ 2,00", Dinheiro.new(2).real_formatado
end
# ==
def testa_igualdade
assert_equal Dinheiro.new(1), Dinheiro.new(1)
end
def testa_igualdade_quando_passa_possivel_dinheiro
assert_equal Dinheiro.new(1), 1.0
end
def testa_igualdade_quando_passa_algo_que_nao_seja_dinheiro
assert_equal false, Dinheiro.new(1) == 'salario'
end
# / (divisao)
def testa_divisao
DIVISAO.each { |parcelas, divisao| assert_equal parcelas, divisao }
end
def testa_divisao_por_zero
assert_raise(ZeroDivisionError) { 1.real / 0 }
end
def testa_divisao_por_algo_que_nao_seja_um_escalar
assert_raise(DivisaPorNaoEscalarError) { 10.reais / 2.reais }
end
# parcelar
def testa_parcelar
PARCELAS.each { |parcelas, divisao| assert_equal parcelas, divisao }
end
def testa_parcelar_por_zero
assert_raise(ZeroDivisionError) { 1.real.parcelar 0 }
end
def testa_parcelar_por_algo_que_nao_seja_um_escalar
assert_raise(DivisaPorNaoEscalarError) { 10.reais.parcelar(2.reais) }
end
# initialize
def testa_se_cria_dinheiro_a_partir_de_float
verifica_se_cria_dinheiro_para 1.0
end
def testa_se_cria_dinheiro_a_partir_de_fixnum
verifica_se_cria_dinheiro_para 1
end
def testa_se_cria_dinheiro_a_partir_de_bigdecimal
verifica_se_cria_dinheiro_para BigDecimal.new("1")
end
def testa_se_cria_dinheiro_a_partir_de_string
verifica_se_cria_dinheiro_para "1"
end
def testa_se_rejeita_criacao_de_dinheiro_a_partir_de_string_invalida
QUANTIA_COM_FORMATO_INVALIDO.each do |quantia|
assert_raise DinheiroInvalidoError, "Deveria ter rejeitado [#{quantia}]" do
Dinheiro.new quantia
end
end
end
# + (soma)
def testa_soma
SOMA.each{ |esperado, soma| assert_equal esperado, soma }
end
# - (subtracao)
def testa_subtracao
SUBTRACAO.each { |esperado, subtracao| assert_equal esperado, subtracao }
end
# * (multiplicacao)
def testa_multiplicacao
MULTIPLICACAO.each { |esperado, multiplicacao| assert_equal esperado, multiplicacao }
end
# quantia_de
def testa_quantia_de
assert_equal 0, @dinheiro.quantia_de(0.real)
assert_equal 0, @dinheiro.quantia_de(0)
end
#por_extenso
def testa_por_extenso
assert_equal 'um real', 1.real.por_extenso
assert_equal 'um centavo', (0.01).real.por_extenso
assert_equal 'cem reais', 100.real.por_extenso
assert_equal 'cem reais e um centavo', (100.01).real.por_extenso
end
#por_extenso_em_reais
def testa_por_extenso_em_reais
assert_equal 'um real', 1.real.por_extenso_em_reais
assert_equal 'um centavo', (0.01).real.por_extenso_em_reais
assert_equal 'cem reais', 100.real.por_extenso_em_reais
assert_equal 'cem reais e um centavo', (100.01).real.por_extenso_em_reais
end
def testa_por_extenso_negativo
assert_equal 'um real negativo', -1.real.por_extenso_em_reais
assert_equal 'um centavo negativo', (-0.01).real.por_extenso_em_reais
assert_equal 'cem reais negativos', -100.real.por_extenso_em_reais
assert_equal 'cem reais e um centavo negativos', (-100.01).real.por_extenso_em_reais
assert_equal 'cento e dez reais negativos', (-110).real.por_extenso_em_reais
assert_equal 'vinte e dois reais negativos', -22.reais.por_extenso
assert_not_equal 'vinte e dois centavos negativos', -22.reais.por_extenso
assert_not_equal 'vinte e dois centavos', -22.reais.por_extenso
end
# to_f
def testa_to_f
assert_equal 2.30, 2.30.real.to_f
assert_equal 1000, 1000.real.to_f
end
# quantia_respeita_formato?
def testa_se_quantia_respeita_formato
QUANTIA_COM_FORMATO_VALIDO.each { |quantia| verifica_se_quantia_respeita_formato quantia }
end
# >, <, == (ordenacao)
def testa_comparacao_entre_dinheiro
COMPARACAO.each { |comparacao| assert comparacao }
end
def testa_comparacao_entre_possivel_dinheiro
COMPARACAO_COM_ESCALAR.each { |comparacao_com_escalar| assert comparacao_com_escalar }
end
# decimal
def testa_decimal_quando_todas_as_casas_estao_preenchidas
verifica_decimal("12")
end
def testa_decimal_quando_apenas_uma_das_casas_esta_preenchida
verifica_decimal("10", "1")
end
def testa_decimal_quando_nenhuma_das_casas_esta_preenchida
verifica_decimal("00", "")
end
def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_tres_digitos
verifica_se_transforma_em_string_corretamente "1.23", 123
end
def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_dois_digitos
verifica_se_transforma_em_string_corretamente "0.12", 12
end
def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_um_digito
verifica_se_transforma_em_string_corretamente "0.03", 3
end
def testa_se_transforma_em_string_para_valores_especiais
verifica_se_transforma_em_string_corretamente "-123112211.00", -12311221100
end
# parte_inteira
def testa_parte_inteira
PARTE_INTEIRA.each { |quantia| assert_equal "#{quantia}", Dinheiro.new(quantia).parte_inteira }
end
def testa_se_arredonda_valores_corretamente
ARREDONDAMENTO.each do |esperado, quantia|
assert_equal esperado, Dinheiro.new(quantia).quantia, "Deveria retornar #{esperado} para #{quantia}"
end
end
def testa_se_valor_decimal_cria_o_big_decimal_corretamente
assert_equal BigDecimal.new("1234.56"), Dinheiro.new("1234,56").valor_decimal
end
def testa_soma_de_dinheiro_com_big_decimal
assert_equal Dinheiro.new(200), BigDecimal.new("100").reais + "100".reais
end
def testa_zero_quando_eh_zero
assert Dinheiro.new(0).zero?
end
def testa_zero_quando_nao_eh_zero
assert !Dinheiro.new(1).zero?
end
private
def verifica_se_transforma_em_string_corretamente(quantia_esperada, quantia)
assert_equal quantia_esperada, @dinheiro.transforma_em_string_que_represente_a_quantia(quantia)
end
def verifica_decimal(esperado, quantia = esperado)
assert_equal esperado, @dinheiro.decimal("." + quantia)
assert_equal esperado, @dinheiro.decimal("," + quantia)
assert_equal esperado, @dinheiro.decimal(quantia) if quantia.blank?
end
def verifica_se_quantia_respeita_formato(quantia)
formatos_validos(quantia).each do |quantia_str|
assert 1.real.quantia_respeita_formato?(quantia_str), "O sistema deveria considerar o quantia '#{quantia_str}' dentro do formato valido."
end
end
def formatos_validos(quantia)
formatos_validos = []
quantias_validas(quantia).each do |quantia|
formatos_validos << quantia
[ "R$", "r$" ].each do |simbolo|
[ "", " ", " " ].each do |espacos|
formatos_validos << "#{simbolo}#{espacos}#{quantia}"
end
end
end
formatos_validos
end
def quantias_validas(quantia)
return [quantia] if [ ".", "," ].include?(quantia[0..0])
[ quantia, "-#{quantia}" ]
end
def verifica_se_cria_dinheiro_para(quantia)
assert quantia.para_dinheiro.kind_of?(Dinheiro)
end
end

View File

@@ -0,0 +1,29 @@
require File.dirname(__FILE__) + '/test_helper'
class NilClassTest < Test::Unit::TestCase
def test_real
assert_equal Dinheiro.new(0), nil.real
end
def test_reais
assert_equal Dinheiro.new(0), nil.reais
end
def test_para_dinheiro
assert_equal Dinheiro.new(0), nil.para_dinheiro
end
def test_valor
assert_equal Dinheiro.new(0), nil.valor
end
def test_contabil
assert_equal "0,00", nil.contabil
end
end

View File

@@ -0,0 +1,23 @@
require 'test/unit'
require File.dirname(__FILE__) + '/../../brnumeros/lib/brnumeros'
require File.dirname(__FILE__) + '/../lib/brdinheiro'
def tornar_metodos_publicos(clazz)
clazz.class_eval do
private_instance_methods.each { |instance_method| public instance_method }
private_methods.each { |method| public_class_method method }
end
end
def p80 text
p '*'*80
p text
p '*'*80
end
class Test::Unit::TestCase
def assert_false(boolean, message=nil)
assert_block("assert should not be called with a block.") { !block_given? }
assert_block(build_message(message, "<?> is not false.", boolean)) { !boolean }
end
end