subledger 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.gitignore +9 -0
  2. data/LICENSE.txt +12 -0
  3. data/README.md +136 -0
  4. data/Rakefile +5 -0
  5. data/bin/subledger +6 -0
  6. data/lib/subledger/actor.rb +32 -0
  7. data/lib/subledger/collection_name.rb +25 -0
  8. data/lib/subledger/domain/account.rb +168 -0
  9. data/lib/subledger/domain/balance.rb +102 -0
  10. data/lib/subledger/domain/book.rb +111 -0
  11. data/lib/subledger/domain/category.rb +157 -0
  12. data/lib/subledger/domain/control.rb +180 -0
  13. data/lib/subledger/domain/formatters.rb +31 -0
  14. data/lib/subledger/domain/identity.rb +159 -0
  15. data/lib/subledger/domain/journal_entry.rb +293 -0
  16. data/lib/subledger/domain/key.rb +113 -0
  17. data/lib/subledger/domain/line.rb +272 -0
  18. data/lib/subledger/domain/org.rb +110 -0
  19. data/lib/subledger/domain/report.rb +247 -0
  20. data/lib/subledger/domain/report_rendering.rb +233 -0
  21. data/lib/subledger/domain/roles/activatable.rb +11 -0
  22. data/lib/subledger/domain/roles/archivable.rb +11 -0
  23. data/lib/subledger/domain/roles/attributable.rb +14 -0
  24. data/lib/subledger/domain/roles/collectable.rb +175 -0
  25. data/lib/subledger/domain/roles/creatable.rb +58 -0
  26. data/lib/subledger/domain/roles/describable.rb +33 -0
  27. data/lib/subledger/domain/roles/describable_report_rendering.rb +50 -0
  28. data/lib/subledger/domain/roles/identifiable.rb +15 -0
  29. data/lib/subledger/domain/roles/postable.rb +54 -0
  30. data/lib/subledger/domain/roles/progressable.rb +11 -0
  31. data/lib/subledger/domain/roles/readable.rb +34 -0
  32. data/lib/subledger/domain/roles/restable.rb +69 -0
  33. data/lib/subledger/domain/roles/storable.rb +30 -0
  34. data/lib/subledger/domain/roles/timeable.rb +18 -0
  35. data/lib/subledger/domain/roles/updatable.rb +35 -0
  36. data/lib/subledger/domain/roles/versionable.rb +35 -0
  37. data/lib/subledger/domain/roles.rb +16 -0
  38. data/lib/subledger/domain/value/credit.rb +16 -0
  39. data/lib/subledger/domain/value/debit.rb +16 -0
  40. data/lib/subledger/domain/value/zero.rb +24 -0
  41. data/lib/subledger/domain/value.rb +111 -0
  42. data/lib/subledger/domain.rb +95 -0
  43. data/lib/subledger/exception_handler.rb +65 -0
  44. data/lib/subledger/interface/client.rb +295 -0
  45. data/lib/subledger/interface/dispatcher.rb +20 -0
  46. data/lib/subledger/interface.rb +2 -0
  47. data/lib/subledger/path.rb +106 -0
  48. data/lib/subledger/rest.rb +128 -0
  49. data/lib/subledger/server.rb +3 -0
  50. data/lib/subledger/store/api/errors.rb +95 -0
  51. data/lib/subledger/store/api/roles/activate.rb +21 -0
  52. data/lib/subledger/store/api/roles/archive.rb +21 -0
  53. data/lib/subledger/store/api/roles/balance.rb +39 -0
  54. data/lib/subledger/store/api/roles/categories.rb +51 -0
  55. data/lib/subledger/store/api/roles/collect.rb +58 -0
  56. data/lib/subledger/store/api/roles/create.rb +26 -0
  57. data/lib/subledger/store/api/roles/create_and_post.rb +35 -0
  58. data/lib/subledger/store/api/roles/create_identity.rb +39 -0
  59. data/lib/subledger/store/api/roles/create_line.rb +24 -0
  60. data/lib/subledger/store/api/roles/first_and_last_line.rb +31 -0
  61. data/lib/subledger/store/api/roles/post.rb +25 -0
  62. data/lib/subledger/store/api/roles/progress.rb +21 -0
  63. data/lib/subledger/store/api/roles/read.rb +19 -0
  64. data/lib/subledger/store/api/roles/reports.rb +77 -0
  65. data/lib/subledger/store/api/roles/update.rb +24 -0
  66. data/lib/subledger/store/api/store.rb +103 -0
  67. data/lib/subledger/store/api.rb +20 -0
  68. data/lib/subledger/store.rb +236 -0
  69. data/lib/subledger/supervisor.rb +21 -0
  70. data/lib/subledger/uuid.rb +52 -0
  71. data/lib/subledger/version.rb +4 -0
  72. data/lib/subledger.rb +234 -0
  73. data/spec/spec_helper.rb +77 -0
  74. data/spec/subledger_account_spec.rb +354 -0
  75. data/spec/subledger_book_spec.rb +130 -0
  76. data/spec/subledger_category_spec.rb +203 -0
  77. data/spec/subledger_control_spec.rb +43 -0
  78. data/spec/subledger_identity_spec.rb +47 -0
  79. data/spec/subledger_journal_entry_spec.rb +417 -0
  80. data/spec/subledger_key_spec.rb +43 -0
  81. data/spec/subledger_org_spec.rb +68 -0
  82. data/spec/subledger_report_spec.rb +295 -0
  83. data/spec/subledger_spec.rb +101 -0
  84. data/subledger.gemspec +52 -0
  85. metadata +205 -0
