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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd1b9f7d89658b02753509fe2c2b768627ad4fa8
4
- data.tar.gz: 5978628789242f582e8830f30f24fcca9e59d3e2
3
+ metadata.gz: 5a5adf1070c9805e0c5f9b7795a5053e68605190
4
+ data.tar.gz: 17af8d0b4619d9c529d8417d063b7a9e791a5bd3
5
5
  SHA512:
6
- metadata.gz: e255ab170b8b1ef33fd2ab0639befe62455ab602651b7838b9eb00fb8477343e2b04c49c677ba8693805eee80955df478f537a50b52b2abdde6b808268d8a771
7
- data.tar.gz: 5d75bc6a48e72a46b1794034357c35e9bf01cab8f9aa5851b901f709d90c306f34938a86b1961ea86d92371e667202d4a06c34fe384023a3a48dfca68022dd7b
6
+ metadata.gz: e381ee9ba9d38eb313165aabc7bbbf2cedf3413b5b9af52f85be3634ffa4079d7e3f7e96d02ae7373b4db7fbb049c892760052b93cb34bfd183f92509b03d8f8
7
+ data.tar.gz: d7764d3aa17416fdd1400067457c32fe2e0992e07c64e34694f75bf64791849494558f9bc09dd502b5384d63d71ffbff2185fb99310eb51bb5f88cb8db93b149
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ CODE_OF_CONDUCT.md
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /Gemfile.lock
data/bin/ldgr CHANGED
@@ -2,4 +2,5 @@
2
2
 
3
3
  require 'ldgr'
4
4
 
5
- Ldgr::Parser.parse
5
+ parser = Ldgr::Parser.new
6
+ parser.parse
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
- def self.parse
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
- binding.irb
45
- send(command, config) if COMMANDS.include? command
106
+ send(command) if commands.include? command
46
107
  end
47
108
 
48
- def self.add(config)
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) { Date.today } )
53
- effective = String(config.fetch(:effective) { Date.today })
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) { defaults.fetch('currency') { '$' } }
59
- t.equity = config.fetch(:equity) { defaults.fetch('equity') { 'Cash' } }
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(FILE, 'a') { |file| file.puts transaction }
132
+ File.open(transactions_file, 'a') { |file| file.puts transaction }
65
133
  end
66
134
 
67
- def self.clear(config)
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(FILE, 'r') do |transactions|
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(FILE, output)
169
+ IO.write(transactions_file, output)
94
170
  end
95
171
 
96
- def self.tag(config)
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(FILE, 'r') do |transactions|
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(FILE, output)
198
+ IO.write(transactions_file, output)
116
199
  end
117
200
 
118
- def self.sort(config)
119
- text = File.read(FILE).gsub(/\n+|\r+/, "\n").squeeze("\n").strip
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(FILE, 'w') do |file|
218
+ File.open(transactions_file, 'w') do |file|
129
219
  file.puts results.sort
130
220
  end
131
221
  end
132
222
 
133
- def self.open(_)
134
- def self.open_file(file_to_open)
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
- def self.defaults(config_file=CONFIG_FILE)
144
- LDGR_DEFAULTS.merge(YAML.load_file(config_file).to_h)
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
- SETUP_FILES.each do |file|
266
+ setup_files.each do |file|
151
267
  FileUtils.touch("#{FILEBASE}#{file}")
152
268
  end
153
269
  end
154
270
  end
155
271
 
156
- def self.config_exist?(setup_files=SETUP_FILES)
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
@@ -1,3 +1,3 @@
1
1
  module Ldgr
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
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.8
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-07 00:00:00.000000000 Z
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/