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,110 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class OrgError < Error; end
|
4
|
+
|
5
|
+
class Org
|
6
|
+
|
7
|
+
include Domain
|
8
|
+
|
9
|
+
include Roles::Attributable
|
10
|
+
include Roles::Describable
|
11
|
+
include Roles::Identifiable
|
12
|
+
include Roles::Storable
|
13
|
+
include Roles::Versionable
|
14
|
+
|
15
|
+
include Roles::Creatable
|
16
|
+
include Roles::Readable
|
17
|
+
include Roles::Updatable
|
18
|
+
|
19
|
+
include Roles::Activatable
|
20
|
+
include Roles::Archivable
|
21
|
+
|
22
|
+
include Roles::Restable
|
23
|
+
|
24
|
+
attr_accessor :bucket_name
|
25
|
+
|
26
|
+
def self.root_klass
|
27
|
+
Org
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.post_keys
|
31
|
+
[ :description, :reference, :bucket_name ]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.patch_keys
|
35
|
+
[ :id, :description, :reference, :bucket_name, :version ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.sub_klasses
|
39
|
+
[ active_klass, archived_klass ]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.active_klass
|
43
|
+
ActiveOrg
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.archived_klass
|
47
|
+
ArchivedOrg
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.create args
|
51
|
+
org = super
|
52
|
+
|
53
|
+
args[:store].add_initial_controls_for org
|
54
|
+
|
55
|
+
org
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize args
|
59
|
+
describable args
|
60
|
+
identifiable args
|
61
|
+
storable args
|
62
|
+
versionable args
|
63
|
+
|
64
|
+
@bucket_name = args[:bucket_name]
|
65
|
+
end
|
66
|
+
|
67
|
+
class Entity < Grape::Entity
|
68
|
+
root 'orgs', 'org'
|
69
|
+
|
70
|
+
expose :id, :documentation => { :type => 'string', :desc => 'org ID' }
|
71
|
+
|
72
|
+
expose :description, :documentation => { :type => 'string', :desc => 'description' }
|
73
|
+
|
74
|
+
expose :reference, :documentation => { :type => 'string', :desc => 'reference URI' }
|
75
|
+
|
76
|
+
expose :bucket_name, :documentation => { :type => 'string', :desc => 'backup URI' }
|
77
|
+
|
78
|
+
expose :version, :documentation => { :type => 'integer', :desc => 'version' }
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def self.raise_unless_creatable args
|
84
|
+
store = args[:store]
|
85
|
+
|
86
|
+
store.raise_unless_bucket_name_valid args
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class ArchivedOrg < Org
|
91
|
+
class Entity < Org::Entity
|
92
|
+
root 'archived_orgs', 'archived_org'
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.sub_klasses
|
96
|
+
[ archived_klass ]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class ActiveOrg < Org
|
101
|
+
class Entity < Org::Entity
|
102
|
+
root 'active_orgs', 'active_org'
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.sub_klasses
|
106
|
+
[ active_klass ]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class ReportError < Error; end
|
4
|
+
|
5
|
+
class Report
|
6
|
+
|
7
|
+
include Domain
|
8
|
+
|
9
|
+
include Roles::Attributable
|
10
|
+
include Roles::Describable
|
11
|
+
include Roles::Identifiable
|
12
|
+
include Roles::Storable
|
13
|
+
include Roles::Versionable
|
14
|
+
|
15
|
+
include Roles::Creatable
|
16
|
+
include Roles::Readable
|
17
|
+
include Roles::Updatable
|
18
|
+
|
19
|
+
include Roles::Collectable
|
20
|
+
|
21
|
+
include Roles::Activatable
|
22
|
+
include Roles::Archivable
|
23
|
+
|
24
|
+
include Roles::Restable
|
25
|
+
|
26
|
+
attr_reader :book
|
27
|
+
|
28
|
+
def self.post_keys
|
29
|
+
[ :description, :reference ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.patch_keys
|
33
|
+
[ :id, :description, :reference, :version ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.root_klass
|
37
|
+
Report
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.sub_klasses
|
41
|
+
[ active_klass, archived_klass ]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.active_klass
|
45
|
+
ActiveReport
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.archived_klass
|
49
|
+
ArchivedReport
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize args
|
53
|
+
describable args
|
54
|
+
identifiable args
|
55
|
+
storable args
|
56
|
+
versionable args
|
57
|
+
|
58
|
+
@book = args[:book]
|
59
|
+
end
|
60
|
+
|
61
|
+
class Entity < Grape::Entity
|
62
|
+
root 'reports', 'report'
|
63
|
+
|
64
|
+
expose :id, :documentation => { :type => 'string', :desc => 'report ID' }
|
65
|
+
|
66
|
+
expose :book, :format_with => :id,
|
67
|
+
:documentation => { :type => 'string', :desc => 'book ID' }
|
68
|
+
|
69
|
+
expose :description, :documentation => { :type => 'string', :desc => 'description' }
|
70
|
+
|
71
|
+
expose :reference, :documentation => { :type => 'string', :desc => 'reference URI' }
|
72
|
+
|
73
|
+
expose :version, :documentation => { :type => 'integer', :desc => 'version' }
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def self.raise_unless_creatable args
|
79
|
+
|
80
|
+
book = args[:book]
|
81
|
+
|
82
|
+
if book.nil? or not book.kind_of? Book
|
83
|
+
raise BookError, ':book is required and must be a Book'
|
84
|
+
elsif UUID.invalid? book.id
|
85
|
+
raise BookError, ':book must have a valid :id'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def structure
|
90
|
+
store.report_structure_for self
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ArchivedReport < Report
|
95
|
+
class Entity < Report::Entity
|
96
|
+
root 'archived_reports', 'archived_report'
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.sub_klasses
|
100
|
+
[ archived_klass ]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class ActiveReport < Report
|
105
|
+
class Entity < Report::Entity
|
106
|
+
root 'active_reports', 'active_report'
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.sub_klasses
|
110
|
+
[ active_klass ]
|
111
|
+
end
|
112
|
+
|
113
|
+
def categories &block
|
114
|
+
begin
|
115
|
+
store.collect_categories_for_report self, &block
|
116
|
+
rescue Store::CollectError => e
|
117
|
+
raise ReportError, e
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def attach args
|
122
|
+
validate_attach args
|
123
|
+
|
124
|
+
category = args[:category]
|
125
|
+
parent = args[:parent]
|
126
|
+
|
127
|
+
if category.nil? or category.id.nil?
|
128
|
+
raise ReportError, ':category is required and must have an id'
|
129
|
+
end
|
130
|
+
|
131
|
+
if not parent.nil? and parent.id.nil?
|
132
|
+
raise ReportError, ':parent must have an id'
|
133
|
+
end
|
134
|
+
|
135
|
+
begin
|
136
|
+
store.attach_category_to_report :report => self,
|
137
|
+
:category => category,
|
138
|
+
:parent => parent
|
139
|
+
rescue Store::AttachError => e
|
140
|
+
raise ReportError, e
|
141
|
+
end
|
142
|
+
|
143
|
+
category
|
144
|
+
end
|
145
|
+
|
146
|
+
def detach args
|
147
|
+
category = args[:category]
|
148
|
+
|
149
|
+
if category.nil? or category.id.nil?
|
150
|
+
raise ReportError, ':category is required and must have an id'
|
151
|
+
end
|
152
|
+
|
153
|
+
begin
|
154
|
+
store.detach_category_from_report :report => self,
|
155
|
+
:category => category
|
156
|
+
rescue Store::DetachError => e
|
157
|
+
raise ReportError, e
|
158
|
+
end
|
159
|
+
|
160
|
+
category
|
161
|
+
end
|
162
|
+
|
163
|
+
def render args
|
164
|
+
at = args[:at]
|
165
|
+
|
166
|
+
if at.nil? or not at.kind_of? Time
|
167
|
+
raise ReportError, ':at is required and must be a Time'
|
168
|
+
end
|
169
|
+
|
170
|
+
building_report_rendering = client.building_report_rendering :effective_at => at,
|
171
|
+
:report => self
|
172
|
+
|
173
|
+
store.render args.merge! :building_report_rendering => building_report_rendering
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def add_categories_to report_hash, sub_structure, effective_at
|
179
|
+
categories_hash = report_hash[:categories] = { }
|
180
|
+
|
181
|
+
categories_balance = Balance.new
|
182
|
+
|
183
|
+
sub_structure.each do |category, category_hash|
|
184
|
+
categories_hash[category.id] = category.serializable_hash
|
185
|
+
|
186
|
+
category_balance = add_categories_to categories_hash[category.id], category_hash, effective_at
|
187
|
+
|
188
|
+
category_balance += add_accounts_to categories_hash[category.id], category, effective_at
|
189
|
+
|
190
|
+
categories_hash[category.id][:balance] = category_balance.serializable_hash
|
191
|
+
|
192
|
+
categories_balance += category_balance
|
193
|
+
end
|
194
|
+
|
195
|
+
categories_balance
|
196
|
+
end
|
197
|
+
|
198
|
+
def add_accounts_to report_category_hash, category, effective_at
|
199
|
+
accounts_hash = report_category_hash[:accounts] = { }
|
200
|
+
|
201
|
+
accounts_balance = Balance.new
|
202
|
+
|
203
|
+
category.accounts.each do |account|
|
204
|
+
accounts_hash[account.id] = account.serializable_hash
|
205
|
+
|
206
|
+
account_balance = add_balance_to accounts_hash[account.id], account, effective_at
|
207
|
+
|
208
|
+
accounts_balance += account_balance
|
209
|
+
end
|
210
|
+
|
211
|
+
accounts_balance
|
212
|
+
end
|
213
|
+
|
214
|
+
def add_balance_to report_category_account_hash, account, effective_at
|
215
|
+
balance = account.balance :at => effective_at
|
216
|
+
|
217
|
+
report_category_account_hash[:balance] = balance.serializable_hash
|
218
|
+
|
219
|
+
balance
|
220
|
+
end
|
221
|
+
|
222
|
+
def validate_attach args
|
223
|
+
if UUID.invalid? id
|
224
|
+
raise ReportError, ':report must have a valid :id'
|
225
|
+
end
|
226
|
+
|
227
|
+
category = args[:category]
|
228
|
+
|
229
|
+
if category.nil? or not category.kind_of? Category
|
230
|
+
raise ReportError, ':category is required and must be a Category'
|
231
|
+
elsif UUID.invalid? category.id
|
232
|
+
raise ReportError, ':category must have a valid :id'
|
233
|
+
end
|
234
|
+
|
235
|
+
parent = args[:parent]
|
236
|
+
|
237
|
+
unless parent.nil?
|
238
|
+
if not parent.kind_of? Category
|
239
|
+
raise ReportError, ':parent must be a Category'
|
240
|
+
elsif UUID.invalid? parent.id
|
241
|
+
raise ReportError, ':parent must have a valid :id'
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
class ReportRenderingError < Error; end
|
4
|
+
|
5
|
+
class ReportRendering
|
6
|
+
|
7
|
+
include Domain
|
8
|
+
|
9
|
+
include Roles::Attributable
|
10
|
+
include Roles::DescribableReportRendering
|
11
|
+
include Roles::Identifiable
|
12
|
+
include Roles::Storable
|
13
|
+
include Roles::Timeable
|
14
|
+
|
15
|
+
include Roles::Creatable
|
16
|
+
include Roles::Readable
|
17
|
+
|
18
|
+
include Roles::Collectable
|
19
|
+
|
20
|
+
include Roles::Restable
|
21
|
+
|
22
|
+
attr_reader :book, :report, :effective_at, :rendered_at,
|
23
|
+
:balanced_accounts, :total_accounts
|
24
|
+
|
25
|
+
def self.post_keys
|
26
|
+
[ :description, :reference, :report, :effective_at ]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.root_klass
|
30
|
+
ReportRendering
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.sub_klasses
|
34
|
+
[ building_klass, completed_klass ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.building_klass
|
38
|
+
BuildingReportRendering
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO create uses active_klass, should use creation_klass
|
42
|
+
def self.active_klass
|
43
|
+
building_klass
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.completed_klass
|
47
|
+
CompletedReportRendering
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize args
|
51
|
+
describable_report_rendering args
|
52
|
+
identifiable args
|
53
|
+
storable args
|
54
|
+
|
55
|
+
@book = args[:book]
|
56
|
+
@report = args[:report]
|
57
|
+
@effective_at = round args[:effective_at]
|
58
|
+
@rendered_at = utc_or_now args[:rendered_at]
|
59
|
+
|
60
|
+
@balanced_accounts = args[:balanced_accounts] || 0
|
61
|
+
@total_accounts = args[:total_accounts] || 0
|
62
|
+
|
63
|
+
specialized_initialization args
|
64
|
+
end
|
65
|
+
|
66
|
+
def progress
|
67
|
+
if total_accounts.zero?
|
68
|
+
0
|
69
|
+
else
|
70
|
+
( ( balanced_accounts.to_f / total_accounts ) * 99 ).to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Entity < Grape::Entity
|
75
|
+
root 'report_renderings', 'report_rendering'
|
76
|
+
|
77
|
+
expose :id
|
78
|
+
expose :book, :format_with => :id
|
79
|
+
expose :report, :format_with => :id
|
80
|
+
expose :description
|
81
|
+
expose :reference
|
82
|
+
expose :effective_at, :format_with => :time_full_second
|
83
|
+
expose :total_accounts, :format_with => :integer
|
84
|
+
expose :progress, :format_with => :progress
|
85
|
+
expose :rendered_at, :format_with => :time
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def self.raise_unless_creatable args
|
91
|
+
|
92
|
+
book = args[:book]
|
93
|
+
|
94
|
+
if book.nil? or not book.kind_of? Book
|
95
|
+
raise ReportRenderingError, ':book is required and must be a Book'
|
96
|
+
elsif UUID.invalid? book.id
|
97
|
+
raise ReportRenderingError, ':book must have a valid :id'
|
98
|
+
end
|
99
|
+
|
100
|
+
report = args[:report]
|
101
|
+
|
102
|
+
if report.nil? or not report.kind_of? Report
|
103
|
+
raise ReportRenderingError, ':report is required and must be a Report'
|
104
|
+
elsif UUID.invalid? report.id
|
105
|
+
raise ReportRenderingError, ':report must have a valid :id'
|
106
|
+
end
|
107
|
+
|
108
|
+
effective_at = args[:effective_at]
|
109
|
+
|
110
|
+
if effective_at.nil? or not effective_at.kind_of? Time
|
111
|
+
raise ReportRenderingError, ':effective_at is required and must be a Time'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def round effective_at
|
116
|
+
Time.at( utc_or_nil( effective_at ).to_i ).utc
|
117
|
+
end
|
118
|
+
|
119
|
+
TOTAL_ACCOUNTS_SEMAPHORE = Mutex.new
|
120
|
+
|
121
|
+
def increase_total_accounts_by count
|
122
|
+
TOTAL_ACCOUNTS_SEMAPHORE.synchronize do
|
123
|
+
@total_accounts += count
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
BALANCED_ACCOUNTS_SEMAPHORE = Mutex.new
|
128
|
+
|
129
|
+
def increase_balanced_accounts_by count
|
130
|
+
BALANCED_ACCOUNTS_SEMAPHORE.synchronize do
|
131
|
+
@balanced_accounts += count
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def specialized_initialization args
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class BuildingReportRendering < ReportRendering
|
140
|
+
class Entity < ReportRendering::Entity
|
141
|
+
root 'building_report_renderings', 'building_report_rendering'
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.sub_klasses
|
145
|
+
[ building_klass ]
|
146
|
+
end
|
147
|
+
|
148
|
+
# TODO this should probably move to Domain module
|
149
|
+
|
150
|
+
def to_json
|
151
|
+
MultiJson.dump serializable_hash
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def complete args
|
157
|
+
store.send :become, :becomable => self,
|
158
|
+
:klass => self.class.completed_klass,
|
159
|
+
:extra_args => args
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class CompletedReportRendering < ReportRendering
|
164
|
+
attr_reader :completed_at, :categories, :balance, :warnings
|
165
|
+
|
166
|
+
def self.sub_klasses
|
167
|
+
[ completed_klass ]
|
168
|
+
end
|
169
|
+
|
170
|
+
def progress
|
171
|
+
100
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_json
|
175
|
+
@json ||= if location[0..4] == 's3://'
|
176
|
+
ExceptionHandler.new( :name => 'completed_report_rendering to_json' ).with_retry do
|
177
|
+
s3_bucket.objects[id].read
|
178
|
+
end
|
179
|
+
elsif location[0..6] == 'file://'
|
180
|
+
File.open( location[7..-1], 'r' ).read
|
181
|
+
else
|
182
|
+
raise ReportRenderingError, "unknown URL scheme: #{location}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def categories
|
187
|
+
@categories ||= as_hash['categories']
|
188
|
+
end
|
189
|
+
|
190
|
+
def balance
|
191
|
+
balance_hash = { 'balance' => as_hash['balance'] }
|
192
|
+
|
193
|
+
@balance ||= Rest.to_balance balance_hash, client
|
194
|
+
end
|
195
|
+
|
196
|
+
def warnings
|
197
|
+
@warnings ||= as_hash['warnings']
|
198
|
+
end
|
199
|
+
|
200
|
+
class Entity < ReportRendering::Entity
|
201
|
+
root 'completed_report_renderings', 'completed_report_rendering'
|
202
|
+
|
203
|
+
expose :completed_at, :format_with => :time
|
204
|
+
expose :categories
|
205
|
+
expose :balance, :format_with => :balance
|
206
|
+
expose :warnings
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
attr_reader :location
|
212
|
+
|
213
|
+
def s3_bucket
|
214
|
+
@s3_bucket ||= Store::Aws.s3_client.buckets[RENDERED_REPORTS_BUCKET]
|
215
|
+
end
|
216
|
+
|
217
|
+
def as_hash
|
218
|
+
@hash ||= MultiJson.load to_json
|
219
|
+
end
|
220
|
+
|
221
|
+
def specialized_initialization args
|
222
|
+
@completed_at = utc_or_nil args[:completed_at]
|
223
|
+
@location = args[:location]
|
224
|
+
@json = args[:json]
|
225
|
+
|
226
|
+
@balance = nil
|
227
|
+
@categories = nil
|
228
|
+
@hash = nil
|
229
|
+
@warnings = nil
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Subledger
|
2
|
+
module Domain
|
3
|
+
module Roles
|
4
|
+
module Attributable
|
5
|
+
def attributes
|
6
|
+
instance_variables.inject( { } ) do | attrs, ivar |
|
7
|
+
attrs[ ivar.to_s[1..-1].to_sym ] = instance_variable_get ivar
|
8
|
+
attrs
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|