ldgr 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/bin/ldgr +2 -1
- data/lib/ldgr/parser.rb +149 -30
- data/lib/ldgr/version.rb +1 -1
- metadata +2 -3
- data/CODE_OF_CONDUCT.md +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a5adf1070c9805e0c5f9b7795a5053e68605190
|
4
|
+
data.tar.gz: 17af8d0b4619d9c529d8417d063b7a9e791a5bd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e381ee9ba9d38eb313165aabc7bbbf2cedf3413b5b9af52f85be3634ffa4079d7e3f7e96d02ae7373b4db7fbb049c892760052b93cb34bfd183f92509b03d8f8
|
7
|
+
data.tar.gz: d7764d3aa17416fdd1400067457c32fe2e0992e07c64e34694f75bf64791849494558f9bc09dd502b5384d63d71ffbff2185fb99310eb51bb5f88cb8db93b149
|
data/.gitignore
CHANGED
data/bin/ldgr
CHANGED
data/lib/ldgr/parser.rb
CHANGED
@@ -10,19 +10,82 @@ require 'fileutils'
|
|
10
10
|
require 'yaml'
|
11
11
|
|
12
12
|
module Ldgr
|
13
|
+
# Parses configuration options.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# Examples
|
17
|
+
#
|
18
|
+
# Ldgr::Parser.parse
|
19
|
+
# # => some file action
|
20
|
+
#
|
21
|
+
# Returns nothing on success.
|
13
22
|
class Parser
|
14
23
|
FILEBASE = Dir.home + '/.config/ledger/'
|
15
|
-
FILE = FILEBASE + 'transactions.dat'
|
16
24
|
VERSION = Ldgr::VERSION
|
17
25
|
PROGRAM_NAME = 'ldgr'
|
18
26
|
MATCH = /(?=(\n\d\d\d\d-\d\d-\d\d)(=\d\d\d\d-\d\d-\d\d)*)|\z/
|
19
27
|
OTHER_MATCH = /(?=(\d\d\d\d-\d\d-\d\d)(=\d\d\d\d-\d\d-\d\d)*)/
|
20
|
-
COMMANDS = %w(add sort tag clear open)
|
21
|
-
SETUP_FILES = %w(transactions.dat accounts.dat budgets.dat aliases.dat commodities.dat setup.dat ledger.dat ldgr.yaml)
|
22
|
-
CONFIG_FILE = Pathname(FILEBASE + 'ldgr.yaml')
|
23
|
-
LDGR_DEFAULTS = { currency: '$', equity: 'Cash' }.to_h
|
24
28
|
|
25
|
-
|
29
|
+
attr_accessor :transactions_file, :config
|
30
|
+
|
31
|
+
# Public: Creates a new Parser object
|
32
|
+
#
|
33
|
+
# config - A hash of config options
|
34
|
+
#
|
35
|
+
# Examples
|
36
|
+
#
|
37
|
+
# new(config: {currency: '¥'})
|
38
|
+
# # => <ParserObject>
|
39
|
+
#
|
40
|
+
# Returns a Parser object.
|
41
|
+
def initialize(config: {})
|
42
|
+
@transactions_file = defaults.fetch(:transactions_file)
|
43
|
+
@config = defaults.merge(user_config).merge(config)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: User-specified config options
|
47
|
+
#
|
48
|
+
# Examples
|
49
|
+
#
|
50
|
+
# user_config
|
51
|
+
# # => {all the config options from the user's YAML file}
|
52
|
+
#
|
53
|
+
# Returns a hash of user-specified config options.
|
54
|
+
def user_config
|
55
|
+
path = Pathname(FILEBASE + 'ldgr.yaml')
|
56
|
+
YAML.load_file(path).to_h
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: available commands
|
60
|
+
#
|
61
|
+
# Examples
|
62
|
+
#
|
63
|
+
# commands
|
64
|
+
#
|
65
|
+
# Returns an array of command names.
|
66
|
+
def commands
|
67
|
+
%w(add sort tag clear open)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public: expected setup files
|
71
|
+
#
|
72
|
+
# Examples
|
73
|
+
#
|
74
|
+
# setup_files
|
75
|
+
#
|
76
|
+
# Returns an array of file names.
|
77
|
+
def self.setup_files
|
78
|
+
%w(transactions.dat accounts.dat budgets.dat aliases.dat commodities.dat setup.dat ledger.dat ldgr.yaml)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Kicks off the CLI
|
82
|
+
#
|
83
|
+
# Examples
|
84
|
+
#
|
85
|
+
# parse
|
86
|
+
#
|
87
|
+
# Returns nothing.
|
88
|
+
def parse
|
26
89
|
cli = OptionParser.new do |o|
|
27
90
|
o.banner = "Usage #{PROGRAM_NAME} [add|sort|tag|clear|open]"
|
28
91
|
o.program_name = PROGRAM_NAME
|
@@ -39,37 +102,50 @@ module Ldgr
|
|
39
102
|
o.define '-p', '--payee=PAYEE', String, 'the payee of the transaction'
|
40
103
|
end
|
41
104
|
|
42
|
-
config = defaults
|
43
105
|
command = String(cli.parse(ARGV, into: config)[0])
|
44
|
-
|
45
|
-
send(command, config) if COMMANDS.include? command
|
106
|
+
send(command) if commands.include? command
|
46
107
|
end
|
47
108
|
|
48
|
-
|
109
|
+
# Public: Adds a transaction to the transactions_file.
|
110
|
+
#
|
111
|
+
# Examples
|
112
|
+
#
|
113
|
+
# add
|
114
|
+
#
|
115
|
+
# Returns nothing.
|
116
|
+
def add
|
49
117
|
error_policy = ->(key) { fail "You need to provide a value for #{key.to_s}." }
|
50
118
|
|
51
119
|
transaction = Transaction.new do |t|
|
52
|
-
date = String(config.fetch(:date) {
|
53
|
-
effective = String(config.fetch(:effective) {
|
120
|
+
date = String(config.fetch(:date) { |key| error_policy.call(key) })
|
121
|
+
effective = String(config.fetch(:effective) { |key| error_policy.call(key) })
|
54
122
|
|
55
123
|
t.payee = config.fetch(:payee) { |key| error_policy.call(key) }
|
56
124
|
t.account = config.fetch(:account) { |key| error_policy.call(key) }
|
57
125
|
t.amount = config.fetch(:amount) { |key| error_policy.call(key) }
|
58
|
-
t.currency = config.fetch(:currency) {
|
59
|
-
t.equity = config.fetch(:equity) {
|
126
|
+
t.currency = config.fetch(:currency) { config.fetch(:currency) }
|
127
|
+
t.equity = config.fetch(:equity) { config.fetch(:equity) }
|
60
128
|
t.cleared = config[:cleared] ? '* ' : ''
|
61
129
|
t.date = date == effective ? date : date << '=' << effective
|
62
130
|
end
|
63
131
|
|
64
|
-
File.open(
|
132
|
+
File.open(transactions_file, 'a') { |file| file.puts transaction }
|
65
133
|
end
|
66
134
|
|
67
|
-
|
135
|
+
# Public: Runs through all uncleared transactions that are passed
|
136
|
+
# their effective date and offers to clear them.
|
137
|
+
#
|
138
|
+
# Examples
|
139
|
+
#
|
140
|
+
# clear
|
141
|
+
#
|
142
|
+
# Returns nothing.
|
143
|
+
def clear
|
68
144
|
output = ''
|
69
145
|
pattern = /((^\d{,4}-\d{,2}-\d{,2})(=\d{,4}-\d{,2}-\d{,2})?) ([^\*]+)/
|
70
146
|
count = 0
|
71
147
|
|
72
|
-
File.open(
|
148
|
+
File.open(transactions_file, 'r') do |transactions|
|
73
149
|
transactions.each_line do |transaction|
|
74
150
|
match = pattern.match(transaction)
|
75
151
|
if match && match[3]
|
@@ -90,16 +166,23 @@ module Ldgr
|
|
90
166
|
output << transaction
|
91
167
|
end
|
92
168
|
end
|
93
|
-
IO.write(
|
169
|
+
IO.write(transactions_file, output)
|
94
170
|
end
|
95
171
|
|
96
|
-
|
172
|
+
# Public: Runs through all transactions with only Expenses set as the account and lets you enter an account name.
|
173
|
+
#
|
174
|
+
# Examples
|
175
|
+
#
|
176
|
+
# tag
|
177
|
+
#
|
178
|
+
# Returns nothing.
|
179
|
+
def tag
|
97
180
|
output = ''
|
98
181
|
pattern = /(^\s+Expenses[^:])\s*(¥.+)/
|
99
182
|
count = 0
|
100
183
|
previous = ''
|
101
184
|
|
102
|
-
File.open(
|
185
|
+
File.open(transactions_file, 'r') do |transactions|
|
103
186
|
transactions.each_line do |transaction|
|
104
187
|
match = pattern.match(transaction)
|
105
188
|
if match
|
@@ -112,11 +195,18 @@ module Ldgr
|
|
112
195
|
output << transaction
|
113
196
|
end
|
114
197
|
end
|
115
|
-
IO.write(
|
198
|
+
IO.write(transactions_file, output)
|
116
199
|
end
|
117
200
|
|
118
|
-
|
119
|
-
|
201
|
+
# Public: Sorts all transactions by date.
|
202
|
+
#
|
203
|
+
# Examples
|
204
|
+
#
|
205
|
+
# sort
|
206
|
+
#
|
207
|
+
# Returns nothing.
|
208
|
+
def sort
|
209
|
+
text = File.read(transactions_file).gsub(/\n+|\r+/, "\n").squeeze("\n").strip
|
120
210
|
scanner = StringScanner.new(text)
|
121
211
|
results = []
|
122
212
|
|
@@ -125,13 +215,21 @@ module Ldgr
|
|
125
215
|
scanner.skip_until(OTHER_MATCH)
|
126
216
|
end
|
127
217
|
|
128
|
-
File.open(
|
218
|
+
File.open(transactions_file, 'w') do |file|
|
129
219
|
file.puts results.sort
|
130
220
|
end
|
131
221
|
end
|
132
222
|
|
133
|
-
|
134
|
-
|
223
|
+
# Public: Opens a settings file from ~/.config/ledger
|
224
|
+
#
|
225
|
+
# Examples
|
226
|
+
#
|
227
|
+
# open accounts
|
228
|
+
# # => accounts file opens in $EDITOR
|
229
|
+
#
|
230
|
+
# Returns nothing.
|
231
|
+
def open
|
232
|
+
def open_file(file_to_open)
|
135
233
|
checked_file = "#{FILEBASE}#{file_to_open}.dat"
|
136
234
|
raise "#{checked_file} doesn't exist." unless Pathname(checked_file).exist?
|
137
235
|
system(ENV['EDITOR'], checked_file)
|
@@ -140,20 +238,41 @@ module Ldgr
|
|
140
238
|
open_file(ARGV[1])
|
141
239
|
end
|
142
240
|
|
143
|
-
|
144
|
-
|
241
|
+
# Public: ldgr's default configuration options
|
242
|
+
#
|
243
|
+
# Examples
|
244
|
+
#
|
245
|
+
# defaults
|
246
|
+
# # => {all the configuration options}
|
247
|
+
#
|
248
|
+
# Returns a hash of default configuration options.
|
249
|
+
def defaults
|
250
|
+
{
|
251
|
+
currency: '$',
|
252
|
+
equity: 'Cash',
|
253
|
+
effective: Date.today,
|
254
|
+
date: Date.today,
|
255
|
+
cleared: false,
|
256
|
+
transactions_file: FILEBASE + 'transactions.dat'
|
257
|
+
}
|
145
258
|
end
|
146
259
|
|
260
|
+
# Private: Prepares users' file system for ldgr.
|
261
|
+
#
|
262
|
+
# Returns nothing.
|
147
263
|
def self.setup
|
148
264
|
unless config_exist?
|
149
265
|
FileUtils.mkdir_p(FILEBASE)
|
150
|
-
|
266
|
+
setup_files.each do |file|
|
151
267
|
FileUtils.touch("#{FILEBASE}#{file}")
|
152
268
|
end
|
153
269
|
end
|
154
270
|
end
|
155
271
|
|
156
|
-
|
272
|
+
# Private: Checks if user already has setup files created.
|
273
|
+
#
|
274
|
+
# Returns nothing.
|
275
|
+
def self.config_exist?
|
157
276
|
setup_files.each do |file|
|
158
277
|
return false unless Pathname("#{FILEBASE}#{file}").exist?
|
159
278
|
end
|
data/lib/ldgr/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ldgr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Pittman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -90,7 +90,6 @@ extra_rdoc_files: []
|
|
90
90
|
files:
|
91
91
|
- ".gitignore"
|
92
92
|
- ".travis.yml"
|
93
|
-
- CODE_OF_CONDUCT.md
|
94
93
|
- Gemfile
|
95
94
|
- LICENSE.txt
|
96
95
|
- README.md
|
data/CODE_OF_CONDUCT.md
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
2
|
-
|
3
|
-
## Our Pledge
|
4
|
-
|
5
|
-
In the interest of fostering an open and welcoming environment, we as
|
6
|
-
contributors and maintainers pledge to making participation in our project and
|
7
|
-
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
-
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
-
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
-
orientation.
|
11
|
-
|
12
|
-
## Our Standards
|
13
|
-
|
14
|
-
Examples of behavior that contributes to creating a positive environment
|
15
|
-
include:
|
16
|
-
|
17
|
-
* Using welcoming and inclusive language
|
18
|
-
* Being respectful of differing viewpoints and experiences
|
19
|
-
* Gracefully accepting constructive criticism
|
20
|
-
* Focusing on what is best for the community
|
21
|
-
* Showing empathy towards other community members
|
22
|
-
|
23
|
-
Examples of unacceptable behavior by participants include:
|
24
|
-
|
25
|
-
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
-
advances
|
27
|
-
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
-
* Public or private harassment
|
29
|
-
* Publishing others' private information, such as a physical or electronic
|
30
|
-
address, without explicit permission
|
31
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
-
professional setting
|
33
|
-
|
34
|
-
## Our Responsibilities
|
35
|
-
|
36
|
-
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
-
behavior and are expected to take appropriate and fair corrective action in
|
38
|
-
response to any instances of unacceptable behavior.
|
39
|
-
|
40
|
-
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
-
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
-
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
-
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
-
threatening, offensive, or harmful.
|
45
|
-
|
46
|
-
## Scope
|
47
|
-
|
48
|
-
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
-
when an individual is representing the project or its community. Examples of
|
50
|
-
representing a project or community include using an official project e-mail
|
51
|
-
address, posting via an official social media account, or acting as an appointed
|
52
|
-
representative at an online or offline event. Representation of a project may be
|
53
|
-
further defined and clarified by project maintainers.
|
54
|
-
|
55
|
-
## Enforcement
|
56
|
-
|
57
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
-
reported by contacting the project team at brandon@brandonpittman.net. All
|
59
|
-
complaints will be reviewed and investigated and will result in a response that
|
60
|
-
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
-
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
-
Further details of specific enforcement policies may be posted separately.
|
63
|
-
|
64
|
-
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
-
faith may face temporary or permanent repercussions as determined by other
|
66
|
-
members of the project's leadership.
|
67
|
-
|
68
|
-
## Attribution
|
69
|
-
|
70
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
-
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
-
|
73
|
-
[homepage]: http://contributor-covenant.org
|
74
|
-
[version]: http://contributor-covenant.org/version/1/4/
|