subledger 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +9 -0
- data/LICENSE.txt +12 -0
- data/README.md +136 -0
- data/Rakefile +5 -0
- data/bin/subledger +6 -0
- data/lib/subledger/actor.rb +32 -0
- data/lib/subledger/collection_name.rb +25 -0
- data/lib/subledger/domain/account.rb +168 -0
- data/lib/subledger/domain/balance.rb +102 -0
- data/lib/subledger/domain/book.rb +111 -0
- data/lib/subledger/domain/category.rb +157 -0
- data/lib/subledger/domain/control.rb +180 -0
- data/lib/subledger/domain/formatters.rb +31 -0
- data/lib/subledger/domain/identity.rb +159 -0
- data/lib/subledger/domain/journal_entry.rb +293 -0
- data/lib/subledger/domain/key.rb +113 -0
- data/lib/subledger/domain/line.rb +272 -0
- data/lib/subledger/domain/org.rb +110 -0
- data/lib/subledger/domain/report.rb +247 -0
- data/lib/subledger/domain/report_rendering.rb +233 -0
- data/lib/subledger/domain/roles/activatable.rb +11 -0
- data/lib/subledger/domain/roles/archivable.rb +11 -0
- data/lib/subledger/domain/roles/attributable.rb +14 -0
- data/lib/subledger/domain/roles/collectable.rb +175 -0
- data/lib/subledger/domain/roles/creatable.rb +58 -0
- data/lib/subledger/domain/roles/describable.rb +33 -0
- data/lib/subledger/domain/roles/describable_report_rendering.rb +50 -0
- data/lib/subledger/domain/roles/identifiable.rb +15 -0
- data/lib/subledger/domain/roles/postable.rb +54 -0
- data/lib/subledger/domain/roles/progressable.rb +11 -0
- data/lib/subledger/domain/roles/readable.rb +34 -0
- data/lib/subledger/domain/roles/restable.rb +69 -0
- data/lib/subledger/domain/roles/storable.rb +30 -0
- data/lib/subledger/domain/roles/timeable.rb +18 -0
- data/lib/subledger/domain/roles/updatable.rb +35 -0
- data/lib/subledger/domain/roles/versionable.rb +35 -0
- data/lib/subledger/domain/roles.rb +16 -0
- data/lib/subledger/domain/value/credit.rb +16 -0
- data/lib/subledger/domain/value/debit.rb +16 -0
- data/lib/subledger/domain/value/zero.rb +24 -0
- data/lib/subledger/domain/value.rb +111 -0
- data/lib/subledger/domain.rb +95 -0
- data/lib/subledger/exception_handler.rb +65 -0
- data/lib/subledger/interface/client.rb +295 -0
- data/lib/subledger/interface/dispatcher.rb +20 -0
- data/lib/subledger/interface.rb +2 -0
- data/lib/subledger/path.rb +106 -0
- data/lib/subledger/rest.rb +128 -0
- data/lib/subledger/server.rb +3 -0
- data/lib/subledger/store/api/errors.rb +95 -0
- data/lib/subledger/store/api/roles/activate.rb +21 -0
- data/lib/subledger/store/api/roles/archive.rb +21 -0
- data/lib/subledger/store/api/roles/balance.rb +39 -0
- data/lib/subledger/store/api/roles/categories.rb +51 -0
- data/lib/subledger/store/api/roles/collect.rb +58 -0
- data/lib/subledger/store/api/roles/create.rb +26 -0
- data/lib/subledger/store/api/roles/create_and_post.rb +35 -0
- data/lib/subledger/store/api/roles/create_identity.rb +39 -0
- data/lib/subledger/store/api/roles/create_line.rb +24 -0
- data/lib/subledger/store/api/roles/first_and_last_line.rb +31 -0
- data/lib/subledger/store/api/roles/post.rb +25 -0
- data/lib/subledger/store/api/roles/progress.rb +21 -0
- data/lib/subledger/store/api/roles/read.rb +19 -0
- data/lib/subledger/store/api/roles/reports.rb +77 -0
- data/lib/subledger/store/api/roles/update.rb +24 -0
- data/lib/subledger/store/api/store.rb +103 -0
- data/lib/subledger/store/api.rb +20 -0
- data/lib/subledger/store.rb +236 -0
- data/lib/subledger/supervisor.rb +21 -0
- data/lib/subledger/uuid.rb +52 -0
- data/lib/subledger/version.rb +4 -0
- data/lib/subledger.rb +234 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/subledger_account_spec.rb +354 -0
- data/spec/subledger_book_spec.rb +130 -0
- data/spec/subledger_category_spec.rb +203 -0
- data/spec/subledger_control_spec.rb +43 -0
- data/spec/subledger_identity_spec.rb +47 -0
- data/spec/subledger_journal_entry_spec.rb +417 -0
- data/spec/subledger_key_spec.rb +43 -0
- data/spec/subledger_org_spec.rb +68 -0
- data/spec/subledger_report_spec.rb +295 -0
- data/spec/subledger_spec.rb +101 -0
- data/subledger.gemspec +52 -0
- metadata +205 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
Test 'Subledger' do
|
4
|
+
|
5
|
+
let(:factory) { Subledger::Factory.create }
|
6
|
+
|
7
|
+
Instance do
|
8
|
+
subject { factory.client }
|
9
|
+
|
10
|
+
Test 'Book' do
|
11
|
+
# POST /books
|
12
|
+
RespondsTo 'book.create' do
|
13
|
+
ByReturning 'an instance of Subledger::Domain::ActiveBook' do
|
14
|
+
subject.book.create( factory.book_args ).
|
15
|
+
must_be_instance_of Subledger::Domain::ActiveBook
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# GET /books/:id
|
20
|
+
RespondsTo 'books.read' do
|
21
|
+
let(:book) { factory.create_book :active }
|
22
|
+
|
23
|
+
ByReturning 'a kind of Subledger::Domain::Book' do
|
24
|
+
subject.books.read( :id => book.id ).
|
25
|
+
must_be_instance_of Subledger::Domain::ActiveBook
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# PATCH /books/:id
|
30
|
+
RespondsTo 'books.update' do
|
31
|
+
let(:book ) { factory.create_book :active }
|
32
|
+
let(:description) { 'updated description' }
|
33
|
+
let(:args ) { book.attributes }
|
34
|
+
let(:update_args) { args.merge :description => description,
|
35
|
+
:version => 2 }
|
36
|
+
|
37
|
+
ByReturning 'an updated kind of Subledger::Domain::Book' do
|
38
|
+
patched = subject.books.update update_args
|
39
|
+
|
40
|
+
patched.must_be_instance_of Subledger::Domain::ActiveBook
|
41
|
+
patched.description.must_equal description
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# GET /books ? before = :description & limit = :limit & state = :state
|
46
|
+
# ending
|
47
|
+
# starting
|
48
|
+
# after
|
49
|
+
# preceding = :book_id & limit = :limit & state = :state
|
50
|
+
# following
|
51
|
+
[ :archived, :active ].each do |state|
|
52
|
+
Subledger.scenarios.each do |scenario|
|
53
|
+
action = scenario.keys.first
|
54
|
+
expectations = scenario[action]
|
55
|
+
|
56
|
+
value_key = if action == :preceding or action == :following
|
57
|
+
'id'
|
58
|
+
else
|
59
|
+
'description'
|
60
|
+
end
|
61
|
+
|
62
|
+
RespondsTo "books.collect :state => #{state}, :action => #{action.inspect}" do
|
63
|
+
let(:books) { factory.create_book_collection state }
|
64
|
+
let(:book ) { books[2] }
|
65
|
+
let(:limit) { 2 }
|
66
|
+
|
67
|
+
if action == :preceding or action == :following
|
68
|
+
let(:value) { book.id }
|
69
|
+
else
|
70
|
+
let(:value) { URI.escape book.description }
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:args) { { :action => action,
|
74
|
+
value_key.to_sym => value,
|
75
|
+
:state => state,
|
76
|
+
:limit => limit } }
|
77
|
+
|
78
|
+
When 'not given a block' do
|
79
|
+
let(:collection) { subject.books.collect args }
|
80
|
+
|
81
|
+
ByReturning "a list of #{state} books" do
|
82
|
+
collection.length.must_equal limit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
When 'given a block' do
|
87
|
+
ByYielding "a list of #{state} books" do
|
88
|
+
count = 0
|
89
|
+
|
90
|
+
subject.books.collect( args ).each do |item|
|
91
|
+
expectation = expectations[count]
|
92
|
+
|
93
|
+
item.description.must_equal expectation
|
94
|
+
|
95
|
+
item.must_be_instance_of book.class
|
96
|
+
|
97
|
+
count += 1
|
98
|
+
end
|
99
|
+
|
100
|
+
count.must_equal limit
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# POST /books/:id/archive
|
108
|
+
[ :archived, :active ].each do |state|
|
109
|
+
RespondsTo "#{state}_book_instance.archive" do
|
110
|
+
let(:book) { factory.create_book state }
|
111
|
+
|
112
|
+
ByReturning 'an instance of Subledger::Domain::ArchivedBook' do
|
113
|
+
book.archive.must_be_instance_of Subledger::Domain::ArchivedBook
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# POST /books/:id/activate
|
119
|
+
[ :archived, :active ].each do |state|
|
120
|
+
RespondsTo "#{state}_book_instance.activate" do
|
121
|
+
let(:book) { factory.create_book state }
|
122
|
+
|
123
|
+
ByReturning 'an instance of Subledger::Domain::ActiveBook' do
|
124
|
+
book.activate.must_be_instance_of Subledger::Domain::ActiveBook
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
Test 'Subledger' do
|
4
|
+
|
5
|
+
let(:factory) { Subledger::Factory.create }
|
6
|
+
let(:amount ) { '1' }
|
7
|
+
|
8
|
+
Instance do
|
9
|
+
subject { factory.client }
|
10
|
+
|
11
|
+
Test 'Category' do
|
12
|
+
|
13
|
+
# POST /books/:book_id/categories
|
14
|
+
RespondsTo 'category.create' do
|
15
|
+
ByReturning 'an instance of Subledger::Domain::ActiveCategory' do
|
16
|
+
subject.category.create( factory.category_args ).
|
17
|
+
must_be_instance_of Subledger::Domain::ActiveCategory
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# GET /books/:book_id/categories/:id
|
22
|
+
[ :archived, :active ].each do |state|
|
23
|
+
RespondsTo 'category.read' do
|
24
|
+
let(:category) { factory.create_category state }
|
25
|
+
|
26
|
+
ByReturning "an instance of #{state} category" do
|
27
|
+
subject.category.read( :id => category.id ).
|
28
|
+
must_be_instance_of category.class
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# PATCH /books/:book_id/categories/:id
|
34
|
+
[ :archived, :active ].each do |state|
|
35
|
+
RespondsTo 'categories.update' do
|
36
|
+
let(:category ) { factory.create_category state }
|
37
|
+
let(:args ) { category.attributes }
|
38
|
+
let(:description) { 'updated description' }
|
39
|
+
let(:update_args) { args.merge :description => description,
|
40
|
+
:version => 2 }
|
41
|
+
|
42
|
+
ByReturning "an instance of #{state} category" do
|
43
|
+
patched = subject.categories.update update_args
|
44
|
+
|
45
|
+
patched.must_be_instance_of category.class
|
46
|
+
patched.description.must_equal description
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# GET /books/:book_id/categories ? before = :description & limit = :limit & state = :state
|
52
|
+
# ending
|
53
|
+
# starting
|
54
|
+
# after
|
55
|
+
[ :active, :archived ].each do |state|
|
56
|
+
Subledger.scenarios_order.each do |scenario|
|
57
|
+
action = scenario.keys.first
|
58
|
+
expectations = scenario[action]
|
59
|
+
|
60
|
+
RespondsTo "categories.collect :action => #{action}, :state => #{state}" do
|
61
|
+
let(:categories) { factory.create_category_collection state }
|
62
|
+
let(:item ) { categories[2] }
|
63
|
+
let(:limit ) { 2 }
|
64
|
+
let(:collection) { subject.categories.collect args }
|
65
|
+
let(:klass ) { item.class }
|
66
|
+
|
67
|
+
let(:args) { { :action => action,
|
68
|
+
:description => item.description,
|
69
|
+
:state => state,
|
70
|
+
:limit => limit } }
|
71
|
+
|
72
|
+
When 'not given a block' do
|
73
|
+
ByReturning "a list of #{state} categories" do
|
74
|
+
collection.length.must_equal limit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
When 'given a block' do
|
79
|
+
ByYielding "a list of #{state} categories" do
|
80
|
+
count = 0
|
81
|
+
|
82
|
+
collection.each do |category|
|
83
|
+
expectation = expectations[count]
|
84
|
+
|
85
|
+
category.description.must_equal expectation
|
86
|
+
|
87
|
+
category.must_be_instance_of klass
|
88
|
+
|
89
|
+
count += 1
|
90
|
+
end
|
91
|
+
|
92
|
+
count.must_equal limit
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# GET /categories ? preceding = :book_id & limit = :limit & state = :state
|
100
|
+
# following = :book_id & limit = :limit & state = :state
|
101
|
+
[ :active, :archived ].each do |state|
|
102
|
+
Subledger.scenarios_id.each do |scenario|
|
103
|
+
action = scenario.keys.first
|
104
|
+
expectations = scenario[action]
|
105
|
+
|
106
|
+
RespondsTo "categories.collect :action => #{action}" do
|
107
|
+
let(:categories) { factory.create_category_collection state }
|
108
|
+
let(:item ) { categories[2] }
|
109
|
+
let(:limit ) { 2 }
|
110
|
+
let(:collection) { subject.categories.collect args }
|
111
|
+
let(:klass ) { item.class }
|
112
|
+
|
113
|
+
let(:args) { { :action => action,
|
114
|
+
:id => item.id,
|
115
|
+
:state => state,
|
116
|
+
:limit => limit } }
|
117
|
+
|
118
|
+
When 'not given a block' do
|
119
|
+
ByReturning "a list of #{state} categories" do
|
120
|
+
collection.length.must_equal limit
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
When 'given a block' do
|
125
|
+
ByYielding "a list of #{state} categories" do
|
126
|
+
count = 0
|
127
|
+
|
128
|
+
collection.each do |category|
|
129
|
+
expectation = expectations[count]
|
130
|
+
|
131
|
+
category.description.must_equal expectation
|
132
|
+
|
133
|
+
category.must_be_instance_of klass
|
134
|
+
|
135
|
+
count += 1
|
136
|
+
end
|
137
|
+
|
138
|
+
count.must_equal limit
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# GET /books/:book_id/categories/:id/accounts
|
146
|
+
if Subledger::STORE == :api
|
147
|
+
RespondsTo 'category_instance.accounts' do
|
148
|
+
ByRaising 'a Store::ReportError' do
|
149
|
+
lambda do
|
150
|
+
factory.create_category(:active).accounts
|
151
|
+
end.must_raise Subledger::Store::CategoryError
|
152
|
+
end
|
153
|
+
end
|
154
|
+
else
|
155
|
+
RespondsTo "category_instance.accounts" do
|
156
|
+
let(:category) { factory.create_category :active }
|
157
|
+
|
158
|
+
let(:category_accounts) { }
|
159
|
+
|
160
|
+
When 'not given a block' do
|
161
|
+
ByReturning 'a list of accounts' #do
|
162
|
+
# category.accounts.length.must_equal category_accounts.length
|
163
|
+
# end
|
164
|
+
end
|
165
|
+
|
166
|
+
When 'given a block' do
|
167
|
+
ByYielding 'a list of accounts' #do
|
168
|
+
# count = 0
|
169
|
+
#
|
170
|
+
# category.accounts do |account|
|
171
|
+
# expectation = category_accounts[count]
|
172
|
+
#
|
173
|
+
# account.description.must_equal expectation.description
|
174
|
+
#
|
175
|
+
# account.must_be_instance_of Subledger::Domain::ActiveAccount
|
176
|
+
#
|
177
|
+
# count += 1
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# count.must_equal category_accounts.length
|
181
|
+
# end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# POST /books/:book_id/categories/:id/archive
|
187
|
+
RespondsTo 'category_instance.archive' do
|
188
|
+
ByReturning 'an instance of Subledger::Domain::ArchivedCategory' do
|
189
|
+
factory.create_category(:active).archive.
|
190
|
+
must_be_instance_of Subledger::Domain::ArchivedCategory
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# POST /books/:book_id/archived_categories/:id/activate
|
195
|
+
RespondsTo 'archived_category_instance.activate' do
|
196
|
+
ByReturning 'an instance of Subledger::Domain::ActiveCategory' do
|
197
|
+
factory.create_category(:archived).activate.
|
198
|
+
must_be_instance_of Subledger::Domain::ActiveCategory
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# Test 'Subledger' do
|
4
|
+
#
|
5
|
+
# let(:factory) { Subledger::Factory.create }
|
6
|
+
# let(:identity) { factory.identity }
|
7
|
+
#
|
8
|
+
# Instance do
|
9
|
+
# subject { factory.client }
|
10
|
+
#
|
11
|
+
# Test 'Control' do
|
12
|
+
# # POST /v2/orgs/:org_id/controls
|
13
|
+
# RespondsTo 'controls.create' do
|
14
|
+
# ByReturning 'an instance of Subledger::Domain::ActiveControl' do
|
15
|
+
# subject.controls.create( factory.control_args ).
|
16
|
+
# must_be_instance_of Subledger::Domain::ActiveControl
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # POST /v2/orgs/:org_id/controls/:control_id/archive
|
21
|
+
# [ :archived, :active ].each do |state|
|
22
|
+
# RespondsTo "#{state}_control_instance.archive" do
|
23
|
+
# let(:control) { factory.create_control state, identity }
|
24
|
+
#
|
25
|
+
# ByReturning 'an instance of Subledger::Domain::ArchivedControl' do
|
26
|
+
# control.archive.must_be_instance_of Subledger::Domain::ArchivedControl
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # POST /v2/orgs/:org_id/controls/:control_id/activate
|
32
|
+
# [ :archived, :active ].each do |state|
|
33
|
+
# RespondsTo "#{state}_control_instance.activate" do
|
34
|
+
# let(:control) { factory.create_control state, identity }
|
35
|
+
#
|
36
|
+
# ByReturning 'an instance of Subledger::Domain::ActiveControl' do
|
37
|
+
# control.activate.must_be_instance_of Subledger::Domain::ActiveControl
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
# end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
Test 'Subledger' do
|
4
|
+
|
5
|
+
let(:factory) { Subledger::Factory.create }
|
6
|
+
|
7
|
+
Instance do
|
8
|
+
subject { factory.client }
|
9
|
+
|
10
|
+
Test 'Identities' do
|
11
|
+
# POST /identities
|
12
|
+
RespondsTo 'identities.create' do
|
13
|
+
ByReturning 'an instance of ActiveIdentity and ActiveKey' do
|
14
|
+
identity, key = subject.identities.create factory.identity_args
|
15
|
+
identity.must_be_instance_of Subledger::Domain::ActiveIdentity
|
16
|
+
key.must_be_instance_of Subledger::Domain::ActiveKey
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# GET /identities/:id
|
21
|
+
RespondsTo 'identities.read' do
|
22
|
+
let(:identity) { factory.identity }
|
23
|
+
|
24
|
+
ByReturning 'a kind of Subledger::Domain::ActiveIdentity' do
|
25
|
+
subject.identities.read( :id => identity.id ).
|
26
|
+
must_be_instance_of Subledger::Domain::ActiveIdentity
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# PATCH /identities/:id
|
31
|
+
RespondsTo 'identities.update' do
|
32
|
+
let(:identity ) { factory.identity }
|
33
|
+
let(:description) { 'updated description' }
|
34
|
+
let(:args ) { identity.attributes }
|
35
|
+
let(:update_args) { args.merge :description => description,
|
36
|
+
:version => 2 }
|
37
|
+
|
38
|
+
ByReturning 'an updated kind of Subledger::Domain::ActiveIdentity' do
|
39
|
+
patched = subject.identities.update update_args
|
40
|
+
|
41
|
+
patched.must_be_instance_of Subledger::Domain::ActiveIdentity
|
42
|
+
patched.description.must_equal description
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|