@@ -0,0 +1,24 @@
1
+ module Subledger
2
+ module Domain
3
+ class Zero
4
+
5
+ include Value
6
+
7
+ def self.type
8
+ 'zero'
9
+ end
10
+
11
+ def rest_hash
12
+ { 'type' => 'zero', 'amount' => amount_to_s(@amount) }
13
+ end
14
+
15
+ private
16
+
17
+ def validate_sign arg_amount
18
+ unless arg_amount.zero?
19
+ raise ValueError, 'Amount must be zero'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,111 @@
1
+ # additional requires at bottom
2
+
3
+ module Subledger
4
+ module Domain
5
+ class ValueError < Error; end
6
+
7
+ module Value
8
+
9
+ def self.included base
10
+ base.extend ValueClass
11
+ end
12
+
13
+ module ValueClass
14
+ def raise_unless_creatable args
15
+ value = args[:value]
16
+
17
+ if value.nil? or not value.kind_of? Value
18
+ raise ValueError, ':value is required and must be a Value'
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_reader :amount
24
+
25
+ def initialize arg_amount=0
26
+ if arg_amount.nil?
27
+ raise ValueError, 'amount may not be nil'
28
+ end
29
+
30
+ validate_amount(arg_amount) if arg_amount.respond_to?(:match)
31
+
32
+ big_amount = BigDecimal arg_amount
33
+
34
+ validate_sign big_amount
35
+
36
+ validate_amount( amount_to_s big_amount )
37
+
38
+ @amount = big_amount
39
+ end
40
+
41
+ def + other
42
+ (Balance.new + value + other.value).value
43
+ end
44
+
45
+ def == other
46
+ self.value.class == other.value.class and amount == other.amount
47
+ end
48
+
49
+ def value
50
+ self
51
+ end
52
+
53
+ def to_s
54
+ "#{self.class.type}: #{amount_to_s @amount}"
55
+ end
56
+
57
+ alias :inspect :to_s
58
+
59
+ private
60
+
61
+ def amount_to_s arg_amount
62
+ if arg_amount.zero?
63
+ '0'
64
+ else
65
+ arg_amount.to_s( 'F' ).chomp( '.0' ).sub( /^0+/, '' )
66
+ end
67
+ end
68
+
69
+ def validate_sign arg_amount
70
+ unless arg_amount > 0
71
+ raise ValueError, 'amount must be positive'
72
+ end
73
+ end
74
+
75
+ ERROR_TEXT = 'amount is limited to 15 whole digits and 12 fractional digits'
76
+
77
+ def validate_amount arg_amount
78
+
79
+ unless arg_amount.match /^\d*[.]?\d*$/
80
+ raise ValueError, "amount (#{arg_amount}) must be numeric"
81
+ end
82
+
83
+ return if BigDecimal(arg_amount).zero?
84
+
85
+ arg_amount.sub! /^0+/, ''
86
+ arg_amount.sub! /[.](\d)*0+$/, '.\\1'
87
+
88
+ if arg_amount.include? '.'
89
+ whole, fraction = arg_amount.split '.'
90
+
91
+ whole ||= ''
92
+ fraction ||= ''
93
+
94
+ if whole.length > 15 or fraction.length > 12
95
+ raise ValueError, ERROR_TEXT
96
+ end
97
+ else
98
+ if arg_amount.length > 15
99
+ raise ValueError, ERROR_TEXT
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+
109
+ require 'subledger/domain/value/credit'
110
+ require 'subledger/domain/value/debit'
111
+ require 'subledger/domain/value/zero'
@@ -0,0 +1,95 @@
1
+ require 'bcrypt'
2
+ require 'grape-entity'
3
+
4
+ # additional requires at bottom
5
+
6
+ module Subledger
7
+ module Domain
8
+ class ActivatableError < Error; end
9
+ class ArchivableError < Error; end
10
+ class AttributableError < Error; end
11
+ class CollectableError < Error; end
12
+ class CreatableError < Error; end
13
+ class DescribableError < Error; end
14
+ class DescribableReportRenderingError < Error; end
15
+ class IdentifiableError < Error; end
16
+ class PostableError < Error; end
17
+ class ProgressableError < Error; end
18
+ class ReadableError < Error; end
19
+ class RestableError < Error; end
20
+ class StorableError < Error; end
21
+ class TimeableError < Error; end
22
+ class UpdatableError < Error; end
23
+ class VersionableError < Error; end
24
+
25
+ def self.included base
26
+ base.extend DomainClass
27
+ end
28
+
29
+ module DomainClass
30
+ def keys
31
+ self::Entity.exposures.keys
32
+ end
33
+
34
+ def entity_name
35
+ self::Entity.instance_variable_get( :@root ).to_sym
36
+ end
37
+
38
+ def collection_name
39
+ self::Entity.instance_variable_get( :@collection_root ).to_sym
40
+ end
41
+
42
+ def raise_unless_creatable args
43
+ raise NoMethodError, 'subclasses must provide self.raise_unless_creatable'
44
+ end
45
+ end
46
+
47
+ def == other
48
+ if respond_to?( :id ) and
49
+ other.respond_to?( :id ) and
50
+ ( ( not id.nil? ) and ( not other.id.nil? ) )
51
+ id == other.id
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ def collection_name
58
+ self.class.collection_name
59
+ end
60
+
61
+ def entity_name
62
+ self.class.entity_name
63
+ end
64
+
65
+ def to_s
66
+ "#{entity_name}: #{ MultiJson.dump serializable_hash }"
67
+ end
68
+
69
+ alias :inspect :to_s
70
+
71
+ private
72
+
73
+ def short_klass_name
74
+ self.class.name.match( /[^:]+$/ )[0] + ': '
75
+ end
76
+ end
77
+ end
78
+
79
+ require 'subledger/domain/formatters'
80
+ require 'subledger/domain/roles'
81
+
82
+ require 'subledger/domain/value'
83
+
84
+ require 'subledger/domain/account'
85
+ require 'subledger/domain/balance'
86
+ require 'subledger/domain/book'
87
+ require 'subledger/domain/category'
88
+ require 'subledger/domain/control'
89
+ require 'subledger/domain/identity'
90
+ require 'subledger/domain/journal_entry'
91
+ require 'subledger/domain/key'
92
+ require 'subledger/domain/line'
93
+ require 'subledger/domain/org'
94
+ require 'subledger/domain/report'
95
+ require 'subledger/domain/report_rendering'
@@ -0,0 +1,65 @@
1
+ module Subledger
2
+ class ExceptionHandler
3
+
4
+ # attempts seconds
5
+ # 1 no point!
6
+ # 2 0.15+
7
+ # 3 0.35+
8
+ # 4 0.75+
9
+ # 5 1.55+
10
+ # 6 3.15+
11
+ # 7 6.35+
12
+ # 8 12.75+
13
+ # 9 25.55+ default max_attempts
14
+ # 10 51.15
15
+ #
16
+ # attempts 11 and up are 2 seconds apart
17
+
18
+ def initialize args
19
+ @name = args[:name]
20
+ @exception = args[:exception] || Exception
21
+ @max_attempts = args[:max_attempts] || 10
22
+ @initial_delay = args[:initial_delay] || 0
23
+ end
24
+
25
+ def with_retry
26
+
27
+ attempts = 1
28
+ total = 0
29
+
30
+ sleep @initial_delay
31
+
32
+ begin
33
+ yield
34
+ rescue @exception => e
35
+ if is_internal?( e ) or ( attempts >= @max_attempts )
36
+ raise e, "FAILURE: #{@name}, attempts: #{attempts}, total: #{total}, #{e}"
37
+ elsif attempts < @max_attempts
38
+
39
+ if attempts < 11
40
+ delay = 2 ** attempts * 0.05
41
+ else
42
+ delay += 2
43
+ end
44
+
45
+ if attempts > 8
46
+ LOG.warn "RETRY: #{@name}, attempts: #{attempts}, delay: #{delay}, #{e}"
47
+ end
48
+
49
+ sleep delay
50
+
51
+ total += delay
52
+ attempts += 1
53
+
54
+ retry
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def is_internal? e
62
+ e.class.name.to_s =~ /^Subledger::/
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,295 @@
1
+ module Subledger
2
+ module Interface
3
+ class ClientError < Error; end
4
+
5
+ class Client
6
+
7
+ private
8
+
9
+ attr_reader :client_args
10
+
11
+ COLLECTIONS = { :identity => { :klass => Domain::Identity,
12
+ :root_collection => :identities,
13
+ :state => :active },
14
+ :identities => { :klass => Domain::Identity,
15
+ :root_collection => :identities,
16
+ :state => :active },
17
+ :archived_identity => { :klass => Domain::ArchivedIdentity,
18
+ :root_collection => :identities,
19
+ :state => :archived },
20
+ :archived_identities => { :klass => Domain::ArchivedIdentity,
21
+ :root_collection => :identities,
22
+ :state => :archived },
23
+ :active_identity => { :klass => Domain::ActiveIdentity,
24
+ :root_collection => :identities,
25
+ :state => :active },
26
+ :active_identities => { :klass => Domain::ActiveIdentity,
27
+ :root_collection => :identities,
28
+ :state => :active },
29
+ :key => { :klass => Domain::Key,
30
+ :root_collection => :keys,
31
+ :state => :active },
32
+ :keys => { :klass => Domain::Key,
33
+ :root_collection => :keys,
34
+ :state => :active },
35
+ :archived_key => { :klass => Domain::ArchivedKey,
36
+ :root_collection => :keys,
37
+ :state => :archived },
38
+ :archived_keys => { :klass => Domain::ArchivedKey,
39
+ :root_collection => :keys,
40
+ :state => :archived },
41
+ :active_key => { :klass => Domain::ActiveKey,
42
+ :root_collection => :keys,
43
+ :state => :active },
44
+ :active_keys => { :klass => Domain::ActiveKey,
45
+ :root_collection => :keys,
46
+ :state => :active },
47
+ :control => { :klass => Domain::Control,
48
+ :root_collection => :controls,
49
+ :state => :active },
50
+ :controls => { :klass => Domain::Control,
51
+ :root_collection => :controls,
52
+ :state => :active },
53
+ :archived_control => { :klass => Domain::ArchivedControl,
54
+ :root_collection => :controls,
55
+ :state => :archived },
56
+ :archived_controls => { :klass => Domain::ArchivedControl,
57
+ :root_collection => :controls,
58
+ :state => :archived },
59
+ :active_control => { :klass => Domain::ActiveControl,
60
+ :root_collection => :controls,
61
+ :state => :active },
62
+ :active_controls => { :klass => Domain::ActiveControl,
63
+ :root_collection => :controls,
64
+ :state => :active },
65
+ :org => { :klass => Domain::Org,
66
+ :root_collection => :orgs,
67
+ :state => :active },
68
+ :orgs => { :klass => Domain::Org,
69
+ :root_collection => :orgs,
70
+ :state => :active },
71
+ :archived_org => { :klass => Domain::ArchivedOrg,
72
+ :root_collection => :orgs,
73
+ :state => :archived },
74
+ :archived_orgs => { :klass => Domain::ArchivedOrg,
75
+ :root_collection => :orgs,
76
+ :state => :archived },
77
+ :active_org => { :klass => Domain::ActiveOrg,
78
+ :root_collection => :orgs,
79
+ :state => :active },
80
+ :active_orgs => { :klass => Domain::ActiveOrg,
81
+ :root_collection => :orgs,
82
+ :state => :active },
83
+ :book => { :klass => Domain::Book,
84
+ :root_collection => :books,
85
+ :state => :active },
86
+ :books => { :klass => Domain::Book,
87
+ :root_collection => :books,
88
+ :state => :active },
89
+ :archived_book => { :klass => Domain::ArchivedBook,
90
+ :root_collection => :books,
91
+ :state => :archived },
92
+ :archived_books => { :klass => Domain::ArchivedBook,
93
+ :root_collection => :books,
94
+ :state => :archived },
95
+ :active_book => { :klass => Domain::ActiveBook,
96
+ :root_collection => :books,
97
+ :state => :active },
98
+ :active_books => { :klass => Domain::ActiveBook,
99
+ :root_collection => :books,
100
+ :state => :active },
101
+ :account => { :klass => Domain::Account,
102
+ :root_collection => :accounts,
103
+ :state => :active },
104
+ :accounts => { :klass => Domain::Account,
105
+ :root_collection => :accounts,
106
+ :state => :active },
107
+ :archived_account => { :klass => Domain::ArchivedAccount,
108
+ :root_collection => :accounts,
109
+ :state => :archived },
110
+ :archived_accounts => { :klass => Domain::ArchivedAccount,
111
+ :root_collection => :accounts,
112
+ :state => :archived },
113
+ :active_account => { :klass => Domain::ActiveAccount,
114
+ :root_collection => :accounts,
115
+ :state => :active },
116
+ :active_accounts => { :klass => Domain::ActiveAccount,
117
+ :root_collection => :accounts,
118
+ :state => :active },
119
+ :category_accounts => { :klass => Domain::Account,
120
+ :root_collection => :category_accounts,
121
+ :state => :nil },
122
+ :journal_entry => { :klass => Domain::JournalEntry,
123
+ :root_collection => :journal_entries,
124
+ :state => :active },
125
+ :journal_entries => { :klass => Domain::JournalEntry,
126
+ :root_collection => :journal_entries,
127
+ :state => :active },
128
+ :archived_journal_entry => { :klass => Domain::ArchivedJournalEntry,
129
+ :root_collection => :journal_entries,
130
+ :state => :archived },
131
+ :archived_journal_entries => { :klass => Domain::ArchivedJournalEntry,
132
+ :root_collection => :journal_entries,
133
+ :state => :archived },
134
+ :active_journal_entry => { :klass => Domain::ActiveJournalEntry,
135
+ :root_collection => :journal_entries,
136
+ :state => :active },
137
+ :active_journal_entries => { :klass => Domain::ActiveJournalEntry,
138
+ :root_collection => :journal_entries,
139
+ :state => :active },
140
+ :posting_journal_entry => { :klass => Domain::PostingJournalEntry,
141
+ :root_collection => :journal_entries,
142
+ :state => :posting },
143
+ :posting_journal_entries => { :klass => Domain::PostingJournalEntry,
144
+ :root_collection => :journal_entries,
145
+ :state => :posting },
146
+ :posted_journal_entry => { :klass => Domain::PostedJournalEntry,
147
+ :root_collection => :journal_entries,
148
+ :state => :posted },
149
+ :posted_journal_entries => { :klass => Domain::PostedJournalEntry,
150
+ :root_collection => :journal_entries,
151
+ :state => :posted },
152
+ :line => { :klass => Domain::Line,
153
+ :root_collection => :lines,
154
+ :state => :active },
155
+ :lines => { :klass => Domain::Line,
156
+ :root_collection => :lines,
157
+ :state => :active },
158
+ :archived_line => { :klass => Domain::ArchivedLine,
159
+ :root_collection => :lines,
160
+ :state => :archived },
161
+ :archived_lines => { :klass => Domain::ArchivedLine,
162
+ :root_collection => :lines,
163
+ :state => :archived },
164
+ :active_line => { :klass => Domain::ActiveLine,
165
+ :root_collection => :lines,
166
+ :state => :active },
167
+ :active_lines => { :klass => Domain::ActiveLine,
168
+ :root_collection => :lines,
169
+ :state => :active },
170
+ :posted_line => { :klass => Domain::PostedLine,
171
+ :root_collection => :lines,
172
+ :state => :posted },
173
+ :posted_lines => { :klass => Domain::PostedLine,
174
+ :root_collection => :lines,
175
+ :state => :posted },
176
+ :account_line => { :klass => Domain::PostedLine,
177
+ :root_collection => :account_lines,
178
+ :state => :nil },
179
+ :account_lines => { :klass => Domain::PostedLine,
180
+ :root_collection => :account_lines,
181
+ :state => :nil },
182
+ :category => { :klass => Domain::Category,
183
+ :root_collection => :categories,
184
+ :state => :active },
185
+ :categories => { :klass => Domain::Category,
186
+ :root_collection => :categories,
187
+ :state => :active },
188
+ :archived_category => { :klass => Domain::ArchivedCategory,
189
+ :root_collection => :categories,
190
+ :state => :archived },
191
+ :archived_categories => { :klass => Domain::ArchivedCategory,
192
+ :root_collection => :categories,
193
+ :state => :archived },
194
+ :active_category => { :klass => Domain::ActiveCategory,
195
+ :root_collection => :categories,
196
+ :state => :active },
197
+ :active_categories => { :klass => Domain::ActiveCategory,
198
+ :root_collection => :categories,
199
+ :state => :active },
200
+ :report_categories => { :klass => Domain::Category,
201
+ :root_collection => :report_categories,
202
+ :state => :nil },
203
+ :report => { :klass => Domain::Report,
204
+ :root_collection => :reports,
205
+ :state => :active },
206
+ :reports => { :klass => Domain::Report,
207
+ :root_collection => :reports,
208
+ :state => :active },
209
+ :archived_report => { :klass => Domain::ArchivedReport,
210
+ :root_collection => :reports,
211
+ :state => :archived },
212
+ :archived_reports => { :klass => Domain::ArchivedReport,
213
+ :root_collection => :reports,
214
+ :state => :archived },
215
+ :active_report => { :klass => Domain::ActiveReport,
216
+ :root_collection => :reports,
217
+ :state => :active },
218
+ :active_reports => { :klass => Domain::ActiveReport,
219
+ :root_collection => :reports,
220
+ :state => :active },
221
+ :report_rendering => { :klass => Domain::ReportRendering,
222
+ :root_collection => :report_renderings,
223
+ :state => :building },
224
+ :report_renderings => { :klass => Domain::ReportRendering,
225
+ :root_collection => :report_renderings,
226
+ :state => :building },
227
+ :building_report_rendering => { :klass => Domain::BuildingReportRendering,
228
+ :root_collection => :report_renderings,
229
+ :state => :building },
230
+ :building_report_renderings => { :klass => Domain::BuildingReportRendering,
231
+ :root_collection => :report_renderings,
232
+ :state => :building },
233
+ :completed_report_rendering => { :klass => Domain::CompletedReportRendering,
234
+ :root_collection => :report_renderings,
235
+ :state => :completed },
236
+ :completed_report_renderings => { :klass => Domain::CompletedReportRendering,
237
+ :root_collection => :report_renderings,
238
+ :state => :completed }
239
+ }
240
+
241
+ public
242
+
243
+ attr_reader :store
244
+
245
+ def initialize args
246
+ @client_args = args
247
+
248
+ # TODO client should not have @store and #store
249
+
250
+ @store = args[:store]
251
+ end
252
+
253
+ COLLECTIONS.each do |name, attributes|
254
+ klass = attributes[:klass]
255
+ state = attributes[:state] == :nil ? 'nil' : attributes[:state].inspect
256
+ root_collection = attributes[:root_collection]
257
+
258
+ method_text = <<-"EOM"
259
+ def #{name} *args
260
+ @#{name}_dispatcher ||= Dispatcher.new @client_args.
261
+ merge(
262
+ { :klass => #{klass},
263
+ :client => self,
264
+ :state => #{state},
265
+ :root_collection => :#{root_collection} } )
266
+
267
+ if args.empty?
268
+ @#{name}_dispatcher
269
+ else
270
+ @#{name}_dispatcher.new args.first.dup
271
+ end
272
+ end
273
+ EOM
274
+
275
+ class_eval method_text
276
+ end
277
+
278
+ def debit *args
279
+ args.empty? ? Domain::Debit : Domain::Debit.new( *args )
280
+ end
281
+
282
+ def credit *args
283
+ args.empty? ? Domain::Credit : Domain::Credit.new( *args )
284
+ end
285
+
286
+ def zero *args
287
+ Domain::Zero.new
288
+ end
289
+
290
+ def balance *args
291
+ Domain::Balance.new *args
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,20 @@
1
+ module Subledger
2
+ module Interface
3
+ class Dispatcher
4
+ def initialize args
5
+ @klass = args[:klass]
6
+
7
+ @dispatch_args = args
8
+ end
9
+
10
+ def method_missing method, args, &block
11
+ args.delete :store
12
+ args.delete :client
13
+
14
+ merged_args = @dispatch_args.merge args
15
+
16
+ @klass.send method, merged_args, &block
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ require 'subledger/interface/client'
2
+ require 'subledger/interface/dispatcher'