subledger 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- 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,106 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Path
|
3
|
+
def self.for_collection args
|
4
|
+
"/#{ API_VERSION }#{ path args }"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.for_entity args
|
8
|
+
"#{ for_collection args }/#{ args[ :anchor ].id }"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.for_entity_without_api_version args
|
12
|
+
"#{ path args }/#{ args[ :anchor ].id }"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.for_collect args
|
16
|
+
action = args[:action]
|
17
|
+
state = args[:state]
|
18
|
+
limit = args[:limit]
|
19
|
+
collection_name = args[:collection_name]
|
20
|
+
|
21
|
+
key = key_for args
|
22
|
+
value = URI.escape value_for( args ).to_s
|
23
|
+
|
24
|
+
path = for_collection( args ) + '?'
|
25
|
+
|
26
|
+
query_params = []
|
27
|
+
|
28
|
+
query_params << "state=#{ state }" unless collection_name == :account_lines
|
29
|
+
query_params << "action=#{ action }"
|
30
|
+
query_params << "#{ key }=#{ value }" unless value.respond_to?( :length ) and value.length.zero?
|
31
|
+
query_params << "limit=#{ limit }" unless limit.nil?
|
32
|
+
|
33
|
+
path + query_params.join('&')
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
COLLECTION_PATHS = {
|
39
|
+
:identities => lambda { |anchor| 'identities' },
|
40
|
+
:keys => lambda { |anchor| "identities/#{anchor.identity.id}/keys" },
|
41
|
+
:controls => lambda { |anchor| "identities/#{anchor.identity.id}/controls" },
|
42
|
+
:orgs => lambda { |anchor| 'orgs' },
|
43
|
+
:books => lambda { |anchor| "orgs/#{anchor.org.id}/books" },
|
44
|
+
:accounts => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/accounts" },
|
45
|
+
:journal_entries => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/journal_entries" },
|
46
|
+
:lines => lambda { |anchor| "orgs/#{anchor.journal_entry.book.org.id}/books/#{anchor.journal_entry.book.id}/journal_entries/#{anchor.journal_entry.id}/lines" },
|
47
|
+
:account_lines => lambda { |anchor| "orgs/#{anchor.account.book.org.id}/books/#{anchor.account.book.id}/accounts/#{anchor.account.id}/lines" },
|
48
|
+
:reports => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/reports" },
|
49
|
+
:categories => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/categories" },
|
50
|
+
:report_renderings => lambda { |anchor| "orgs/#{anchor.book.org.id}/books/#{anchor.book.id}/report_renderings" }
|
51
|
+
}
|
52
|
+
|
53
|
+
def self.path args
|
54
|
+
anchor = args[:anchor]
|
55
|
+
|
56
|
+
collection_name = CollectionName.without_state args
|
57
|
+
|
58
|
+
path = COLLECTION_PATHS[collection_name].call anchor
|
59
|
+
|
60
|
+
"/#{path}"
|
61
|
+
end
|
62
|
+
|
63
|
+
KEY_FOR = { :books => 'description',
|
64
|
+
:accounts => 'description',
|
65
|
+
:journal_entries => 'effective_at',
|
66
|
+
:lines => 'order',
|
67
|
+
:account_lines => 'effective_at',
|
68
|
+
:reports => 'description',
|
69
|
+
:categories => 'description',
|
70
|
+
:report_renderings => 'rendered_at'
|
71
|
+
}
|
72
|
+
|
73
|
+
def self.key_for args
|
74
|
+
action = args[:action]
|
75
|
+
|
76
|
+
if [ :preceding, :following ].include? action
|
77
|
+
'id'
|
78
|
+
else
|
79
|
+
KEY_FOR[ CollectionName.without_state args ]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
VALUE_FOR = { :books => lambda { |anchor| anchor.description },
|
84
|
+
:accounts => lambda { |anchor| anchor.description },
|
85
|
+
:journal_entries => lambda { |anchor| anchor.effective_at.iso8601(3) },
|
86
|
+
:lines => lambda { |anchor| anchor.order },
|
87
|
+
:account_lines => lambda { |anchor| anchor.effective_at.iso8601(3) },
|
88
|
+
:reports => lambda { |anchor| anchor.description },
|
89
|
+
:categories => lambda { |anchor| anchor.description },
|
90
|
+
:report_renderings => lambda { |anchor| anchor.rendered_at.iso8601(3) }
|
91
|
+
}
|
92
|
+
|
93
|
+
def self.value_for args
|
94
|
+
action = args[:action]
|
95
|
+
anchor = args[:anchor]
|
96
|
+
|
97
|
+
if [ :preceding, :following ].include? action
|
98
|
+
anchor.id
|
99
|
+
else
|
100
|
+
collection_name = CollectionName.without_state args
|
101
|
+
|
102
|
+
VALUE_FOR[collection_name].call anchor
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Rest
|
3
|
+
def self.to_args rest_args, client
|
4
|
+
args = { }
|
5
|
+
|
6
|
+
rest_args.each do |key, value|
|
7
|
+
key_symbol = key.to_sym
|
8
|
+
|
9
|
+
unless value.nil?
|
10
|
+
code = LAMBDAS[key]
|
11
|
+
|
12
|
+
args[key_symbol] = if code.nil? or value.kind_of? Domain
|
13
|
+
value
|
14
|
+
else
|
15
|
+
code.call client, args, key, value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
args
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.to_balance rest_args, client
|
24
|
+
to_args( rest_args, client )[:balance]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.present_entity entity
|
28
|
+
{ entity.entity_name => entity.serializable_hash }
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.present_collection entity_name, collection
|
32
|
+
entities = collection.collect { |entity| entity.serializable_hash }
|
33
|
+
|
34
|
+
{ entity_name => entities }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
ACCOUNT = lambda do |client, args, key, value|
|
40
|
+
client.account :id => value
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO eliminate if condition in BALANCE LAMBDA
|
44
|
+
|
45
|
+
BALANCE = lambda do |client, args, key, value|
|
46
|
+
if value.has_key? :debit_value
|
47
|
+
client.balance :debit_value => debit_value( client, value[:debit_value ]['amount'] ),
|
48
|
+
:credit_value => credit_value( client, value[:credit_value]['amount'] )
|
49
|
+
else
|
50
|
+
client.balance :debit_value => debit_value( client, value['debit_value' ]['amount'] ),
|
51
|
+
:credit_value => credit_value( client, value['credit_value']['amount'] )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
BOOK = lambda do |client, args, key, value|
|
56
|
+
client.book :id => value
|
57
|
+
end
|
58
|
+
|
59
|
+
CATEGORIES = lambda do |client, args, key, value|
|
60
|
+
args[:categories] = MultiJson.dump value
|
61
|
+
end
|
62
|
+
|
63
|
+
IDENTITY = lambda do |client, args, key, value|
|
64
|
+
client.identity :id => value
|
65
|
+
end
|
66
|
+
|
67
|
+
JOURNAL_ENTRY = lambda do |client, args, key, value|
|
68
|
+
client.journal_entry :id => value
|
69
|
+
end
|
70
|
+
|
71
|
+
MODE = lambda do |client, args, key, value|
|
72
|
+
value.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
NB = lambda do | client, args, key, value |
|
76
|
+
client.send value
|
77
|
+
end
|
78
|
+
|
79
|
+
ORG = lambda do |client, args, key, value|
|
80
|
+
client.org :id => value
|
81
|
+
end
|
82
|
+
|
83
|
+
TIME = lambda do |client, args, key, value|
|
84
|
+
Subledger.parse_time key, value
|
85
|
+
end
|
86
|
+
|
87
|
+
VALUE = lambda do |client, args, key, value|
|
88
|
+
client.send value['type'], value['amount']
|
89
|
+
end
|
90
|
+
|
91
|
+
VERSION = lambda do |client, args, key, value|
|
92
|
+
value.to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
REPORT = lambda do |client, args, key, value|
|
96
|
+
client.report :id => value
|
97
|
+
end
|
98
|
+
|
99
|
+
LAMBDAS = {
|
100
|
+
'account' => ACCOUNT,
|
101
|
+
'balance' => BALANCE,
|
102
|
+
'book' => BOOK,
|
103
|
+
'categories' => CATEGORIES,
|
104
|
+
'completed_at' => TIME,
|
105
|
+
'credit_value' => VALUE,
|
106
|
+
'debit_value' => VALUE,
|
107
|
+
'effective_at' => TIME,
|
108
|
+
'identity' => IDENTITY,
|
109
|
+
'journal_entry' => JOURNAL_ENTRY,
|
110
|
+
'mode' => MODE,
|
111
|
+
'normal_balance' => NB,
|
112
|
+
'org' => ORG,
|
113
|
+
'posted_at' => TIME,
|
114
|
+
'rendered_at' => TIME,
|
115
|
+
'report' => REPORT,
|
116
|
+
'value' => VALUE,
|
117
|
+
'version' => VERSION
|
118
|
+
}
|
119
|
+
|
120
|
+
def self.debit_value client, amount
|
121
|
+
BigDecimal( amount ) == 0 ? client.zero : client.debit( amount )
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.credit_value client, amount
|
125
|
+
BigDecimal( amount ) == 0 ? client.zero : client.credit( amount )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Errors
|
5
|
+
# Raised when the Subledger API returns a 400 status code
|
6
|
+
class BadRequest < Error; end
|
7
|
+
|
8
|
+
# Raised when the Subledger API returns a 401 status code
|
9
|
+
class Unauthorized < Error; end
|
10
|
+
|
11
|
+
# Raised when the Subledger API returns a 403 status code
|
12
|
+
class Forbidden < Error; end
|
13
|
+
|
14
|
+
# Raised when the Subledger API returns a 404 status code
|
15
|
+
class NotFound < Error; end
|
16
|
+
|
17
|
+
# Raised when the Subledger API returns a 406 status code
|
18
|
+
class NotAcceptable < Error; end
|
19
|
+
|
20
|
+
# Raised when the Subledger API returns a 409 status code
|
21
|
+
class Conflict < Error; end
|
22
|
+
|
23
|
+
# Raised when the Subledger API returns a 422 status code
|
24
|
+
class UnprocessableEntity < Error; end
|
25
|
+
|
26
|
+
# Raised when the Subledger API returns a 500 status code
|
27
|
+
class InternalServerError < Error; end
|
28
|
+
|
29
|
+
# Raised when the Subledger API returns a 501 status code
|
30
|
+
class NotImplemented < Error; end
|
31
|
+
|
32
|
+
# Raised when the Subledger API returns a 502 status code
|
33
|
+
class BadGateway < Error; end
|
34
|
+
|
35
|
+
# Raised when the Subledger API returns a 503 status code
|
36
|
+
class ServiceUnavailable < Error; end
|
37
|
+
|
38
|
+
# Raise when the Subledger API returns any other status code
|
39
|
+
class HttpError < Error; end
|
40
|
+
|
41
|
+
class ServerNotAvailable < Error
|
42
|
+
def initialize wrapped
|
43
|
+
@wrapped = wrapped
|
44
|
+
end
|
45
|
+
|
46
|
+
def message
|
47
|
+
"#{super} was not avaiable. Please check your configuration. (#{@wrapped.message})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ResponseError < Faraday::Response::Middleware
|
52
|
+
|
53
|
+
EXCEPTIONS = {
|
54
|
+
400 => BadRequest,
|
55
|
+
401 => Unauthorized,
|
56
|
+
403 => Forbidden,
|
57
|
+
404 => NotFound,
|
58
|
+
406 => NotAcceptable,
|
59
|
+
409 => Store::UpdateConflictError,
|
60
|
+
422 => UnprocessableEntity,
|
61
|
+
500 => InternalServerError,
|
62
|
+
501 => NotImplemented,
|
63
|
+
502 => BadGateway,
|
64
|
+
503 => ServiceUnavailable
|
65
|
+
}
|
66
|
+
|
67
|
+
def call env
|
68
|
+
@app.call(env).on_complete do
|
69
|
+
status = env[:status].to_i
|
70
|
+
|
71
|
+
if ( 400..600 ).include? status
|
72
|
+
raise exception_for( status ), response_for( env )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def exception_for status
|
78
|
+
EXCEPTIONS[status] || HttpError
|
79
|
+
end
|
80
|
+
|
81
|
+
def response_for env
|
82
|
+
{ :status => env[:status],
|
83
|
+
:url => url_for( env ),
|
84
|
+
:headers => env[:response_headers],
|
85
|
+
:body => env[:body] }
|
86
|
+
end
|
87
|
+
|
88
|
+
def url_for env
|
89
|
+
env[:url].to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Activate
|
5
|
+
def activate activatable
|
6
|
+
path = Path.for_entity( :anchor => activatable ) + '/activate'
|
7
|
+
|
8
|
+
begin
|
9
|
+
json_body = http.post do |req|
|
10
|
+
req.url path
|
11
|
+
end.body
|
12
|
+
rescue Exception => e
|
13
|
+
raise ActivateError, "Cannot activate #{activatable}: #{e}"
|
14
|
+
end
|
15
|
+
|
16
|
+
new_or_initialize json_body, activatable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Archive
|
5
|
+
def archive archivable
|
6
|
+
path = Path.for_entity( :anchor => archivable ) + '/archive'
|
7
|
+
|
8
|
+
begin
|
9
|
+
json_body = http.post do |req|
|
10
|
+
req.url path
|
11
|
+
end.body
|
12
|
+
rescue Exception => e
|
13
|
+
raise ArchiveError, "Cannot archive #{archivable}: #{e}"
|
14
|
+
end
|
15
|
+
|
16
|
+
new_or_initialize json_body, archivable
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Balance
|
5
|
+
def account_balance args
|
6
|
+
client = args[:client]
|
7
|
+
account = args[:account]
|
8
|
+
at = args[:at].iso8601(3)
|
9
|
+
|
10
|
+
path = Path.for_entity( :anchor => account ) + '/balance?at=' + at
|
11
|
+
|
12
|
+
begin
|
13
|
+
response_hash = parse_json(
|
14
|
+
http.get( path ).body )
|
15
|
+
rescue Exception => e
|
16
|
+
raise BalanceError, "Cannot balance #{account}: #{e}"
|
17
|
+
end
|
18
|
+
|
19
|
+
Rest.to_balance response_hash, client
|
20
|
+
end
|
21
|
+
|
22
|
+
def journal_entry_balance args
|
23
|
+
client = args[:client]
|
24
|
+
journal_entry = args[:journal_entry]
|
25
|
+
|
26
|
+
path = Path.for_entity( :anchor => journal_entry ) + '/balance'
|
27
|
+
|
28
|
+
begin
|
29
|
+
response_hash = parse_json( http.get( path ).body )
|
30
|
+
rescue Exception => e
|
31
|
+
raise BalanceError, "Cannot balance #{journal_entry}: #{e}"
|
32
|
+
end
|
33
|
+
|
34
|
+
Rest.to_balance response_hash, client
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Categories
|
5
|
+
def attach_account_to_category args
|
6
|
+
category = args[:category]
|
7
|
+
account = args[:account]
|
8
|
+
|
9
|
+
path = Path.for_entity( :anchor => category ) + '/attach'
|
10
|
+
|
11
|
+
attach_post_hash = { 'account' => account.id }
|
12
|
+
|
13
|
+
begin
|
14
|
+
json_body = http.post do |req|
|
15
|
+
req.url path
|
16
|
+
req.body = attach_post_hash
|
17
|
+
end.body
|
18
|
+
rescue Exception => e
|
19
|
+
raise AttachError, "Cannot attach #{account}: #{e}"
|
20
|
+
end
|
21
|
+
|
22
|
+
account
|
23
|
+
end
|
24
|
+
|
25
|
+
def detach_account_from_category args
|
26
|
+
category = args[:category]
|
27
|
+
account = args[:account]
|
28
|
+
|
29
|
+
path = Path.for_entity( :anchor => category ) + '/detach'
|
30
|
+
|
31
|
+
detach_post_hash = { 'account' => account.id }
|
32
|
+
|
33
|
+
begin
|
34
|
+
json_body = http.post do |req|
|
35
|
+
req.url path
|
36
|
+
req.body = detach_post_hash
|
37
|
+
end.body
|
38
|
+
rescue Exception => e
|
39
|
+
raise DetachError, "Cannot detach #{account}: #{e}"
|
40
|
+
end
|
41
|
+
|
42
|
+
account
|
43
|
+
end
|
44
|
+
|
45
|
+
def collect_accounts_for_category category
|
46
|
+
raise CategoryError, 'category#accounts is not yet implemented'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Collect
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def before args
|
9
|
+
collect_with args
|
10
|
+
end
|
11
|
+
|
12
|
+
def ending args
|
13
|
+
collect_with args
|
14
|
+
end
|
15
|
+
|
16
|
+
def starting args
|
17
|
+
collect_with args
|
18
|
+
end
|
19
|
+
|
20
|
+
def after args
|
21
|
+
collect_with args
|
22
|
+
end
|
23
|
+
|
24
|
+
def preceding args
|
25
|
+
collect_with args
|
26
|
+
end
|
27
|
+
|
28
|
+
def following args
|
29
|
+
collect_with args
|
30
|
+
end
|
31
|
+
|
32
|
+
def collect_with args
|
33
|
+
path = Path.for_collect args
|
34
|
+
|
35
|
+
anchor = args[:anchor]
|
36
|
+
|
37
|
+
klass = anchor.class
|
38
|
+
|
39
|
+
begin
|
40
|
+
collection = parse_json http.get( path ).body
|
41
|
+
rescue Exception => e
|
42
|
+
raise CollectError, "Cannot collect: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
client = anchor.client
|
46
|
+
|
47
|
+
key = collection.keys.first
|
48
|
+
|
49
|
+
collection[key].collect do |response_hash|
|
50
|
+
args = Rest.to_args response_hash, client
|
51
|
+
|
52
|
+
client.send key, args
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module Create
|
5
|
+
def create creatable
|
6
|
+
unless creatable.id.nil?
|
7
|
+
raise CreateError, 'cannot create an object with an :id'
|
8
|
+
end
|
9
|
+
|
10
|
+
path = Path.for_collection :anchor => creatable
|
11
|
+
|
12
|
+
begin
|
13
|
+
json_body = http.post do |req|
|
14
|
+
req.url path
|
15
|
+
req.body = creatable.post_hash
|
16
|
+
end.body
|
17
|
+
rescue Exception => e
|
18
|
+
raise CreateError, "Cannot create #{creatable}: #{e}"
|
19
|
+
end
|
20
|
+
|
21
|
+
new_or_initialize json_body, creatable
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateAndPost
|
5
|
+
def create_and_post args
|
6
|
+
dje = args[:active_journal_entry]
|
7
|
+
dje_lines = args[:active_lines]
|
8
|
+
pje = args[:posting_journal_entry]
|
9
|
+
|
10
|
+
path = Path.for_collection( :anchor => dje ) + '/create_and_post'
|
11
|
+
|
12
|
+
json_body = http.post do |req|
|
13
|
+
req.url path
|
14
|
+
|
15
|
+
unless dje.post_delay.zero?
|
16
|
+
req.headers['X-Subledger-Post-Slowly'] = 'true'
|
17
|
+
end
|
18
|
+
|
19
|
+
req.body = create_and_post_hash( dje, dje_lines )
|
20
|
+
end.body
|
21
|
+
|
22
|
+
new_or_initialize json_body, pje
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def create_and_post_hash dje, dje_lines
|
28
|
+
lines = dje_lines.collect { |line| line.post_hash }
|
29
|
+
|
30
|
+
dje.post_hash.merge :lines => lines
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateIdentity
|
5
|
+
ACTIVE_IDENTITY = 'active_identity'
|
6
|
+
ACTIVE_KEY = 'active_key'
|
7
|
+
|
8
|
+
def create_identity identity
|
9
|
+
client = identity.client
|
10
|
+
|
11
|
+
path = Path.for_collection :anchor => identity
|
12
|
+
|
13
|
+
begin
|
14
|
+
json_body = http.post do |req|
|
15
|
+
req.url path
|
16
|
+
req.body = identity.post_hash
|
17
|
+
end.body
|
18
|
+
rescue Exception => e
|
19
|
+
raise CreateError, "Cannot create #{identity.class}: #{e}"
|
20
|
+
end
|
21
|
+
|
22
|
+
response_hash = parse_json json_body
|
23
|
+
|
24
|
+
identity_hash = { 'active_identity' => response_hash[ACTIVE_IDENTITY] }
|
25
|
+
identity_json = MultiJson.dump identity_hash
|
26
|
+
|
27
|
+
new_identity = new_or_initialize identity_json, identity
|
28
|
+
|
29
|
+
identity.send :initialize, new_identity.attributes
|
30
|
+
|
31
|
+
key_args = Rest.to_args response_hash[ACTIVE_KEY], client
|
32
|
+
key = client.active_keys key_args
|
33
|
+
|
34
|
+
return identity, key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module CreateLine
|
5
|
+
def create_line args
|
6
|
+
begin
|
7
|
+
line = args[:line]
|
8
|
+
|
9
|
+
path = Path.for_entity( :anchor => line.journal_entry ) + '/create_line'
|
10
|
+
|
11
|
+
json_body = http.post do |req|
|
12
|
+
req.url path
|
13
|
+
req.body = line.post_hash
|
14
|
+
end.body
|
15
|
+
rescue Exception => e
|
16
|
+
raise CreateLineError, "Cannot create #{line}: #{e}"
|
17
|
+
end
|
18
|
+
|
19
|
+
new_or_initialize json_body, line
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Store
|
3
|
+
module Api
|
4
|
+
module FirstAndLastLine
|
5
|
+
|
6
|
+
def first_and_last_line args
|
7
|
+
anchor = args[:account]
|
8
|
+
client = anchor.client
|
9
|
+
|
10
|
+
path = Path.for_entity( :anchor => anchor ) + '/first_and_last_line'
|
11
|
+
|
12
|
+
klass = anchor.class
|
13
|
+
|
14
|
+
begin
|
15
|
+
collection = parse_json http.get( path ).body
|
16
|
+
rescue Exception => e
|
17
|
+
raise FirstAndLastLineError, "Cannot get first and last line: #{e}"
|
18
|
+
end
|
19
|
+
|
20
|
+
key = collection.keys.first
|
21
|
+
|
22
|
+
collection[key].collect do |response_hash|
|
23
|
+
args = Rest.to_args response_hash, client
|
24
|
+
|
25
|
+
client.send key, args
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|