ledger-rest 2.0.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +1 -1
  4. data/Guardfile +4 -22
  5. data/{LICENSE.txt → LICENSE} +1 -1
  6. data/README.md +62 -11
  7. data/config.ru +1 -1
  8. data/ledger-rest.gemspec +17 -17
  9. data/ledger-rest.yml.example +3 -0
  10. data/lib/ledger-rest/app.rb +21 -34
  11. data/lib/ledger-rest/core_ext.rb +4 -3
  12. data/lib/ledger-rest/git.rb +8 -7
  13. data/lib/ledger-rest/ledger.rb +29 -24
  14. data/lib/ledger-rest/ledger/balance.rb +48 -15
  15. data/lib/ledger-rest/ledger/budget.rb +35 -16
  16. data/lib/ledger-rest/ledger/entry.rb +1 -4
  17. data/lib/ledger-rest/ledger/parser.rb +41 -37
  18. data/lib/ledger-rest/ledger/register.rb +31 -24
  19. data/lib/ledger-rest/ledger/transaction.rb +33 -28
  20. data/lib/ledger-rest/transactions.py +47 -0
  21. data/lib/ledger-rest/version.rb +2 -1
  22. data/spec/files/append.ledger +13 -0
  23. data/spec/files/budget.ledger +4 -0
  24. data/spec/files/main.ledger +4 -0
  25. data/spec/files/opening_balance.ledger +4 -0
  26. data/spec/files/transactions.ledger +11 -0
  27. data/spec/ledger-rest.yml +3 -0
  28. data/spec/ledger-rest/api/accounts_spec.rb +19 -0
  29. data/spec/ledger-rest/api/balance_spec.rb +117 -0
  30. data/spec/ledger-rest/api/budget_spec.rb +110 -0
  31. data/spec/ledger-rest/api/payees_spec.rb +19 -0
  32. data/spec/ledger-rest/api/register_spec.rb +233 -0
  33. data/spec/ledger-rest/api/transactions_spec.rb +201 -0
  34. data/spec/ledger-rest/api/version_spec.rb +13 -0
  35. data/spec/ledger-rest/ledger/balance_spec.rb +124 -0
  36. data/spec/ledger-rest/ledger/parser_spec.rb +184 -117
  37. data/spec/ledger-rest/ledger/transaction_spec.rb +38 -31
  38. data/spec/spec_helper.rb +22 -0
  39. data/spec/support/deep_eq_matcher.rb +149 -0
  40. metadata +52 -31
  41. data/ledger-rest.org +0 -22
