standup_md 0.1.3 → 0.3.2

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +7 -1
  4. data/README.md +182 -98
  5. data/Rakefile +11 -28
  6. data/bin/standup +1 -1
  7. data/doc/README_md.html +183 -78
  8. data/doc/StandupMD.html +83 -1248
  9. data/doc/StandupMD/Cli.html +124 -474
  10. data/doc/StandupMD/Cli/Helpers.html +167 -0
  11. data/doc/StandupMD/Config.html +230 -0
  12. data/doc/StandupMD/Config/Cli.html +355 -0
  13. data/doc/StandupMD/Config/Entry.html +284 -0
  14. data/doc/StandupMD/Config/EntryList.html +197 -0
  15. data/doc/StandupMD/Config/File.html +609 -0
  16. data/doc/StandupMD/Entry.html +478 -0
  17. data/doc/StandupMD/EntryList.html +759 -0
  18. data/doc/StandupMD/File.html +614 -0
  19. data/doc/created.rid +15 -8
  20. data/doc/index.html +189 -79
  21. data/doc/js/navigation.js.gz +0 -0
  22. data/doc/js/search_index.js +1 -1
  23. data/doc/js/search_index.js.gz +0 -0
  24. data/doc/js/searcher.js.gz +0 -0
  25. data/doc/table_of_contents.html +153 -263
  26. data/lib/standup_md.rb +29 -508
  27. data/lib/standup_md/cli.rb +63 -242
  28. data/lib/standup_md/cli/helpers.rb +165 -0
  29. data/lib/standup_md/config.rb +45 -0
  30. data/lib/standup_md/config/cli.rb +106 -0
  31. data/lib/standup_md/config/entry.rb +61 -0
  32. data/lib/standup_md/config/entry_list.rb +26 -0
  33. data/lib/standup_md/config/file.rb +199 -0
  34. data/lib/standup_md/entry.rb +121 -0
  35. data/lib/standup_md/entry_list.rb +166 -0
  36. data/lib/standup_md/file.rb +183 -0
  37. data/lib/standup_md/file/helpers.rb +62 -0
  38. data/lib/standup_md/version.rb +5 -5
  39. data/standup_md.gemspec +1 -0
  40. metadata +35 -5
  41. data/doc/TestCli.html +0 -792
  42. data/doc/TestHelper.html +0 -282
  43. data/doc/TestStandupMD.html +0 -1354
@@ -1,537 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
- require 'fileutils'
5
3
  require_relative 'standup_md/version'
4
+ require_relative 'standup_md/file'
5
+ require_relative 'standup_md/entry'
6
+ require_relative 'standup_md/entry_list'
7
+ require_relative 'standup_md/cli'
8
+ require_relative 'standup_md/config'
6
9
 
7
10
  ##
8
11
  # The class for handing reading/writing of entries.
9
- #
10
- # @example
11
- # su = StandupMD.new
12
- class StandupMD
12
+ module StandupMD
13
+ @config_file_loaded = false
13
14
 
14
15
  ##
15
- # Convenience method for calling +new+ + +load+
16
+ # Method for accessing the configuration.
16
17
  #
17
- # @param [Hash] attributes Attributes to set before loading.
18
- #
19
- # @example
20
- # su = StandupMD.load(bullet_character: '*')
21
- def self.load(attributes = {})
22
- self.new do |s|
23
- attributes.each do |k, v|
24
- next unless s.respond_to?(k)
25
- s.send("#{k}=", v)
26
- end
27
- end.load
18
+ # @return [StanupMD::Cli]
19
+ def self.config
20
+ @config ||= StandupMD::Config.new
28
21
  end
29
22
 
