ldgr 0.1.8 → 0.1.9
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/.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/
|