@@ -0,0 +1,201 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe '/transactions' do
5
+ describe 'GET' do
6
+ let(:valid_json) do
7
+ [
8
+ {
9
+ 'date' => '2013/01/01',
10
+ 'payee' => 'Opening Balance',
11
+ 'cleared' => true,
12
+ 'posts' =>
13
+ [
14
+ {
15
+ 'account' => 'Assets:Giro',
16
+ 'amount' => 2000.0,
17
+ 'commodity' => 'EUR'
18
+ }, {
19
+ 'account' => 'Assets:Reimbursements:Hans Maulwurf',
20
+ 'amount' => 19.0,
21
+ 'commodity' => 'EUR'
22
+ }, {
23
+ 'account' => 'Equity:Opening Balances',
24
+ 'amount' => -2019.0,
25
+ 'commodity' => 'EUR'
26
+ }
27
+ ],
28
+ 'pending' => false
29
+ }, {
30
+ 'date' => '2013/12/03',
31
+ 'payee' => 'NaveenaPath',
32
+ 'cleared' => false,
33
+ 'posts' =>
34
+ [
35
+ {
36
+ 'account' => 'Expenses:Restaurants',
37
+ 'amount' => 9.0,
38
+ 'commodity' => 'EUR'
39
+ }, {
40
+ 'account' => 'Assets:Cash',
41
+ 'amount' => -9.0,
42
+ 'commodity' => 'EUR'
43
+ }
44
+ ],
45
+ 'pending' => false
46
+ }, {
47
+ 'date' => '2013/12/05',
48
+ 'payee' => 'Shikgoo',
49
+ 'cleared' => false,
50
+ 'posts' =>
51
+ [
52
+ {
53
+ 'account' => 'Expenses:Restaurants',
54
+ 'amount' => 12.0,
55
+ 'commodity' => 'EUR'
56
+ }, {
57
+ 'account' => 'Liabilities:Max Mustermann',
58
+ 'amount' => -12.0,
59
+ 'commodity' => 'EUR'
60
+ }
61
+ ],
62
+ 'pending' => false
63
+ }, {
64
+ 'posts' =>
65
+ [
66
+ {
67
+ 'account' => 'Assets:Reimbursements:Hans Maulwurf',
68
+ 'amount' => 3.1,
69
+ 'commodity' => 'EUR'
70
+ }, {
71
+ 'account' => 'Assets:Cash',
72
+ 'amount' => -3.1,
73
+ 'commodity' => 'EUR'
74
+ }
75
+ ],
76
+ 'note' => 'Sonnenblumenkernbrot',
77
+ 'payee' => 'Bioladen Tegeler Straße',
78
+ 'date' => '2013/12/10',
79
+ 'cleared' => false,
80
+ 'pending' => false
81
+ }, {
82
+ 'effective_date' => '2013/12/02',
83
+ 'posts' =>
84
+ [
85
+ {
86
+ 'account' => 'Assets:Cash',
87
+ 'amount' => 50.0,
88
+ 'commodity' => 'EUR'
89
+ }, {
90
+ 'account' => 'Assets:Giro',
91
+ 'amount' => -50.0,
92
+ 'commodity' => 'EUR'
93
+ }
94
+ ],
95
+ 'payee' => 'Sparkasse',
96
+ 'date' => '2013/12/01',
97
+ 'cleared' => true,
98
+ 'pending' => false
99
+ }, {
100
+ 'date' => '2013/12/04',
101
+ 'payee' => 'Trattoria La Vialla',
102
+ 'cleared' => false,
103
+ 'posts' =>
104
+ [
105
+ {
106
+ 'account' => 'Expenses:Restaurants',
107
+ 'amount' => 32.2,
108
+ 'commodity' => 'EUR'
109
+ }, {
110
+ 'account' => 'Assets:Cash',
111
+ 'amount' => -32.2,
112
+ 'commodity' => 'EUR'
113
+ }
114
+ ],
115
+ 'pending' => false
116
+ }, {
117
+ 'date' => '2013/12/06',
118
+ 'payee' => 'Customer X',
119
+ 'cleared' => false,
120
+ 'posts' =>
121
+ [
122
+ {
123
+ 'account' => 'Assets:Giro',
124
+ 'amount' => 1200.0,
125
+ 'commodity' => 'EUR'
126
+ }, {
127
+ 'account' => 'Income:Invoice',
128
+ 'amount' => -1200.0,
129
+ 'commodity' => 'EUR'
130
+ }
131
+ ],
132
+ 'pending' => false
133
+ }
134
+ ]
135
+ end
136
+
137
+ it 'returns all transactions' do
138
+ get '/transactions'
139
+ JSON.parse(last_response.body).should deep_eq valid_json
140
+ end
141
+ end
142
+
143
+ describe 'POST' do
144
+ let(:transaction) do
145
+ {
146
+ date: '2013/12/12',
147
+ cleared: true,
148
+ payee: 'New Payee',
149
+ postings:
150
+ [
151
+ {
152
+ account: 'Expenses:Restaurants',
153
+ amount: 11.0,
154
+ commodity: 'EUR'
155
+ },
156
+ {
157
+ account: 'Assets:Cash',
158
+ amount: -11.0,
159
+ commodity: 'EUR'
160
+ }
161
+ ]
162
+ }
163
+ end
164
+
165
+ let(:correct_response) do
166
+ { transaction: transaction }
167
+ end
168
+
169
+ it 'adds a new transaction to the append file' do
170
+ restore_file('spec/files/append.ledger') do
171
+ post '/transactions', transaction.to_json
172
+
173
+ last_response.status.should == 201
174
+ JSON.parse(last_response.body, symbolize_names: true).should deep_eq correct_response
175
+ puts File.read('spec/files/append.ledger').should == <<RESULT
176
+ 2013/12/03 NaveenaPath
177
+ Expenses:Restaurants 9.00EUR
178
+ Assets:Cash
179
+
180
+ 2013/12/05 Shikgoo
181
+ Expenses:Restaurants 12.00EUR
182
+ Liabilities:Max Mustermann
183
+ ; meta_info: Some interesting meta information
184
+
185
+ 2013/12/10 Bioladen Tegeler Straße
186
+ ; Sonnenblumenkernbrot
187
+ Assets:Reimbursements:Hans Maulwurf 3.10EUR
188
+ Assets:Cash
189
+
190
+ 2013/12/12 * New Payee
191
+ Expenses:Restaurants 11.00EUR
192
+ Assets:Cash -11.00EUR
193
+ RESULT
194
+ end
195
+ end
196
+ end
197
+
198
+ describe 'PUT' do
199
+ it 'updates a transaction'
200
+ end
201
+ end
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe '/version' do
5
+ it 'returns the ledger-rest version' do
6
+ get '/version'
7
+
8
+ JSON.parse(last_response.body).should == {
9
+ 'version' => LedgerRest::VERSION,
10
+ 'ledger-version' => LedgerRest::Ledger.version
11
+ }
12
+ end
13
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe LedgerRest::Ledger::Balance do
4
+ describe '::expand_accounts' do
5
+ let(:accounts) do
6
+ [
7
+ {
8
+ fullname: 'Expenses',
9
+ name: 'Expenses',
10
+ depth: 1,
11
+ total: '453.10EUR'
12
+ },
13
+ {
14
+ fullname: 'Expenses:Groceries:Bread',
15
+ name: 'Groceries:Bread',
16
+ depth: 3,
17
+ total: '3.10EUR'
18
+ },
19
+ {
20
+ fullname: 'Assets:Cash',
21
+ name: 'Assets:Cash',
22
+ depth: 1,
23
+ total: '200.00EUR'
24
+ }
25
+ ]
26
+ end
27
+
28
+ let(:expanded) do
29
+ [
30
+ { fullname: 'Expenses', name: 'Expenses', depth: 1, total: '453.10EUR' },
31
+ { fullname: 'Expenses:Groceries', name: 'Groceries', depth: 2, total: '3.10EUR' },
32
+ { fullname: 'Expenses:Groceries:Bread', name: 'Bread', depth: 3, total: '3.10EUR' },
33
+ { fullname: 'Assets', name: 'Assets', depth: 1, total: '200.00EUR' },
34
+ { fullname: 'Assets:Cash', name: 'Cash', depth: 2, total: '200.00EUR' }
35
+ ]
36
+ end
37
+
38
+ it 'expands accounts correctly' do
39
+ LedgerRest::Ledger::Balance.expand_accounts(accounts).should deep_eq expanded
40
+ end
41
+ end
42
+
43
+ describe '::wrap_accounts' do
44
+ let(:accounts) do
45
+ [
46
+ {
47
+ fullname: 'Expenses',
48
+ name: 'Expenses',
49
+ depth: 1,
50
+ total: '453.10EUR'
51
+ },
52
+ {
53
+ fullname: 'Expenses:Groceries',
54
+ name: 'Groceries',
55
+ depth: 2,
56
+ total: '3.10EUR'
57
+ },
58
+ {
59
+ fullname: 'Expenses:Groceries:Bread',
60
+ name: 'Bread',
61
+ depth: 3,
62
+ total: '3.10EUR'
63
+ },
64
+ {
65
+ fullname: 'Expenses:Flat',
66
+ name: 'Flat',
67
+ depth: 2,
68
+ total: '450.00EUR'
69
+ },
70
+ {
71
+ fullname: 'Assets',
72
+ name: 'Assets',
73
+ depth: 1,
74
+ total: '200.00EUR'
75
+ }
76
+ ]
77
+ end
78
+
79
+ let(:wrapped) do
80
+ [
81
+ {
82
+ fullname: 'Expenses',
83
+ name: 'Expenses',
84
+ depth: 1,
85
+ total: '453.10EUR',
86
+ accounts:
87
+ [
88
+ {
89
+ fullname: 'Expenses:Groceries',
90
+ name: 'Groceries',
91
+ depth: 2,
92
+ total: '3.10EUR',
93
+ accounts:
94
+ [
95
+ {
96
+ fullname: 'Expenses:Groceries:Bread',
97
+ name: 'Bread',
98
+ depth: 3,
99
+ total: '3.10EUR'
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ fullname: 'Expenses:Flat',
105
+ name: 'Flat',
106
+ depth: 2,
107
+ total: '450.00EUR'
108
+ }
109
+ ]
110
+ },
111
+ {
112
+ fullname: 'Assets',
113
+ name: 'Assets',
114
+ depth: 1,
115
+ total: '200.00EUR'
116
+ }
117
+ ]
118
+ end
119
+
120
+ it 'wraps accounts correctly' do
121
+ LedgerRest::Ledger::Balance.wrap_accounts(accounts).should deep_eq wrapped
122
+ end
123
+ end
124
+ end
@@ -6,79 +6,83 @@ describe LedgerRest::Ledger::Parser do
6
6
  @parser = LedgerRest::Ledger::Parser.new
7
7
  end
8
8
 
9
- context '#parse' do
10
- subject {
11
- LedgerRest::Ledger::Transaction.new(:date => "2012/03/01",
12
- :effective_date => "2012/03/23",
13
- :cleared => true,
14
- :pending => false,
15
- :code => "INV#23",
16
- :payee => "me, myself and I",
17
- :postings => [
18
- {
19
- :account => "Expenses:Imaginary",
20
- :amount => "€ 23",
21
- :per_unit_cost => "USD 2300",
22
- :actual_date => "2012/03/24",
23
- :effective_date => "2012/03/25"
24
- }, {
25
- :account => "Expenses:Magical",
26
- :amount => "€ 42",
27
- :posting_cost => "USD 23000000",
28
- :virtual => true
29
- }, {
30
- :account => "Assets:Mighty"
31
- }, {
32
- :comment => "This is a freeform comment"
33
- },
34
- ])
35
- }
9
+ describe '#parse' do
10
+ subject do
11
+ LedgerRest::Ledger::Transaction.new(date: '2012/03/01',
12
+ effective_date: '2012/03/23',
13
+ cleared: true,
14
+ pending: false,
15
+ code: 'INV#23',
16
+ payee: 'me, myself and I',
17
+ postings:
18
+ [
19
+ {
20
+ account: 'Expenses:Imaginary',
21
+ amount: 23.0,
22
+ commodity: 'EUR',
23
+ per_unit_cost: 2300.0,
24
+ per_unit_commodity: 'USD',
25
+ actual_date: '2012/03/24',
26
+ effective_date: '2012/03/25'
27
+ }, {
28
+ account: 'Expenses:Magical',
29
+ amount: 42.0,
30
+ commodity: 'EUR',
31
+ posting_cost: 23000000.0,
32
+ posting_cost_commodity: 'USD',
33
+ virtual: true
34
+ }, {
35
+ account: 'Assets:Mighty'
36
+ }, {
37
+ comment: 'This is a freeform comment'
38
+ },
39
+ ])
40
+ end
36
41
 
37
42
  it 'should parse a to_ledger converted transaction into the same original hash' do
38
43
  @parser.parse(subject.to_ledger).should == subject
39
44
  end
40
-
41
45
  end
42
46
 
43
- context '#parse_date' do
47
+ describe '#parse_date' do
44
48
  before :all do
45
49
  @ret = @parser.parse_date("2012/11/23 * Rest with\nAnd Stuff")
46
50
  end
47
51
 
48
- it 'should return the parsed date' do
52
+ it 'returns the parsed date' do
49
53
  @ret[0].should == '2012/11/23'
50
54
  end
51
55
 
52
- it 'should return the rest of the input' do
56
+ it 'returns the rest of the input' do
53
57
  @ret[1].should == " * Rest with\nAnd Stuff"
54
58
  end
55
59
  end
56
60
 
57
- context '#parse_effective_date' do
61
+ describe '#parse_effective_date' do
58
62
  before :all do
59
63
  @ret = @parser.parse_effective_date("=2012/11/24 * Rest with\nAnd Stuff")
60
64
  end
61
65
 
62
- it 'should return the parsed date' do
63
- @ret[0].should == "2012/11/24"
66
+ it 'returns the parsed date' do
67
+ @ret[0].should == '2012/11/24'
64
68
  end
65
69
 
66
- it 'should return the rest of the input' do
70
+ it 'returns the rest of the input' do
67
71
  @ret[1].should == " * Rest with\nAnd Stuff"
68
72
  end
69
73
  end
70
74
 
71
- context '#parse_state' do
75
+ describe '#parse_state' do
72
76
  context 'given cleared transaction input' do
73
77
  before :all do
74
78
  @ret = @parser.parse_cleared(" * Rest with\nAnd Stuff")
75
79
  end
76
80
 
77
- it 'should return true' do
81
+ it 'returns true' do
78
82
  @ret[0].should == true
79
83
  end
80
84
 
81
- it 'should return the rest of the input' do
85
+ it 'returns the rest of the input' do
82
86
  @ret[1].should == "Rest with\nAnd Stuff"
83
87
  end
84
88
  end
@@ -88,27 +92,27 @@ describe LedgerRest::Ledger::Parser do
88
92
  @ret = @parser.parse_cleared("Rest with\nAnd Stuff")
89
93
  end
90
94
 
91
- it 'should return false' do
95
+ it 'returns false' do
92
96
  @ret[0].should == false
93
97
  end
94
98
 
95
- it 'should return the rest of the input' do
99
+ it 'returns the rest of the input' do
96
100
  @ret[1].should == "Rest with\nAnd Stuff"
97
101
  end
98
102
  end
99
103
  end
100
104
 
101
- context '#parse_pending' do
105
+ describe '#parse_pending' do
102
106
  context 'given pending transaction input' do
103
107
  before :all do
104
108
  @ret = @parser.parse_pending(" ! Rest with\nAnd Stuff")
105
109
  end
106
110
 
107
- it 'should return true' do
111
+ it 'returns true' do
108
112
  @ret[0].should == true
109
113
  end
110
114
 
111
- it 'should return the rest of the input' do
115
+ it 'returns the rest of the input' do
112
116
  @ret[1].should == "Rest with\nAnd Stuff"
113
117
  end
114
118
  end
@@ -118,22 +122,22 @@ describe LedgerRest::Ledger::Parser do
118
122
  @ret = @parser.parse_pending("Rest with\nAnd Stuff")
119
123
  end
120
124
 
121
- it 'should return false' do
125
+ it 'returns false' do
122
126
  @ret[0].should == false
123
127
  end
124
128
 
125
- it 'should return the rest of the input' do
129
+ it 'returns the rest of the input' do
126
130
  @ret[1].should == "Rest with\nAnd Stuff"
127
131
  end
128
132
  end
129
133
  end
130
134
 
131
- context '#parse_code' do
135
+ describe '#parse_code' do
132
136
  context 'given a transaction with white-spaced code' do
133
137
  subject { @parser.parse_code(" (#123) Rest with\nAnd Stuff") }
134
138
 
135
139
  its(:first) { should == '#123' }
136
- its(:last) { should == "Rest with\nAnd Stuff"}
140
+ its(:last) { should == "Rest with\nAnd Stuff" }
137
141
  end
138
142
 
139
143
  context 'given a transaction with code' do
@@ -146,124 +150,124 @@ describe LedgerRest::Ledger::Parser do
146
150
  context 'given a transaction without code' do
147
151
  subject { @parser.parse_code("Rest with\nAnd Stuff") }
148
152
 
149
- its(:first) { should == nil }
153
+ its(:first) { should be_nil }
150
154
  its(:last) { should == "Rest with\nAnd Stuff" }
151
155
  end
152
156
  end
153
157
 
154
- context '#parse_payee' do
158
+ describe '#parse_payee' do
155
159
  context 'given an unstripped line' do
156
160
  subject { @parser.parse_payee(" Monsieur Le Payee\n Some:Account 123EUR\n Some:Other")}
157
161
 
158
- its(:first) { should == "Monsieur Le Payee" }
159
- its(:last) { should == " Some:Account 123EUR\n Some:Other"}
162
+ its(:first) { should == 'Monsieur Le Payee' }
163
+ its(:last) { should == " Some:Account 123EUR\n Some:Other" }
160
164
  end
161
165
 
162
166
  context 'given a stripped line' do
163
167
  context 'given an unstripped line' do
164
168
  subject { @parser.parse_payee("Monsieur Le Payee\n Some:Account 123EUR\n Some:Other")}
165
169
 
166
- its(:first) { should == "Monsieur Le Payee" }
167
- its(:last) { should == " Some:Account 123EUR\n Some:Other"}
170
+ its(:first) { should == 'Monsieur Le Payee' }
171
+ its(:last) { should == " Some:Account 123EUR\n Some:Other" }
168
172
  end
169
173
  end
170
174
  end
171
175
 
172
- context '#parse_comments' do
176
+ describe '#parse_comments' do
173
177
  context 'given no comments' do
174
- subject { @parser.parse_comments(" Assets:Some:Stuff 23EUR")}
178
+ subject { @parser.parse_comments(' Assets:Some:Stuff 23EUR')}
175
179
 
176
- it 'should return all comments' do
177
- subject[0].should == nil
180
+ it 'returns all comments' do
181
+ subject[0].should be_nil
178
182
  end
179
183
 
180
- it 'should return the rest of the input' do
181
- subject[1].should == " Assets:Some:Stuff 23EUR"
184
+ it 'returns the rest of the input' do
185
+ subject[1].should == ' Assets:Some:Stuff 23EUR'
182
186
  end
183
187
  end
184
188
 
185
189
  context 'given one line of transaction comments' do
186
190
  subject { @parser.parse_comments(" ; ABC\n Assets:Some:Stuff 23EUR")}
187
191
 
188
- it 'should return all comments' do
192
+ it 'returns all comments' do
189
193
  subject[0].should == "ABC\n"
190
194
  end
191
195
 
192
- it 'should return the rest of the input' do
193
- subject[1].should == " Assets:Some:Stuff 23EUR"
196
+ it 'returns the rest of the input' do
197
+ subject[1].should == ' Assets:Some:Stuff 23EUR'
194
198
  end
195
199
  end
196
200
 
197
201
  context 'given multiple lines of transaction comments' do
198
202
  subject { @parser.parse_comments(" ; ABC\n ;DEF\n Assets:Some:Stuff 23EUR")}
199
203
 
200
- it 'should return all comments' do
204
+ it 'returns all comments' do
201
205
  subject[0].should == "ABC\nDEF\n"
202
206
  end
203
207
 
204
- it 'should return the rest of the input' do
205
- subject[1].should == " Assets:Some:Stuff 23EUR"
208
+ it 'returns the rest of the input' do
209
+ subject[1].should == ' Assets:Some:Stuff 23EUR'
206
210
  end
207
211
  end
208
212
  end
209
213
 
210
- context '#parse_account' do
214
+ describe '#parse_account' do
211
215
  context 'given normal' do
212
216
  subject { @parser.parse_account(" Assets:Some:Nice 200EUR\n Assets:Account")}
213
217
 
214
- it 'should return the account' do
215
- subject[0].should == "Assets:Some:Nice"
218
+ it 'returns the account' do
219
+ subject[0].should == 'Assets:Some:Nice'
216
220
  end
217
221
 
218
- it 'should not be virtual' do
222
+ it 'is not virtual' do
219
223
  subject[1].should == false
220
224
  end
221
225
 
222
- it 'should not be balanced virtual' do
226
+ it 'is not balanced virtual' do
223
227
  subject[2].should == false
224
228
  end
225
229
 
226
- it 'should return the rest of the input' do
230
+ it 'returns the rest of the input' do
227
231
  subject[3].should == "200EUR\n Assets:Account"
228
232
  end
229
233
  end
230
234
 
231
235
  context 'given input without amount' do
232
- subject { @parser.parse_account(" Assets:Some:Nice")}
236
+ subject { @parser.parse_account(' Assets:Some:Nice') }
233
237
 
234
- it 'should return the account' do
235
- subject[0].should == "Assets:Some:Nice"
238
+ it 'returns the account' do
239
+ subject[0].should == 'Assets:Some:Nice'
236
240
  end
237
241
 
238
- it 'should not be virtual' do
242
+ it 'is not virtual' do
239
243
  subject[1].should == false
240
244
  end
241
245
 
242
- it 'should not be balanced virtual' do
246
+ it 'is not balanced virtual' do
243
247
  subject[2].should == false
244
248
  end
245
249
 
246
- it 'should return the rest of the input' do
247
- subject[3].should == ""
250
+ it 'returns the rest of the input' do
251
+ subject[3].should == ''
248
252
  end
249
253
  end
250
254
 
251
255
  context 'given virtual' do
252
256
  subject { @parser.parse_account(" (Assets:Some:Nice) 200EUR\n Assets:Account")}
253
257
 
254
- it 'should return the account' do
255
- subject[0].should == "Assets:Some:Nice"
258
+ it 'returns the account' do
259
+ subject[0].should == 'Assets:Some:Nice'
256
260
  end
257
261
 
258
- it 'should not be virtual' do
262
+ it 'is not virtual' do
259
263
  subject[1].should == true
260
264
  end
261
265
 
262
- it 'should not be balanced virtual' do
266
+ it 'is not balanced virtual' do
263
267
  subject[2].should == false
264
268
  end
265
269
 
266
- it 'should return the rest of the input' do
270
+ it 'returns the rest of the input' do
267
271
  subject[3].should == "200EUR\n Assets:Account"
268
272
  end
269
273
  end
@@ -271,92 +275,155 @@ describe LedgerRest::Ledger::Parser do
271
275
  context 'given balanced virtual' do
272
276
  subject { @parser.parse_account(" [Assets:Some:Nice] 200EUR\n Assets:Account")}
273
277
 
274
- it 'should return the account' do
275
- subject[0].should == "Assets:Some:Nice"
278
+ it 'returns the account' do
279
+ subject[0].should == 'Assets:Some:Nice'
276
280
  end
277
281
 
278
- it 'should not be virtual' do
282
+ it 'is not virtual' do
279
283
  subject[1].should == true
280
284
  end
281
285
 
282
- it 'should not be balanced virtual' do
286
+ it 'is not balanced virtual' do
283
287
  subject[2].should == true
284
288
  end
285
289
 
286
- it 'should return the rest of the input' do
290
+ it 'returns the rest of the input' do
287
291
  subject[3].should == "200EUR\n Assets:Account"
288
292
  end
289
293
  end
290
294
  end
291
295
 
292
- context '#parse_amount' do
296
+ describe '#parse_amount_parts' do
297
+ context 'given "23.00EUR"' do
298
+ subject { @parser.parse_amount_parts('23.00EUR') }
299
+
300
+ it 'returns the value' do
301
+ subject[0].should == 23.0
302
+ end
303
+
304
+ it 'returns the commodity' do
305
+ subject[1].should == 'EUR'
306
+ end
307
+ end
308
+
309
+ context 'given "23EUR"' do
310
+ subject { @parser.parse_amount_parts('23EUR') }
311
+
312
+ it 'returns the value' do
313
+ subject[0].should == 23.0
314
+ end
315
+
316
+ it 'returns the commodity' do
317
+ subject[1].should == 'EUR'
318
+ end
319
+ end
320
+
321
+ context 'given "USD23"' do
322
+ subject { @parser.parse_amount_parts('USD23') }
323
+
324
+ it 'returns the value' do
325
+ subject[0].should == 23.0
326
+ end
327
+
328
+ it 'returns the commodity' do
329
+ subject[1].should == 'USD'
330
+ end
331
+ end
332
+
333
+ context 'given "USD23.00"' do
334
+ subject { @parser.parse_amount_parts('USD23') }
335
+
336
+ it 'returns the value' do
337
+ subject[0].should == 23.0
338
+ end
339
+
340
+ it 'returns the commodity' do
341
+ subject[1].should == 'USD'
342
+ end
343
+ end
344
+
345
+ context 'given "€ 23.00"' do
346
+ subject { @parser.parse_amount_parts('€ 23.00') }
347
+
348
+ it 'returns the value' do
349
+ subject[0].should == 23.0
350
+ end
351
+
352
+ it 'returns the commodity' do
353
+ subject[1].should == '€'
354
+ end
355
+ end
356
+ end
357
+
358
+ describe '#parse_amount' do
293
359
  context 'given "23.00EUR"' do
294
- subject { @parser.parse_amount("23.00EUR") }
360
+ subject { @parser.parse_amount('23.00EUR') }
295
361
 
296
- it 'should return amount and commodity' do
297
- subject[0].should == "23.00EUR"
362
+ it 'returns amount and commodity' do
363
+ subject[0].should == '23.00EUR'
298
364
  end
299
365
 
300
- it 'should return no posting_cost' do
301
- subject[1].should == nil
366
+ it 'returns no posting_cost' do
367
+ subject[1].should be_nil
302
368
  end
303
369
 
304
- it 'should return no per_unit_cost' do
305
- subject[2].should == nil
370
+ it 'returns no per_unit_cost' do
371
+ subject[2].should be_nil
306
372
  end
307
373
  end
308
374
 
309
375
  context 'given "25 AAPL @ 10.00EUR"' do
310
- subject { @parser.parse_amount("25 AAPL @ 10.00EUR") }
376
+ subject { @parser.parse_amount('25 AAPL @ 10.00EUR') }
311
377
 
312
- it 'should return amount and commodity' do
313
- subject[0].should == "25 AAPL"
378
+ it 'returns amount and commodity' do
379
+ subject[0].should == '25 AAPL'
314
380
  end
315
381
 
316
- it 'should return correct posting_cost' do
317
- subject[1].should == "10.00EUR"
382
+ it 'returns correct posting_cost' do
383
+ subject[1].should == '10.00EUR'
318
384
  end
319
385
 
320
- it 'should return no per_unit_cost' do
321
- subject[2].should == nil
386
+ it 'returns no per_unit_cost' do
387
+ subject[2].should be_nil
322
388
  end
323
389
  end
324
390
 
325
391
  context 'given "30Liters @@ 1.64EUR"' do
326
- subject { @parser.parse_amount("30Liters @@ 1.64EUR") }
392
+ subject { @parser.parse_amount('30Liters @@ 1.64EUR') }
327
393
 
328
- it 'should return amount and commodity' do
329
- subject[0].should == "30Liters"
394
+ it 'returns amount and commodity' do
395
+ subject[0].should == '30Liters'
330
396
  end
331
397
 
332
- it 'should return no posting_cost' do
333
- subject[1].should == nil
398
+ it 'returns no posting_cost' do
399
+ subject[1].should be_nil
334
400
  end
335
401
 
336
- it 'should return correct per_unit_cost' do
337
- subject[2].should == "1.64EUR"
402
+ it 'returns correct per_unit_cost' do
403
+ subject[2].should == '1.64EUR'
338
404
  end
339
405
  end
340
406
  end
341
407
 
342
- context '#parse_posting' do
408
+ describe '#parse_posting' do
343
409
  context 'given posting with comment' do
344
410
  subject { @parser.parse_posting(" Assets:Test:Account 123EUR\n ; Some comment") }
345
411
 
346
- it 'should have parsed correctly' do
412
+ it 'has parsed correctly' do
347
413
  subject.should == {
348
- :account => "Assets:Test:Account",
349
- :amount => "123EUR"
414
+ account: 'Assets:Test:Account',
415
+ amount: 123.0,
416
+ commodity: 'EUR'
350
417
  }
351
418
  end
352
419
  end
353
420
 
354
421
  context 'given source posting' do
355
- subject { @parser.parse_posting(" Assets:Test:Account") }
422
+ subject { @parser.parse_posting(' Assets:Test:Account') }
356
423
 
357
- it 'should have parsed correctly' do
424
+ it 'has parsed correctly' do
358
425
  subject.should == {
359
- :account => "Assets:Test:Account"
426
+ account: 'Assets:Test:Account'
360
427
  }
361
428
  end
362
429
  end