30
- # :section: Attributes that aren't settable by user, but are gettable.
31
-
32
- ##
33
- # The string that will be used for the entry headers.
34
- #
35
- # @return [String]
36
- attr_reader :header
37
-
38
- ##
39
- # The file name should equal file_name_format parsed by Date.strftime.
40
- # The default is +Date.today.strftime('%Y_%m.md')+
41
- #
42
- # @return [String]
43
- #
44
- # @example
45
- # su = StandupMD.new { |s| s.file_name_format = '%y_%m.markdown' }
46
- # su.file
47
- # # => Users/johnsmith/.cache/standup_md/20_04.markdown
48
- attr_reader :file
49
-
50
- ##
51
- # The file that contains previous entries. When last month's file exists, but
52
- # this month's doesn't or is empty, previous_file should equal last month's
53
- # file.
54
- #
55
- # @return [String]
56
- #
57
- # @example
58
- # # Assuming the current month is April, 2020
59
- #
60
- # Dir.entries(su.directory)
61
- # # => []
62
- # su = StandupMD.new
63
- # su.previous_file
64
- # # => ''
65
- #
66
- # Dir.entries(su.directory)
67
- # # => ['2020_03.md']
68
- # su = StandupMD.new
69
- # su.previous_file
70
- # # => '2020_03.md'
71
- #
72
- # Dir.entries(su.directory)
73
- # # => ['2020_03.md', '2020_04.md']
74
- # su = StandupMD.new
75
- # su.previous_file
76
- # # => '2020_04.md'
77
- attr_reader :previous_file
78
-
79
- ##
80
- # The entry for today's date as a hash. If +file+ already has an entry for
81
- # today, it will be read and used as +current_entry+. If there is no entry
82
- # for today, one should be generated from scaffolding.
83
- #
84
- # @return [Hash]
85
- #
86
- # @example
87
- # StandupMD.new.current_entry
88
- # # => {
89
- # # '2020-04-02' => {
90
- # # 'Previous' => ['Task from yesterday'],
91
- # # 'Current' => ["<!-- ADD TODAY'S WORK HERE -->"],
92
- # # 'Impediments' => ['None'],
93
- # # 'Notes' => [],
94
- # # }
95
- # # }
96
- attr_reader :current_entry
97
-
98
- ##
99
- # All previous entry for the same month as today. If it's the first day of
100
- # the month, +all_previous_entries+ will be all of last month's entries. They
101
- # will be a hash in the same format as +current_entry+.
102
- #
103
- # @return [Hash]
104
- attr_reader :all_previous_entries
105
-
106
- ##
107
- # Current entry plus all previous entries. This will be a hash in the same
108
- # format at +current_entry+ and +all_previous_entries+.
109
- #
110
- # @return [Hash]
111
- attr_reader :all_entries
112
-
113
- # :section: Attributes that are settable by the user, but have custom setters.
114
-
115
- ##
116
- # The directory where the markdown files are kept.
117
- #
118
- # @return [String]
119
- #
120
- # @default
121
- # File.join(ENV['HOME'], '.cache', 'standup_md')
122
- attr_reader :directory
123
-
124
- ##
125
- # Array of tasks for today. This is the work expected to be performed today.
126
- # Default is an empty array, but when writing to file, the default is
127
- #
128
- # @return [Array]
129
- #
130
- # @default
131
- # ["<!-- ADD TODAY'S WORK HERE -->"]
132
- attr_reader :current_entry_tasks
133
-
134
- ##
135
- # Array of impediments for today's entry.
136
- #
137
- # @return [Array]
138
- attr_reader :impediments
139
-
140
23
  ##
