total_recall 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/README.md +41 -5
- data/lib/total_recall/templates/sample.yml.tt +16 -10
- data/lib/total_recall/version.rb +1 -1
- data/lib/total_recall.rb +97 -31
- data/spec/spec_helper.rb +2 -5
- data/spec/total_recall/total_recall_spec.rb +53 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f485de3326ba5aa633added9df0493ab450c74d
|
4
|
+
data.tar.gz: 592cba4e119cf53c307c9734b6d518bb96b90ac5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4e8ef75a9d06eef5b482edd546535d85472ae299bb004216d44b75a4d072e0eabfc9e430203f273bc86ffefdf5eb2f28cd06433da2ae2f30887d63c9439bd63
|
7
|
+
data.tar.gz: 38ee00655b8c99b10855fb7bfca8d4c8625886e5e0cb218f47241797b9aa19560da19e45b48936ca09ca6aff607a9150e0eda1b3a701fdfd91de8b9735d76f83
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# 0.5.0 / unreleased
|
2
|
+
|
3
|
+
* extend the ledger subcommand by passing it a file with customizations
|
4
|
+
|
5
|
+
```
|
6
|
+
$ cat my_extension.rb
|
7
|
+
module MyExtension
|
8
|
+
def ask_account(*args)
|
9
|
+
# some custom stuff
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
TotalRecall::SessionHelper.include MyExtension
|
14
|
+
|
15
|
+
$ total_recall ledger -c sample.yml -r ./my_extension.rb
|
16
|
+
```
|
17
|
+
|
18
|
+
* add version subcommand
|
19
|
+
|
20
|
+
* add default-helper
|
21
|
+
|
22
|
+
Let's you point to the default-value of an attribute:
|
23
|
+
|
24
|
+
```yaml
|
25
|
+
:context:
|
26
|
+
:transactions:
|
27
|
+
:__defaults__:
|
28
|
+
:a: 1
|
29
|
+
:a: !!proc |
|
30
|
+
ask("What value has a?", default: default)
|
31
|
+
```
|
32
|
+
|
33
|
+
* add transaction-helper
|
34
|
+
|
35
|
+
This allows you to use already set attributes of the transaction:
|
36
|
+
|
37
|
+
```yaml
|
38
|
+
:context:
|
39
|
+
:transactions:
|
40
|
+
:a: 1
|
41
|
+
:b: !!proc |
|
42
|
+
transaction.a.succ
|
43
|
+
```
|
44
|
+
|
45
|
+
# 0.4.0 / 2014-06-04
|
46
|
+
|
47
|
+
* Add yaml-config.
|
48
|
+
|
data/README.md
CHANGED
@@ -2,13 +2,19 @@
|
|
2
2
|
|
3
3
|
Turn **any** csv-file into a [Ledger](http://ledger-cli.org/) journal.
|
4
4
|
|
5
|
+
## Install
|
6
|
+
|
7
|
+
```bash
|
8
|
+
gem install total_recall
|
9
|
+
```
|
10
|
+
|
5
11
|
## Quickstart
|
6
12
|
|
7
13
|
### Generate a sample config
|
8
14
|
|
9
15
|
```bash
|
10
16
|
total_recall sample
|
11
|
-
# An annotated config 'sample.yml' will be written
|
17
|
+
# An annotated config 'sample.yml' will be written the current directory.
|
12
18
|
```
|
13
19
|
|
14
20
|
### Generate a ledger
|
@@ -58,10 +64,11 @@ May I suggest:
|
|
58
64
|
total_recall
|
59
65
|
|
60
66
|
# Commands:
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
67
|
+
# total_recall help [COMMAND] # Describe available commands or one specific command
|
68
|
+
# total_recall init NAME # Generate a minimal config NAME.yml
|
69
|
+
# total_recall ledger -c, --config=CONFIG # Convert CONFIG to a ledger
|
70
|
+
# total_recall sample # Generate an annotated config
|
71
|
+
# total_recall version # Show total_recall version
|
65
72
|
|
66
73
|
# typically you would do:
|
67
74
|
total_recall init my-bank
|
@@ -78,6 +85,35 @@ total_recall ledger -c my-bank.yml > my-bank.dat
|
|
78
85
|
ledger -f my-bank.dat bal
|
79
86
|
```
|
80
87
|
|
88
|
+
## Extensibility
|
89
|
+
|
90
|
+
You can extend the ledger subcommand by passing a file with additions to it:
|
91
|
+
|
92
|
+
```
|
93
|
+
total_recall ledger -c my-bank.yml -r ./my_extension.rb
|
94
|
+
```
|
95
|
+
|
96
|
+
This makes it possible to add helpers or redefine existing ones:
|
97
|
+
|
98
|
+
```
|
99
|
+
cat my_extension.rb
|
100
|
+
module MyExtension
|
101
|
+
# adding some options to an existing helper:
|
102
|
+
def ask_account(question, options = {})
|
103
|
+
question.upcase! if options.delete(:scream)
|
104
|
+
super
|
105
|
+
end
|
106
|
+
|
107
|
+
# a new helper:
|
108
|
+
def guess_account(question, options = {})
|
109
|
+
guess = Guesser.new.guess
|
110
|
+
ask_account(question, default: guess)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
TotalRecall::SessionHelper.include MyExtension
|
115
|
+
```
|
116
|
+
|
81
117
|
## Develop
|
82
118
|
|
83
119
|
```bash
|
@@ -36,12 +36,14 @@
|
|
36
36
|
#
|
37
37
|
#:file: sample.csv
|
38
38
|
:raw: |-
|
39
|
-
"
|
40
|
-
"03.11.2013";"
|
39
|
+
"date";"description";"effective_data";"amount"
|
40
|
+
"03.11.2013";"Foo";"04.11.2013";"1.638,00"
|
41
|
+
"03.11.2013";"Bar";"04.11.2013";"-492,93"
|
41
42
|
:options:
|
42
43
|
# Any option accepted by CSV#new (http://www.ruby-doc.org/stdlib-2.1.1/libdoc/csv/rdoc/CSV.html#method-c-new).
|
43
44
|
:col_sep: ";"
|
44
|
-
:headers:
|
45
|
+
:headers: true
|
46
|
+
:header_converters: :symbol # row[:date] rather than row[0]
|
45
47
|
|
46
48
|
:context:
|
47
49
|
# 3). Define the context
|
@@ -56,15 +58,19 @@
|
|
56
58
|
default: 'Assets:Checking')
|
57
59
|
:currency: $
|
58
60
|
# ...and fields that vary per transaction.
|
59
|
-
# Helper method exist such as `row`, `ask_account` (see `TotalRecall::
|
60
|
-
:date: !!proc
|
61
|
-
|
62
|
-
|
63
|
-
:
|
64
|
-
|
61
|
+
# Helper method exist such as `row`, `ask_account` (see `TotalRecall::SessionHelper` for more).
|
62
|
+
:date: !!proc row[:date]
|
63
|
+
:description: !!proc row[:description]
|
64
|
+
:amount: !!proc row[:amount]
|
65
|
+
:currency: !!proc |
|
66
|
+
case row[:description]
|
67
|
+
when /some regex to detect non-dollar/ then '€'
|
68
|
+
else
|
69
|
+
default
|
70
|
+
end
|
65
71
|
:to: !!proc |
|
66
72
|
# show the row and let the user decide
|
67
|
-
render_row(columns: [
|
73
|
+
render_row(columns: [:date, :description, :amount])
|
68
74
|
ask_account("To what account did the money go?")
|
69
75
|
:generated_at: !!proc Time.now
|
70
76
|
|
data/lib/total_recall/version.rb
CHANGED
data/lib/total_recall.rb
CHANGED
@@ -4,26 +4,24 @@ require "mustache"
|
|
4
4
|
require 'csv'
|
5
5
|
|
6
6
|
module TotalRecall
|
7
|
-
|
7
|
+
module DefaultHelper
|
8
8
|
require 'highline/import'
|
9
9
|
require "terminal-table"
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
def transaction
|
12
|
+
self
|
13
|
+
end
|
13
14
|
|
14
|
-
def
|
15
|
-
@config
|
15
|
+
def config
|
16
|
+
@config
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
@row
|
20
|
-
instance_eval(&block)
|
21
|
-
ensure
|
22
|
-
@row = nil
|
19
|
+
def row
|
20
|
+
@row
|
23
21
|
end
|
24
22
|
|
25
|
-
def
|
26
|
-
@
|
23
|
+
def default
|
24
|
+
@default
|
27
25
|
end
|
28
26
|
|
29
27
|
def ask(question, &block)
|
@@ -53,16 +51,61 @@ module TotalRecall
|
|
53
51
|
|
54
52
|
def render_row(options = {})
|
55
53
|
options = { columns: [] }.merge(options)
|
56
|
-
_row = options[:columns].map{|i| row[i] }
|
54
|
+
_row = options[:columns].map {|i| row[i] }
|
57
55
|
$stderr.puts Terminal::Table.new(rows: [ _row ])
|
58
56
|
end
|
57
|
+
|
58
|
+
def extract_transaction(row)
|
59
|
+
@row = row
|
60
|
+
transactions_config.each do |k,v|
|
61
|
+
next if k[/^__/]
|
62
|
+
self[k] = value_for(k, v)
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
def value_for(key, v)
|
69
|
+
if v.respond_to?(:call)
|
70
|
+
@default = self[key.to_sym]
|
71
|
+
instance_eval(&v)
|
72
|
+
else
|
73
|
+
v
|
74
|
+
end
|
75
|
+
ensure
|
76
|
+
@default = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def transactions_config
|
80
|
+
config[:context][:transactions]
|
81
|
+
end
|
82
|
+
|
83
|
+
def highline
|
84
|
+
@highline ||= HighLine.new($stdin, $stderr)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Include your module into {SessionHelper} if you want to
|
89
|
+
# add helpers or redefine existing ones.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# module MyExtension
|
93
|
+
# def guess_account(question, options = {})
|
94
|
+
# guess = AccountGuesser.new.guess
|
95
|
+
# ask_account("What acount provided this?", default: guess)
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# TotalRecall::SessionHelper.include MyExtension
|
100
|
+
module SessionHelper
|
101
|
+
include DefaultHelper
|
59
102
|
end
|
60
103
|
|
61
104
|
class Config
|
62
|
-
YAML::add_builtin_type('proc') {|_, val| eval("proc{ #{val} }") }
|
105
|
+
YAML::add_builtin_type('proc') {|_, val| eval("proc { #{val} }") }
|
63
106
|
|
64
107
|
def initialize(options = {})
|
65
|
-
options = {file: 'total_recall.yml'}.merge(options)
|
108
|
+
options = { file: 'total_recall.yml' }.merge(options)
|
66
109
|
@config_file = File.expand_path(options[:file])
|
67
110
|
end
|
68
111
|
|
@@ -93,30 +136,40 @@ module TotalRecall
|
|
93
136
|
end
|
94
137
|
end
|
95
138
|
|
139
|
+
def context
|
140
|
+
@context ||= config[:context].merge(transactions: transactions)
|
141
|
+
end
|
142
|
+
|
96
143
|
def session
|
97
|
-
@session ||=
|
144
|
+
@session ||= session_class.new(transactions_config_defaults, :config => config)
|
98
145
|
end
|
99
146
|
|
100
|
-
def
|
101
|
-
@
|
147
|
+
def transaction_attributes
|
148
|
+
@transaction_attributes ||= begin
|
149
|
+
transactions_config.dup.delete_if{|k,_| k[/__/]}.keys |
|
150
|
+
transactions_config_defaults.keys
|
151
|
+
end
|
102
152
|
end
|
103
153
|
|
104
|
-
def
|
105
|
-
@
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
154
|
+
def session_class
|
155
|
+
@session_class ||= begin
|
156
|
+
Class.new(Struct.new(*transaction_attributes)) do
|
157
|
+
include SessionHelper
|
158
|
+
|
159
|
+
def initialize(values = {}, options = {})
|
160
|
+
@config = options[:config]
|
161
|
+
values.each do |k,v|
|
162
|
+
self[k] = value_for(k, v)
|
163
|
+
end
|
110
164
|
end
|
111
165
|
end
|
112
166
|
end
|
113
167
|
end
|
114
168
|
|
115
|
-
def
|
116
|
-
@
|
117
|
-
|
118
|
-
|
119
|
-
result[k] = v.respond_to?(:call) ? session.with_row(nil, &v) : v
|
169
|
+
def transactions
|
170
|
+
@transactions ||= begin
|
171
|
+
csv.each_with_object([]) do |row, transactions|
|
172
|
+
transactions << Hash[session.extract_transaction(row).each_pair.to_a]
|
120
173
|
end
|
121
174
|
end
|
122
175
|
end
|
@@ -125,6 +178,10 @@ module TotalRecall
|
|
125
178
|
config[:context][:transactions]
|
126
179
|
end
|
127
180
|
|
181
|
+
def transactions_config_defaults
|
182
|
+
transactions_config[:__defaults__] || {}
|
183
|
+
end
|
184
|
+
|
128
185
|
def ledger
|
129
186
|
Mustache.render(template, context)
|
130
187
|
end
|
@@ -136,10 +193,14 @@ module TotalRecall
|
|
136
193
|
include Thor::Actions
|
137
194
|
source_root File.expand_path('../total_recall/templates', __FILE__)
|
138
195
|
|
139
|
-
desc "ledger", "Convert
|
196
|
+
desc "ledger", "Convert CONFIG to a ledger"
|
140
197
|
method_option :config, :aliases => "-c", :desc => "Config file", :required => true
|
198
|
+
method_option :require, :aliases => "-r", :desc => "File to load"
|
141
199
|
def ledger
|
142
|
-
|
200
|
+
load(options[:require]) if options[:require]
|
201
|
+
|
202
|
+
config_path = File.expand_path(options[:config])
|
203
|
+
puts TotalRecall::Config.new(file: config_path).ledger
|
143
204
|
end
|
144
205
|
|
145
206
|
desc "sample", "Generate an annotated config"
|
@@ -158,5 +219,10 @@ module TotalRecall
|
|
158
219
|
@name = name
|
159
220
|
template("simple.yml.tt", destination)
|
160
221
|
end
|
222
|
+
|
223
|
+
desc "version", "Show total_recall version"
|
224
|
+
def version
|
225
|
+
puts TotalRecall::VERSION
|
226
|
+
end
|
161
227
|
end
|
162
228
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -173,4 +173,57 @@ describe TotalRecall::Config do
|
|
173
173
|
expect(instance.context).to match(transactions: [{from: 'From'}], a: 1)
|
174
174
|
end
|
175
175
|
end
|
176
|
+
|
177
|
+
describe 'helper methods' do
|
178
|
+
describe '#transaction' do
|
179
|
+
it 'gives access to the existing attributes' do
|
180
|
+
instance = instance_with_config(<<-CONFIG)
|
181
|
+
:csv:
|
182
|
+
:raw: some csv
|
183
|
+
:context:
|
184
|
+
:transactions:
|
185
|
+
:a: attribute a
|
186
|
+
:b: !!proc |
|
187
|
+
%|not %s| % transaction.a
|
188
|
+
CONFIG
|
189
|
+
|
190
|
+
expect(instance.transactions.first).to match({a: 'attribute a', b: 'not attribute a'})
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#config' do
|
195
|
+
it 'gives access to the full config' do
|
196
|
+
instance = instance_with_config(<<-CONFIG)
|
197
|
+
:csv:
|
198
|
+
:raw: some csv
|
199
|
+
:context:
|
200
|
+
:transactions:
|
201
|
+
:a: !!proc |
|
202
|
+
config[:a]
|
203
|
+
:a: 1
|
204
|
+
CONFIG
|
205
|
+
|
206
|
+
expect(instance.transactions.first).to match({a: 1})
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#default' do
|
211
|
+
it 'gives access to the default' do
|
212
|
+
instance = instance_with_config(<<-CONFIG)
|
213
|
+
:csv:
|
214
|
+
:raw: some csv
|
215
|
+
:context:
|
216
|
+
:transactions:
|
217
|
+
:__defaults__:
|
218
|
+
:a: !!proc 2
|
219
|
+
:b?: true
|
220
|
+
:a: !!proc |
|
221
|
+
default.succ
|
222
|
+
:b?: true
|
223
|
+
CONFIG
|
224
|
+
|
225
|
+
expect(instance.transactions.first).to match({a: 3, b?: true})
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
176
229
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: total_recall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gert Goet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -133,6 +133,7 @@ extra_rdoc_files: []
|
|
133
133
|
files:
|
134
134
|
- ".gitignore"
|
135
135
|
- ".travis.yml"
|
136
|
+
- CHANGELOG.md
|
136
137
|
- Gemfile
|
137
138
|
- README.md
|
138
139
|
- Rakefile
|