standup_md 0.1.3 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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/