141
- # Character used as bullets for list entries.
142
- #
143
- # @return [String] either - (dash) or * (asterisk)
144
- attr_reader :bullet_character
145
-
146
- ##
147
- # Number of octothorps that should preface entry headers.
148
- #
149
- # @return [Integer] between 1 and 5
150
- attr_reader :header_depth
151
-
152
- ##
153
- # Number of octothorps that should preface sub-headers.
154
- #
155
- # @return [Integer] between 2 and 6
156
- attr_reader :sub_header_depth
157
-
158
- ##
159
- # The tasks from the previous task's "Current" section.
160
- #
161
- # @return [Array]
162
- attr_reader :previous_entry_tasks
163
-
164
- ##
165
- # Array of notes to add to today's entry.
166
- #
167
- # @return [Array]
168
- attr_reader :notes
169
-
170
- # :section: Attributes with default getters and setters.
171
-
172
- ##
173
- # The format to use for file names. This should include a month (%m) and
174
- # year (%y) so the file can rotate every month. This will prevent files
175
- # from getting too large.
176
- #
177
- # @param [String] file_name_format Parsed by +strftime+
178
- #
179
- # @return [String]
180
- attr_accessor :file_name_format
181
-
182
- ##
183
- # The date format to use for entry headers.
184
- #
185
- # @param [String] header_date_format Parsed by +strftime+
186
- #
187
- # @return [String]
188
- attr_accessor :header_date_format
189
-
190
- ##
191
- # The header to use for the +Current+ section.
192
- #
193
- # @param [String] current_header
194
- #
195
- # @return [String]
196
- attr_accessor :current_header
197
-
198
- ##
199
- # The header to use for the +Previous+ section.
200
- #
201
- # @param [String] previous_header
202
- #
203
- # @return [String]
204
- attr_accessor :previous_header
205
-
206
- ##
207
- # The header to use for the +Impediments+ section.
208
- #
209
- # @param [String] impediments_header
210
- #
211
- # @return [String]
212
- attr_accessor :impediments_header
213
-
214
- ##
215
- # The header to use for the +Notes+ section.
216
- #
217
- # @param [String] notes_header
218
- #
219
- # @return [String]
220
- attr_accessor :notes_header
221
-
222
- ##
223
- # Constructor. Yields the instance so you can pass a block to access setters.
224
- #
225
- # @return [self]
24
+ # Reset all configuration values to their defaults.
226
25
  #
227
- # @example
228
- # su = StandupMD.new do |s|
229
- # s.directory = @workdir
230
- # s.file_name_format = '%y_%m.markdown'
231
- # s.bullet_character = '*'
232
- # end
233
- def initialize
234
- @notes = []
235
- @header_depth = 1
236
- @sub_header_depth = 2
237
- @bullet_character = '-'
238
- @current_entry_tasks = ["<!-- ADD TODAY'S WORK HERE -->"]
239
- @impediments = ['None']
240
- @file_name_format = '%Y_%m.md'
241
- @directory = File.join(ENV['HOME'], '.cache', 'standup_md')
242
- @header_date_format = '%Y-%m-%d'
243
- @current_header = 'Current'
244
- @previous_header = 'Previous'
245
- @impediments_header = 'Impediments'
246
- @notes_header = 'Notes'
247
- @sub_header_order = %w[previous current impediments notes]
248
-
249
- yield self if block_given?
26
+ # @return [StandupMD::Config]
27
+ def self.reset_config
28
+ @config = StandupMD::Config.new
250
29
  end
251
30
 
252
- # :section: Booleans
253
- # Helper methods for booleans.
254
-
255
31
  ##
256
- # Has the file been written since instantiated?
257
- #
258
- # @return [boolean]
32
+ # Allows for configuration via a block. Useful when making config files.
259
33
  #
260
34
  # @example
261
- # su = StandupMD.new
262
- # su.file_written?
263
- # # => false
264
- # su.write
265
- # su.file_written?
266
- # # => true
267
- def file_written?
268
- @file_written
269
- end
270
-
271
- ##
272
- # Was today's entry already in the file?
273
- #
274
- # @return [boolean] true if today's entry was already in the file
275
- def entry_previously_added?
276
- @entry_previously_added
277
- end
278
-
279
- # :section: Custom setters
280
- # Setters that required validations.
281
-
282
- ##
283
- # Setter for current entry tasks.
284
- #
285
- # @param [Array] tasks
286
- #
287
- # @return [Array]
288
- def previous_entry_tasks=(tasks)
289
- raise 'Must be an Array' unless tasks.is_a?(Array)
290
- @previous_entry_tasks = tasks
291
- end
292
-
293
- ##
294
- # Setter for notes.
295
- #
296
- # @param [Array] notes
297
- #
298
- # @return [Array]
299
- def notes=(tasks)
300
- raise 'Must be an Array' unless tasks.is_a?(Array)
301
- @notes = tasks
302
- end
303
-
304
- ##
305
- # Setter for current entry tasks.
306
- #
307
- # @param [Array] tasks
308
- #
309
- # @return [Array]
310
- def current_entry_tasks=(tasks)
311
- raise 'Must be an Array' unless tasks.is_a?(Array)
312
- @current_entry_tasks = tasks
35
+ # StandupMD.configure { |s| s.cli.editor = 'mate' }
36
+ def self.configure
37
+ yield self.config
313
38
  end
