Skip to content

Instantly share code, notes, and snippets.

@haru01
Created June 17, 2010 22:50
Show Gist options
  • Save haru01/442912 to your computer and use it in GitHub Desktop.
Save haru01/442912 to your computer and use it in GitHub Desktop.
require 'rubygems'
require 'mongo_mapper'
require 'pp'
MongoMapper.database = 'accounting-patterns'
# see accounting transaction http://martinfowler.com/apsupp/accounting.pdf
class AccountService
class AcccountingRunner
def initialize
@transaction = AccountingTransaction.new
end
attr_accessor :transaction
def from name, amount
entry = Entry.new(:amount => -amount)
entry.tmp_account = Account.find_by_name name
entry.account = Account.find_by_name name
@transaction.entries << entry
end
def to name, amount
entry = Entry.new(:amount => amount)
entry.tmp_account = Account.find_by_name name
entry.account = Account.find_by_name name
@transaction.entries << entry
end
def valid?
@transaction.total_zero?
end
def destory
@transaction.entries.delete_all
@transaction.delete
end
end
def send_money
runner = AcccountingRunner.new
yield runner
if runner.valid?
# runner.transaction.save! saveしてないんだが。。。
else
runner.destory
raise "バランスしません"
end
end
end
class Account
include MongoMapper::Document
key :name, String, :required => true, :unique => true
many :entries, :order => 'create_at asc'
def balance
entries.inject(0) do |result, entry|
result + entry.amount
end
end
end
class Entry
attr_accessor :tmp_account
include MongoMapper::Document
key :amount, Integer
belongs_to :account
timestamps!
end
class AccountingTransaction
include MongoMapper::Document
many :entries
timestamps!
def total_zero?
entries.inject(0) {|result, entry| result + entry.amount } == 0
end
end
describe "accounting transaction" do
before(:each) do
init_db
end
let(:account_service) { AccountService.new}
context "送金 佐藤さんから田中さんへの例" do
before(:each) do
account_service.send_money do |runner|
runner.from("佐藤", 99)
runner.to("田中", 99)
end
end
it "移動の総量は0であること" do
trn = AccountingTransaction.last :order => "timestamps"
trn.should be_total_zero
end
it "送り元の balance が減っていること" do
sato = Account.find_by_name("佐藤")
sato.balance.should == 200 - 99
end
it "送り先の balance が増えていること" do
sato = Account.find_by_name("田中")
sato.balance.should == 300 + 99
end
end
context "送金 鈴木さんから 佐藤さん,田中さんへの例" do
before(:each) do
account_service.send_money do |runner|
runner.from("鈴木", 260)
runner.to("佐藤", 110)
runner.to("田中", 150)
end
end
it "移動の総量は0であること" do
AccountingTransaction.count.should == 1
trn = AccountingTransaction.last :order => "timestamps"
trn.should be_total_zero
end
it "送り元の balance が減っていること" do
a = Account.find_by_name("鈴木")
a.balance.should == 700 - 260
end
it "送り先1の balance が増えていること" do
a = Account.find_by_name("佐藤")
a.balance.should == 200 + 110
end
it "送り先2の balance が増えていること" do
a = Account.find_by_name("田中")
a.balance.should == 300 + 150
end
end
context "バランスしない 送金のケース" do
it "初期か状態であること" do
AccountingTransaction.count.should == 0
lambda {
account_service.send_money do |runner|
runner.from("鈴木", 260)
runner.to("田中", 150)
end
}.should raise_error("バランスしません")
should_init
end
def should_init
suzuki = Account.find_by_name("鈴木")
suzuki.balance.should == 700
sato = Account.find_by_name("佐藤")
sato.balance.should == 200
tanaka = Account.find_by_name("田中")
tanaka.balance.should == 300
end
end
def init_db
Account.delete_all
Entry.delete_all
AccountingTransaction.delete_all
sato = Account.new (:name => "佐藤")
sato.entries << Entry.new(:amount => 200)
# sato.save saveしていないんだが。。。。 こうゆうもの?
tanaka = Account.new (:name => "田中")
tanaka.entries << Entry.new(:amount => 300)
# tanaka.save
tanaka = Account.new (:name => "鈴木")
tanaka.entries << Entry.new(:amount => 700)
#tanaka.save
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment