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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 162939627043b1aea142c3c8a6dedc9c0557801a
4
- data.tar.gz: fd307f5b53406336a46225d36b9138d11d228bfd
3
+ metadata.gz: 4f485de3326ba5aa633added9df0493ab450c74d
4
+ data.tar.gz: 592cba4e119cf53c307c9734b6d518bb96b90ac5
5
5
  SHA512:
6
- metadata.gz: a78dcb6fa9e0d5d533dcc8bef4d34769ced4f8809c8f074158c4a3c3de2cd45a53cacd2364d0e0b941330cc7fccf35c8f443106eada6f6dfa7c2b48cc555db3f
7
- data.tar.gz: 1ac9db0b80f7c7e08be72ccadb399d1eb209b56bbfa40bafcc0fa3c7037c691e3e329c6a9e4147bcda57b6893058a8b2f8a1b10459ebb31ca7fb428ec701a80f
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 to CWD.
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
- # total_recall help [COMMAND] # Describe available commands or one specific command
62
- # total_recall init NAME # Generate a minimal config NAME.yml
63
- # total_recall ledger -c, --config=CONFIG # Convert the config to a ledger
64
- # total_recall sample # Generate an annotated config
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
- "03.11.2013";"Foo";"04.11.2013";"1.638,00";""
40
- "03.11.2013";"Bar";"04.11.2013";"-492,93";""
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: false
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::Helper` for more).
60
- :date: !!proc |
61
- # For weird date formats consider using Date#strptime (http://www.ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-c-strptime)
62
- Date.parse(row[0])
63
- :description: !!proc row[1]
64
- :amount: !!proc row[3]
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: [0, 1, 3])
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
 
@@ -1,3 +1,3 @@
1
1
  module TotalRecall
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/total_recall.rb CHANGED
@@ -4,26 +4,24 @@ require "mustache"
4
4
  require 'csv'
5
5
 
6
6
  module TotalRecall
7
- class Helper
7
+ module DefaultHelper
8
8
  require 'highline/import'
9
9
  require "terminal-table"
10
10
 
11
- attr_reader :config
12
- attr_accessor :row
11
+ def transaction
12
+ self
13
+ end
13
14
 
14
- def initialize(config = {})
15
- @config = config
15
+ def config
16
+ @config
16
17
  end
17
18
 
18
- def with_row(row, &block)
19
- @row = row
20
- instance_eval(&block)
21
- ensure
22
- @row = nil
19
+ def row
20
+ @row
23
21
  end
24
22
 
25
- def highline
26
- @highline ||= HighLine.new($stdin, $stderr)
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 ||= Helper.new(config)
144
+ @session ||= session_class.new(transactions_config_defaults, :config => config)
98
145
  end
99
146
 
100
- def context
101
- @context ||= config[:context].merge(transactions: transactions)
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 transactions
105
- @transactions ||= begin
106
- csv.each_with_object([]) do |row, result|
107
- result << transaction_defaults.merge(transactions_config).each_with_object({}) do |(k,v), cfg|
108
- next if k[/^__/]
109
- cfg[k] = v.respond_to?(:call) ? session.with_row(row, &v) : v
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 transaction_defaults
116
- @transaction_defaults ||= begin
117
- defaults = transactions_config[:__defaults__] || {}
118
- defaults.each_with_object({}) do |(k,v), result|
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 the config to a ledger"
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
- puts TotalRecall::Config.new(file: File.expand_path(options[:config])).ledger
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
@@ -1,7 +1,4 @@
1
- require "rubygems"
2
- require "bundler"
3
- Bundler.setup
1
+ require "bundler/setup"
2
+ Bundler.require :default, :test
4
3
 
5
- $:.unshift File.expand_path("../../lib", __FILE__)
6
- require "total_recall"
7
4
  require 'fakefs/spec_helpers'
@@ -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.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-04 00:00:00.000000000 Z
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