314
39
 
315
40
  ##
316
- # Setter for impediments.
317
- #
318
- # @param [Array] tasks
319
- #
320
- # @return [Array]
321
- def impediments=(tasks)
322
- raise 'Must be an Array' unless tasks.is_a?(Array)
323
- @impediments = tasks
324
- end
325
-
326
- ##
327
- # Setter for bullet_character. Must be * (asterisk) or - (dash).
328
- #
329
- # @param [String] character
330
- #
331
- # @return [String]
332
- def bullet_character=(character)
333
- raise 'Must be "-" or "*"' unless %w[- *].include?(character)
334
- @bullet_character = character
335
- end
336
-
337
- ##
338
- # Setter for directory. Must be expanded in case the user uses `~` for home.
339
- # If the directory doesn't exist, it will be created. To reset instance
340
- # variables after changing the directory, you'll need to call load.
341
- #
342
- # @param [String] directory
343
- #
344
- # @return [String]
345
- def directory=(directory)
346
- # TODO test this
347
- directory = File.expand_path(directory)
348
- FileUtils.mkdir_p(directory) unless File.directory?(directory)
349
- @directory = directory
350
- end
351
-
352
- ##
353
- # Number of octothorps (#) to use before the main header.
354
- #
355
- # @param [Integer] depth
356
- #
357
- # @return [Integer]
358
- def header_depth=(depth)
359
- if !depth.between?(1, 5)
360
- raise 'Header depth out of bounds (1..5)'
361
- elsif depth >= sub_header_depth
362
- raise 'header_depth must be larger than sub_header_depth'
363
- end
364
- @header_depth = depth
365
- end
366
-
367
- ##
368
- # Number of octothorps (#) to use before sub headers (Current, Previous, etc).
369
- #
370
- # @param [Integer] depth
371
- #
372
- # @return [Integer]
373
- def sub_header_depth=(depth)
374
- if !depth.between?(2, 6)
375
- raise 'Sub-header depth out of bounds (2..6)'
376
- elsif depth <= header_depth
377
- raise 'sub_header_depth must be smaller than header_depth'
378
- end
379
- @sub_header_depth = depth
380
- end
381
-
382
- ##
383
- # Preferred order for sub-headers.
384
- #
385
- # @param [Array] Values must be %w[previous current impediment notes]
386
- #
387
- # @return [Array]
388
- def sub_header_order=(array)
389
- order = %w[previous current impediments notes]
390
- raise "Values must be #{order.join{', '}}" unless order.sort == array.sort
391
- @sub_header_order = array
392
- end
393
-
394
- # :section: Misc
395
- # Misc.
396
-
397
- ##
398
- # Return a copy of the sub-header order so the user can't modify the array.
399
- #
400
- # @return [Array]
401
- def sub_header_order
402
- @sub_header_order.dup
403
- end
404
-
405
- ##
406
- # Writes a new entry to the file if the first entry in the file isn't today.
41
+ # Has a config file been loaded?
407
42
  #
408
43
  # @return [Boolean]
409
- def write
410
- File.open(file, 'w') do |f|
411
- all_entries.each do |head, s_heads|
412
- f.puts '#' * header_depth + ' ' + head
413
- sub_header_order.map { |value| "#{value}_header" }.each do |sub_head|
414
- sh = send(sub_head).capitalize
415
- next if !s_heads[sh] || s_heads[sh].empty?
416
- f.puts '#' * sub_header_depth + ' ' + sh
417
- s_heads[sh].each { |task| f.puts bullet_character + ' ' + task }
418
- end
419
- f.puts
420
- break if new_month?
421
- end
422
- end
423
- @file_written = true
44
+ def self.config_file_loaded?
45
+ @config_file_loaded
424
46
  end
