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
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StandupMD
4
+
5
+ ##
6
+ # Enumerable list of entries.
7
+ class EntryList
8
+ include Enumerable
9
+
10
+ ##
11
+ # Access to the class's configuration.
12
+ #
13
+ # @return [StandupMD::Config::EntryList]
14
+ def self.config
15
+ @config ||= StandupMD.config.entry_list
16
+ end
17
+
18
+ ##
19
+ # Contruct a list. Can pass any amount of +StandupMD::Entry+ instances.
20
+ #
21
+ # @param [Entry] entries
22
+ #
23
+ # @return [StandupMD::EntryList]
24
+ def initialize(*entries)
25
+ @config = self.class.config
26
+ unless entries.all? { |e| e.is_a?(StandupMD::Entry) }
27
+ raise ArgumentError, 'Entry must instance of StandupMD::Entry'
28
+ end
29
+ @entries = entries
30
+ end
31
+
32
+ ##
33
+ # Iterate over the list and yield each entry.
34
+ def each(&block)
35
+ @entries.each(&block)
36
+ end
37
+
38
+ ##
39
+ # Appends entries to list.
40
+ #
41
+ # @param [StandupMD::Entry] entry
42
+ #
43
+ # @return [Array]
44
+ def <<(entry)
45
+ unless entry.is_a?(StandupMD::Entry)
46
+ raise ArgumentError, 'Entry must instance of StandupMD::Entry'
47
+ end
48
+ @entries << entry
49
+ end
50
+
51
+ ##
52
+ # Finds an entry based on date. This method assumes the list has already
53
+ # been sorted.
54
+ def find(key)
55
+ to_a.bsearch { |e| e.date == key }
56
+ end
57
+
58
+ ##
59
+ # How many entries are in the list.
60
+ #
61
+ # @return [Integer]
62
+ def size
63
+ @entries.size
64
+ end
65
+
66
+ ##
67
+ # Is the list empty?
68
+ #
69
+ # @return [Boolean] true if empty
70
+ def empty?
71
+ @entries.empty?
72
+ end
73
+
74
+ ##
75
+ # Returns a copy of self sorted by date.
76
+ #
77
+ # @return [StandupMD::EntryList]
78
+ def sort
79
+ self.class.new(*@entries.sort)
80
+ end
81
+
82
+ ##
83
+ # Replace entries with sorted entries by date.
84
+ #
85
+ # @return [StandupMD::EntryList]
86
+ def sort!
87
+ @entries = @entries.sort
88
+ self
89
+ end
90
+
91
+ ##
92
+ # Returns a copy of self sorted by date.
93
+ #
94
+ # @return [StandupMD::EntryList]
95
+ def sort_reverse
96
+ self.class.new(*@entries.sort.reverse)
97
+ end
98
+
99
+ ##
100
+ # Returns entries that are between the start and end date. This method
101
+ # assumes the list has already been sorted.
102
+ #
103
+ # @param [Date] start_date
104
+ #
105
+ # @param [Date] end_date
106
+ #
107
+ # @return [Array]
108
+ def filter(start_date, end_date)
109
+ self.class.new(
110
+ *@entries.select { |e| e.date.between?(start_date, end_date) }
111
+ )
112
+ end
113
+
114
+ ##
115
+ # Replaces entries with results of filter.
116
+ #
117
+ # @param [Date] start_date
118
+ #
119
+ # @param [Date] end_date
120
+ #
121
+ # @return [Array]
122
+ def filter!(start_date, end_date)
123
+ @entries = filter(start_date, end_date)
124
+ self
125
+ end
126
+
127
+ ##
128
+ # The first entry in the list. This method assumes the list has
129
+ # already been sorted.
130
+ #
131
+ # @return [StandupMD::Entry]
132
+ def first
133
+ to_a.first
134
+ end
135
+
136
+ ##
137
+ # The last entry in the list. This method assumes the list has
138
+ # already been sorted.
139
+ #
140
+ # @return [StandupMD::Entry]
141
+ def last
142
+ to_a.last
143
+ end
144
+
145
+ ##
146
+ # The list as a hash, with the dates as keys.
147
+ #
148
+ # @return [Hash]
149
+ def to_h
150
+ Hash[@entries.map { |e| [e.date, {
151
+ 'current' => e.current,
152
+ 'previous' => e.previous,
153
+ 'impediments' => e.impediments,
154
+ 'notes' => e.notes
155
+ }]}]
156
+ end
157
+
158
+ ##
159
+ # The entry list as a json object.
160
+ #
161
+ # @return [String]
162
+ def to_json
163
+ to_h.to_json
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'fileutils'
5
+ require_relative 'file/helpers'
6
+
7
+ module StandupMD
8
+
9
+ ##
10
+ # Class for handling reading and writing standup files.
11
+ class File
12
+ include StandupMD::File::Helpers
13
+
14
+ ##
15
+ # Access to the class's configuration.
16
+ #
17
+ # @return [StandupMD::Config::EntryList]
18
+ def self.config
19
+ @config ||= StandupMD.config.file
20
+ end
21
+
22
+ ##
23
+ # Convenience method for calling File.find(file_name).load
24
+ #
25
+ # @param [String] file_name
26
+ #
27
+ # @return [StandupMD::File]
28
+ def self.load(file_name)
29
+ new(file_name).load
30
+ end
31
+
32
+ ##
33
+ # Find standup file in directory by file name.
34
+ #
35
+ # @param [String] File_naem
36
+ def self.find(file_name)
37
+ file = Dir.entries(config.directory).bsearch { |f| f == file_name }
38
+ if file.nil? && !config.create
39
+ raise "File #{file_name} not found." unless config.create
40
+ end
41
+ new(file_name)
42
+ end
43
+
44
+ ##
45
+ # Find standup file in directory by Date object.
46
+ #
47
+ # @param [Date] date
48
+ def self.find_by_date(date)
49
+ unless date.is_a?(Date)
50
+ raise ArgumentError, "Argument must be a Date object"
51
+ end
52
+ find(date.strftime(config.name_format))
53
+ end
54
+
55
+ ##
56
+ # The list of entries in the file.
57
+ #
58
+ # @return [StandupMP::EntryList]
59
+ attr_reader :entries
60
+
61
+ ##
62
+ # The name of the file.
63
+ #
64
+ # @return [String]
65
+ attr_reader :name
66
+
67
+ ##
68
+ # Constructs the instance.
69
+ #
70
+ # @param [String] file_name
71
+ #
72
+ # @return [StandupMP::File]
73
+ def initialize(file_name)
74
+ @config = self.class.config
75
+ if file_name.include?(::File::SEPARATOR)
76
+ raise ArgumentError,
77
+ "#{file_name} contains directory. Please use `StandupMD.config.file.directory=`"
78
+ end
79
+
80
+ unless ::File.directory?(@config.directory)
81
+ raise "Dir #{@config.directory} not found." unless @config.create
82
+ FileUtils.mkdir_p(@config.directory)
83
+ end
84
+
85
+ @name = ::File.expand_path(::File.join(@config.directory, file_name))
86
+
87
+ unless ::File.file?(@name)
88
+ raise "File #{@name} not found." unless @config.create
89
+ FileUtils.touch(@name)
90
+ end
91
+
92
+ @new = ::File.zero?(@name)
93
+ @loaded = false
94
+ end
95
+
96
+ ##
97
+ # Was the file just now created?
98
+ #
99
+ # @return [Boolean] true if new
100
+ def new?
101
+ @new
102
+ end
103
+
104
+ ##
105
+ # Has the file been loaded?
106
+ #
107
+ # @return [Boolean] true if loaded
108
+ def loaded?
109
+ @loaded
110
+ end
111
+
112
+ ##
113
+ # Does the file exist?
114
+ #
115
+ # @return [Boolean] true if exists
116
+ def exist?
117
+ ::File.exist?(name)
118
+ end
119
+
120
+ ##
121
+ # Loads the file's contents.
122
+ # TODO clean up this method.
123
+ #
124
+ # @return [StandupMD::FileList]
125
+ def load
126
+ raise "File #{name} does not exist." unless ::File.file?(name)
127
+ entry_list = EntryList.new
128
+ record = {}
129
+ section_type = ''
130
+ ::File.foreach(name) do |line|
131
+ line.chomp!
132
+ next if line.strip.empty?
133
+ if is_header?(line)
134
+ unless record.empty?
135
+ entry_list << new_entry(record)
136
+ record = {}
137
+ end
138
+ record['header'] = line.sub(%r{^\#{#{@config.header_depth}}\s*}, '')
139
+ section_type = @config.notes_header
140
+ record[section_type] = []
141
+ elsif is_sub_header?(line)
142
+ section_type = determine_section_type(line)
143
+ record[section_type] = []
144
+ else
145
+ record[section_type] << line.sub(bullet_character_regex, '')
146
+ end
147
+ end
148
+ entry_list << new_entry(record) unless record.empty?
149
+ @loaded = true
150
+ @entries = entry_list.sort
151
+ self
152
+ rescue => e
153
+ raise "File malformation: #{e}"
154
+ end
155
+
156
+ ##
157
+ # Writes a new entry to the file if the first entry in the file isn't today.
158
+ # This method is destructive; if a file for entries in the date range
159
+ # already exists, it will be clobbered with the entries in the range.
160
+ #
161
+ # @param [Hash] start_and_end_date
162
+ #
163
+ # @return [Boolean] true if successful
164
+ def write(dates = {})
165
+ sorted_entries = entries.sort
166
+ start_date = dates.fetch(:start_date, sorted_entries.first.date)
167
+ end_date = dates.fetch(:end_date, sorted_entries.last.date)
168
+ ::File.open(name, 'w') do |f|
169
+ sorted_entries.filter(start_date, end_date).sort_reverse.each do |entry|
170
+ f.puts header(entry.date)
171
+ @config.sub_header_order.each do |attr|
172
+ tasks = entry.send(attr)
173
+ next if !tasks || tasks.empty?
174
+ f.puts sub_header(@config.send("#{attr}_header").capitalize)
175
+ tasks.each { |task| f.puts @config.bullet_character + ' ' + task }
176
+ end
177
+ f.puts
178
+ end
179
+ end
180
+ true
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StandupMD
4
+ class File
5
+
6
+ ##
7
+ # Module responsible for reading and writing standup files.
8
+ module Helpers # :nodoc:
9
+
10
+ private
11
+
12
+ def is_header?(line) # :nodoc:
13
+ line.match(header_regex)
14
+ end
15
+
16
+ def is_sub_header?(line) # :nodoc:
17
+ line.match(sub_header_regex)
18
+ end
19
+
20
+ def header_regex # :nodoc:
21
+ %r{^#{'#' * StandupMD.config.file.header_depth}\s+}
22
+ end
23
+
24
+ def sub_header_regex # :nodoc:
25
+ %r{^#{'#' * StandupMD.config.file.sub_header_depth}\s+}
26
+ end
27
+
28
+ def bullet_character_regex # :nodoc:
29
+ %r{\s*#{StandupMD.config.file.bullet_character}\s*}
30
+ end
31
+
32
+ def determine_section_type(line) # :nodoc:
33
+ line = line.sub(%r{^\#{#{StandupMD.config.file.sub_header_depth}}\s*}, '')
34
+ [
35
+ StandupMD.config.file.current_header,
36
+ StandupMD.config.file.previous_header,
37
+ StandupMD.config.file.impediments_header,
38
+ StandupMD.config.file.notes_header
39
+ ].each { |header| return header if line.include?(header) }
40
+ raise "Unrecognized header [#{line}]"
41
+ end
42
+
43
+ def new_entry(record) # :nodoc:
44
+ Entry.new(
45
+ Date.strptime(record['header'], StandupMD.config.file.header_date_format),
46
+ record[StandupMD.config.file.current_header],
47
+ record[StandupMD.config.file.previous_header],
48
+ record[StandupMD.config.file.impediments_header],
49
+ record[StandupMD.config.file.notes_header]
50
+ )
51
+ end
52
+
53
+ def header(date)
54
+ '#' * StandupMD.config.file.header_depth + ' ' + date.strftime(StandupMD.config.file.header_date_format)
55
+ end
56
+
57
+ def sub_header(sh)
58
+ '#' * StandupMD.config.file.sub_header_depth + ' ' + sh
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,9 +1,9 @@
1
- class StandupMD
1
+ # frozen_string_literal: true
2
+
3
+ module StandupMD
2
4
  ##
3
5
  # The gem verision
4
6
  #
5
- # @example
6
- # StandupMD::VERSION
7
- # # => '0.1.3'
8
- VERSION = '0.1.3'
7
+ # @return [String]
8
+ VERSION = '0.3.2'
9
9
  end
@@ -36,4 +36,5 @@ Gem::Specification.new do |spec|
36
36
  spec.require_paths = ['lib']
37
37
  spec.add_development_dependency 'rake', '~> 13.0', '>= 13.0.1'
38
38
  spec.add_development_dependency 'test-unit', '~> 3.3', '>= 3.3.5'
39
+ spec.add_development_dependency 'simplecov'
39
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standup_md
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Gray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-01 00:00:00.000000000 Z
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -50,6 +50,20 @@ dependencies:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: 3.3.5
53
+ - !ruby/object:Gem::Dependency
54
+ name: simplecov
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
53
67
  description: Generate and edit standups in markdown format
54
68
  email: evanthegrayt@vivaldi.net
55
69
  executables:
@@ -69,9 +83,15 @@ files:
69
83
  - doc/README_md.html
70
84
  - doc/StandupMD.html
71
85
  - doc/StandupMD/Cli.html
72
- - doc/TestCli.html
73
- - doc/TestHelper.html
74
- - doc/TestStandupMD.html
86
+ - doc/StandupMD/Cli/Helpers.html
87
+ - doc/StandupMD/Config.html
88
+ - doc/StandupMD/Config/Cli.html
89
+ - doc/StandupMD/Config/Entry.html
90
+ - doc/StandupMD/Config/EntryList.html
91
+ - doc/StandupMD/Config/File.html
92
+ - doc/StandupMD/Entry.html
93
+ - doc/StandupMD/EntryList.html
94
+ - doc/StandupMD/File.html
75
95
  - doc/created.rid
76
96
  - doc/css/fonts.css
77
97
  - doc/css/rdoc.css
@@ -118,6 +138,16 @@ files:
118
138
  - doc/table_of_contents.html
119
139
  - lib/standup_md.rb
120
140
  - lib/standup_md/cli.rb
141
+ - lib/standup_md/cli/helpers.rb
142
+ - lib/standup_md/config.rb
143
+ - lib/standup_md/config/cli.rb
144
+ - lib/standup_md/config/entry.rb
145
+ - lib/standup_md/config/entry_list.rb
146
+ - lib/standup_md/config/file.rb
147
+ - lib/standup_md/entry.rb
148
+ - lib/standup_md/entry_list.rb
149
+ - lib/standup_md/file.rb
150
+ - lib/standup_md/file/helpers.rb
121
151
  - lib/standup_md/version.rb
122
152
  - standup_md.gemspec
123
153
  homepage: https://evanthegrayt.github.io/standup_md/