425
47
 
426
48
  ##
427
- # Sets internal instance variables. Called when first instantiated, or after
428
- # directory is set.
49
+ # Loads a config file.
429
50
  #
430
- # @return [self]
431
- def load
432
- FileUtils.mkdir_p(directory) unless File.directory?(directory)
433
-
434
- @today = Date.today
435
- @header = today.strftime(header_date_format)
436
- @file_written = false
437
- @file = File.expand_path(File.join(directory, today.strftime(file_name_format)))
438
- @previous_file = get_previous_file
439
- @all_previous_entries = get_all_previous_entries
440
- @entry_previously_added = all_previous_entries.key?(header)
441
- @previous_entry_tasks = previous_entry[current_header]
442
- @current_entry = @all_previous_entries.delete(header) || new_entry
443
- @all_entries = {header => current_entry}.merge(all_previous_entries)
444
-
445
- FileUtils.touch(file) unless File.file?(file)
446
- self
447
- end
448
-
449
- ##
450
- # Alias of +load+
451
- #
452
- # @return [self]
453
- alias_method :reload, :load
454
-
455
- ##
456
- # Is today a different month than the previous entry?
457
- def new_month?
458
- file != previous_file
459
- end
460
-
461
- private
462
-
463
- ##
464
- # Scaffolding with which new entries will be created.
465
- def new_entry # :nodoc:
466
- {
467
- previous_header => previous_entry_tasks || [],
468
- current_header => current_entry_tasks,
469
- impediments_header => impediments,
470
- notes_header => notes,
471
- }
472
- end
473
-
474
- ##
475
- # Date object of today's date.
476
- def today # :nodoc:
477
- @today
478
- end
479
-
480
- ##
481
- # The file that contains the previous entry. If previous entry was same month,
482
- # previous_file will be the same as file. If previous entry was last month,
483
- # and a file exists for last month, previous_file is last month's file.
484
- # If neither is true, returns an empty string.
485
- def get_previous_file # :nodoc:
486
- return file if File.file?(file) && !File.zero?(file)
487
- prev_month_file = File.expand_path(File.join(
488
- directory,
489
- today.prev_month.strftime(file_name_format)
490
- ))
491
- File.file?(prev_month_file) ? prev_month_file : ''
492
- end
493
-
494
- def get_all_previous_entries # :nodoc:
495
- return {} unless File.file?(previous_file)
496
- prev_entries = {}
497
- entry_header = ''
498
- section_type = ''
499
- File.foreach(previous_file) do |line|
500
- line.chomp!
501
- next if line.strip.empty?
502
- if line.match(%r{^#{'#' * header_depth}\s+})
503
- entry_header = line.sub(%r{^\#{#{header_depth}}\s*}, '')
504
- section_type = notes_header
505
- prev_entries[entry_header] ||= {}
506
- elsif line.match(%r{^#{'#' * sub_header_depth}\s+})
507
- section_type = determine_section_type(
508
- line.sub(%r{^\#{#{sub_header_depth}}\s*}, '')
509
- )
510
- prev_entries[entry_header][section_type] = []
511
- else
512
- prev_entries[entry_header][section_type] << line.sub(
513
- %r{\s*#{bullet_character}\s*}, ''
514
- )
515
- end
516
- end
517
- prev_entries
518
- rescue => e
519
- raise "File malformation: #{e}"
520
- end
521
-
522
- def determine_section_type(line) # :nodoc:
523
- [
524
- current_header,
525
- previous_header,
526
- impediments_header,
527
- notes_header
528
- ].each { |header| return header if line.include?(header) }
529
- raise "Unknown header type [#{line}]"
530
- end
531
-
532
- def previous_entry # :nodoc:
533
- all_previous_entries.each do |key, value|
534
- return value unless key == header
535
- end
51
+ # @param [String] file
52
+ def self.load_config_file(file)
53
+ file = ::File.expand_path(file)
54
+ raise "File #{file} does not exist." unless ::File.file?(file)
55
+ @config_file_loaded = true
56
+ load file
536
57
  end
537
58
  end