standup_md 0.3.17 → 1.0.1
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/Gemfile.lock +2 -2
- data/README.md +7 -12
- data/completion/zsh/_standup +1 -0
- data/lib/standup_md/cli/helpers.rb +26 -13
- data/lib/standup_md/cli.rb +4 -0
- data/lib/standup_md/config/file.rb +21 -0
- data/lib/standup_md/entry.rb +50 -28
- data/lib/standup_md/file.rb +4 -44
- data/lib/standup_md/parsers/markdown.rb +170 -0
- data/lib/standup_md/section.rb +77 -0
- data/lib/standup_md/task.rb +60 -0
- data/lib/standup_md/title.rb +41 -0
- data/lib/standup_md/version.rb +7 -3
- data/lib/standup_md.rb +6 -2
- metadata +5 -2
- data/lib/standup_md/file/helpers.rb +0 -65
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 74c06f0aa4db099f1e64131fdd7e184dd8270db7ee99dd97f9c2cd97374ccef1
|
|
4
|
+
data.tar.gz: 71e038fce40a7f972b058baee23a7cfbbc1b2eabdbb8f0ccee466ea6fdf70345
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6321c1cf53d23f8a2cd05f191f87059bc9ac308951373ab391ef53c844289098b1edcd4fcb6ef107d7f5bea9f124f7cbd105d75c3d40c2f3fe689427be783264
|
|
7
|
+
data.tar.gz: 8cefeb0ac1a75a8313ebdaabed8edfdf5b928b73c625c3f3bafebe1b330fafe6d44de2ebc26aee9379ce811b376eac601edd23dc4870c6fafc9b5acc0babf1aa
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
standup_md (0.
|
|
4
|
+
standup_md (1.0.1)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -115,7 +115,7 @@ CHECKSUMS
|
|
|
115
115
|
standard (1.54.0) sha256=7a4b08f83d9893083c8f03bc486f0feeb6a84d48233b40829c03ef4767ea0100
|
|
116
116
|
standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b
|
|
117
117
|
standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2
|
|
118
|
-
standup_md (0.
|
|
118
|
+
standup_md (1.0.1)
|
|
119
119
|
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
|
|
120
120
|
test-unit (3.7.8) sha256=689d1ca53f4d46f678b4e5d68d8e4294f87fc5e8b9238cc4de7c5727e8d65943
|
|
121
121
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
data/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# The Standup Doctor
|
|
2
2
|
<!-- vi: set ts=4 sw=4 : -->
|
|
3
3
|
[](https://actions-badge.atrox.dev/evanthegrayt/standup_md/goto?ref=master)
|
|
4
|
-
[](https://rubygems.org/gems/standup_md)
|
|
5
|
+

|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
8
|
> The cure for all your standup woes.
|
|
@@ -151,16 +152,7 @@ You'll notice, a lot of settings don't have the ability to be changed at runtime
|
|
|
151
152
|
when calling the executable. This is because the file structure is very
|
|
152
153
|
important, and changing values that affect formatting will cause problems with
|
|
153
154
|
the file parser. If you don't want to use a default, make the change in your
|
|
154
|
-
config file before you start editing standups.
|
|
155
|
-
issue](https://github.com/evanthegrayt/standup_md/issues/16) for handling this
|
|
156
|
-
for the user, but they're not available yet.
|
|
157
|
-
|
|
158
|
-
There are no options to change the headers at runtime because it uses the
|
|
159
|
-
headers to detect tasks from previous entries. If changed at runtime, this would
|
|
160
|
-
cause errors. For this reason, if you don't like the default headers, change
|
|
161
|
-
them in your configuration file after installation, and then try to not change
|
|
162
|
-
them again.
|
|
163
|
-
|
|
155
|
+
config file before you start editing standups.
|
|
164
156
|
|
|
165
157
|
#### Available Config File Options and Defaults
|
|
166
158
|
For command-line usage, this file needs to be named `~/.standuprc`. To use in a
|
|
@@ -180,6 +172,7 @@ StandupMD.configure do |c|
|
|
|
180
172
|
c.file.sub_header_order = %w[previous current impediments notes]
|
|
181
173
|
c.file.directory = ::File.join(ENV["HOME"], ".cache", "standup_md")
|
|
182
174
|
c.file.bullet_character = "-"
|
|
175
|
+
c.file.indent_width = 2
|
|
183
176
|
c.file.name_format = "%Y_%m.md"
|
|
184
177
|
c.file.create = true
|
|
185
178
|
|
|
@@ -214,10 +207,11 @@ Some of these options can be changed at runtime. They are as follows.
|
|
|
214
207
|
|
|
215
208
|
```
|
|
216
209
|
--current ARRAY List of current entry's tasks
|
|
217
|
-
--previous ARRAY List of
|
|
210
|
+
--previous ARRAY List of previous entry's tasks
|
|
218
211
|
--impediments ARRAY List of impediments for current entry
|
|
219
212
|
--notes ARRAY List of notes for current entry
|
|
220
213
|
--sub-header-order ARRAY The order of the sub-headers when writing the file
|
|
214
|
+
--indent-width INTEGER Number of spaces used for each nested task level
|
|
221
215
|
-f, --file-name-format STRING Date-formattable string to use for standup file name
|
|
222
216
|
-E, --editor EDITOR Editor to use for opening standup files
|
|
223
217
|
-d, --directory DIRECTORY The directories where standup files are located
|
|
@@ -275,6 +269,7 @@ StandupMD.configure do |c|
|
|
|
275
269
|
c.file.previous_header = "Yesterday"
|
|
276
270
|
c.file.impediments_header = "Hold-ups"
|
|
277
271
|
c.file.bullet_character = "*"
|
|
272
|
+
c.file.indent_width = 2
|
|
278
273
|
c.file.header_date_format = "%m/%d/%Y"
|
|
279
274
|
c.file.sub_header_order = %w[current previous impediments notes]
|
|
280
275
|
end
|
data/completion/zsh/_standup
CHANGED
|
@@ -62,6 +62,7 @@ _arguments -s -S \
|
|
|
62
62
|
'--impediments[List of impediments for current entry]:impediments:_standup_array_values' \
|
|
63
63
|
'--notes[List of notes for current entry]:notes:_standup_array_values' \
|
|
64
64
|
'--sub-header-order[The order of the sub-headers when writing the file]:sub-header order:_standup_sub_header_order' \
|
|
65
|
+
'--indent-width[Number of spaces used for each nested task level]:indent width:' \
|
|
65
66
|
'(-f --file-name-format)-f[Date-formattable string to use for standup file name]:strftime format:' \
|
|
66
67
|
'(-f --file-name-format)--file-name-format[Date-formattable string to use for standup file name]:strftime format:' \
|
|
67
68
|
'(-E --editor)-E[Editor to use for opening standup files]:editor:_path_commands' \
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "standup_md/parsers/markdown"
|
|
4
|
+
|
|
3
5
|
module StandupMD
|
|
4
6
|
class Cli
|
|
5
7
|
##
|
|
@@ -16,11 +18,13 @@ module StandupMD
|
|
|
16
18
|
|
|
17
19
|
puts header(entry)
|
|
18
20
|
config.file.sub_header_order.each do |header_type|
|
|
19
|
-
tasks = entry.public_send(header_type)
|
|
20
|
-
next if
|
|
21
|
+
tasks = entry.public_send("#{header_type}_tasks")
|
|
22
|
+
next if tasks.empty?
|
|
21
23
|
|
|
22
24
|
puts sub_header(header_type)
|
|
23
|
-
tasks.each
|
|
25
|
+
tasks.each do |task|
|
|
26
|
+
puts parser.task_line(task)
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
puts
|
|
26
30
|
end
|
|
@@ -51,7 +55,7 @@ module StandupMD
|
|
|
51
55
|
|
|
52
56
|
opts.on(
|
|
53
57
|
"--previous ARRAY", Array,
|
|
54
|
-
"List of
|
|
58
|
+
"List of previous entry's tasks"
|
|
55
59
|
) { |v| config.entry.previous = v }
|
|
56
60
|
|
|
57
61
|
opts.on(
|
|
@@ -69,6 +73,11 @@ module StandupMD
|
|
|
69
73
|
"The order of the sub-headers when writing the file"
|
|
70
74
|
) { |v| config.file.sub_header_order = v }
|
|
71
75
|
|
|
76
|
+
opts.on(
|
|
77
|
+
"--indent-width INTEGER", Integer,
|
|
78
|
+
"Number of spaces used for each nested task level"
|
|
79
|
+
) { |v| config.file.indent_width = v }
|
|
80
|
+
|
|
72
81
|
opts.on(
|
|
73
82
|
"-f", "--file-name-format STRING",
|
|
74
83
|
"Date-formattable string to use for standup file name"
|
|
@@ -151,14 +160,14 @@ module StandupMD
|
|
|
151
160
|
end
|
|
152
161
|
|
|
153
162
|
##
|
|
154
|
-
# The "previous"
|
|
163
|
+
# The "previous" tasks.
|
|
155
164
|
#
|
|
156
165
|
# @return [Array]
|
|
157
166
|
def previous_entry(file)
|
|
158
|
-
return config.entry.
|
|
159
|
-
return
|
|
167
|
+
return config.entry.previous unless config.cli.auto_fill_previous
|
|
168
|
+
return prev_entry_tasks(prev_file.load.entries) if file.new? && prev_file
|
|
160
169
|
|
|
161
|
-
|
|
170
|
+
prev_entry_tasks(file.entries)
|
|
162
171
|
end
|
|
163
172
|
|
|
164
173
|
##
|
|
@@ -181,13 +190,13 @@ module StandupMD
|
|
|
181
190
|
end
|
|
182
191
|
|
|
183
192
|
##
|
|
184
|
-
# The previous entry.
|
|
193
|
+
# The previous entry's current tasks.
|
|
185
194
|
#
|
|
186
195
|
# @param [StandupMD::EntryList] entries
|
|
187
196
|
#
|
|
188
|
-
# @return [StandupMD::
|
|
189
|
-
def
|
|
190
|
-
entries.empty? ? [] : entries.last.
|
|
197
|
+
# @return [Array<StandupMD::Task>]
|
|
198
|
+
def prev_entry_tasks(entries)
|
|
199
|
+
entries.empty? ? [] : entries.last.current_tasks
|
|
191
200
|
end
|
|
192
201
|
|
|
193
202
|
##
|
|
@@ -217,7 +226,11 @@ module StandupMD
|
|
|
217
226
|
# @return [String]
|
|
218
227
|
def sub_header(header_type)
|
|
219
228
|
"#" * config.file.sub_header_depth + " " +
|
|
220
|
-
config.file.public_send("#{header_type}_header")
|
|
229
|
+
config.file.public_send("#{header_type}_header")
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def parser
|
|
233
|
+
StandupMD::Parsers::Markdown.new(config.file)
|
|
221
234
|
end
|
|
222
235
|
end
|
|
223
236
|
end
|
data/lib/standup_md/cli.rb
CHANGED
|
@@ -20,6 +20,7 @@ module StandupMD
|
|
|
20
20
|
sub_header_order: %w[previous current impediments notes],
|
|
21
21
|
directory: ::File.join(ENV["HOME"], ".cache", "standup_md"),
|
|
22
22
|
bullet_character: "-",
|
|
23
|
+
indent_width: 2,
|
|
23
24
|
name_format: "%Y_%m.md",
|
|
24
25
|
create: true
|
|
25
26
|
}.freeze
|
|
@@ -56,6 +57,14 @@ module StandupMD
|
|
|
56
57
|
# @default "-" (dash)
|
|
57
58
|
attr_reader :bullet_character
|
|
58
59
|
|
|
60
|
+
##
|
|
61
|
+
# Number of spaces used for each nested task level.
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer]
|
|
64
|
+
#
|
|
65
|
+
# @default 2
|
|
66
|
+
attr_reader :indent_width
|
|
67
|
+
|
|
59
68
|
##
|
|
60
69
|
# String to be used as "Current" header.
|
|
61
70
|
#
|
|
@@ -186,6 +195,18 @@ module StandupMD
|
|
|
186
195
|
@bullet_character = char
|
|
187
196
|
end
|
|
188
197
|
|
|
198
|
+
##
|
|
199
|
+
# Setter for indent_width. Must be a positive integer.
|
|
200
|
+
#
|
|
201
|
+
# @param [Integer] width
|
|
202
|
+
#
|
|
203
|
+
# @return [Integer]
|
|
204
|
+
def indent_width=(width)
|
|
205
|
+
raise "Indent width must be a positive integer" unless width.is_a?(Integer) && width.positive?
|
|
206
|
+
|
|
207
|
+
@indent_width = width
|
|
208
|
+
end
|
|
209
|
+
|
|
189
210
|
##
|
|
190
211
|
# Setter for directory. Must be expanded in case the user uses `~` for
|
|
191
212
|
# home. If the directory doesn't exist, it will be created. To reset
|
data/lib/standup_md/entry.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require "standup_md/section"
|
|
4
5
|
|
|
5
6
|
module StandupMD
|
|
6
7
|
##
|
|
@@ -9,6 +10,12 @@ module StandupMD
|
|
|
9
10
|
class Entry
|
|
10
11
|
include Comparable
|
|
11
12
|
|
|
13
|
+
##
|
|
14
|
+
# Section identifiers supported by each standup entry.
|
|
15
|
+
#
|
|
16
|
+
# @return [Array<Symbol>]
|
|
17
|
+
SECTION_TYPES = %i[current previous impediments notes].freeze
|
|
18
|
+
|
|
12
19
|
##
|
|
13
20
|
# Access to the class's configuration.
|
|
14
21
|
#
|
|
@@ -25,30 +32,6 @@ module StandupMD
|
|
|
25
32
|
# @return [Date]
|
|
26
33
|
attr_accessor :date
|
|
27
34
|
|
|
28
|
-
##
|
|
29
|
-
# The tasks for today.
|
|
30
|
-
#
|
|
31
|
-
# @return [Array]
|
|
32
|
-
attr_accessor :current
|
|
33
|
-
|
|
34
|
-
##
|
|
35
|
-
# The tasks from the previous day.
|
|
36
|
-
#
|
|
37
|
-
# @return [Array]
|
|
38
|
-
attr_accessor :previous
|
|
39
|
-
|
|
40
|
-
##
|
|
41
|
-
# Impediments for this entry.
|
|
42
|
-
#
|
|
43
|
-
# @return [Array]
|
|
44
|
-
attr_accessor :impediments
|
|
45
|
-
|
|
46
|
-
##
|
|
47
|
-
# Nnotes to add to this entry.
|
|
48
|
-
#
|
|
49
|
-
# @return [Array]
|
|
50
|
-
attr_accessor :notes
|
|
51
|
-
|
|
52
35
|
##
|
|
53
36
|
# Creates a generic entry. Default values can be set via configuration.
|
|
54
37
|
# Yields the entry if a block is passed so you can change values.
|
|
@@ -81,10 +64,43 @@ module StandupMD
|
|
|
81
64
|
|
|
82
65
|
@config = self.class.config
|
|
83
66
|
@date = date
|
|
84
|
-
@
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
67
|
+
@sections = {}
|
|
68
|
+
self.current = current
|
|
69
|
+
self.previous = previous
|
|
70
|
+
self.impediments = impediments
|
|
71
|
+
self.notes = notes
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
SECTION_TYPES.each do |type|
|
|
75
|
+
define_method(type) do
|
|
76
|
+
section(type).tasks.map(&:to_s)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
define_method("#{type}=") do |tasks|
|
|
80
|
+
set_section(type, tasks)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
define_method("#{type}_tasks") do
|
|
84
|
+
section(type).tasks
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Sections for this entry.
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<StandupMD::Section>]
|
|
92
|
+
def sections
|
|
93
|
+
SECTION_TYPES.map { |type| section(type) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
##
|
|
97
|
+
# Find a section by type.
|
|
98
|
+
#
|
|
99
|
+
# @param [Symbol, String] type
|
|
100
|
+
#
|
|
101
|
+
# @return [StandupMD::Section]
|
|
102
|
+
def section(type)
|
|
103
|
+
@sections[type.to_sym] ||= Section.new(type)
|
|
88
104
|
end
|
|
89
105
|
|
|
90
106
|
##
|
|
@@ -115,5 +131,11 @@ module StandupMD
|
|
|
115
131
|
def to_json
|
|
116
132
|
to_h.to_json
|
|
117
133
|
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def set_section(type, tasks)
|
|
138
|
+
@sections[type.to_sym] = Section.new(type, tasks || [])
|
|
139
|
+
end
|
|
118
140
|
end
|
|
119
141
|
end
|
data/lib/standup_md/file.rb
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
4
|
require "fileutils"
|
|
5
|
-
require "standup_md/
|
|
5
|
+
require "standup_md/parsers/markdown"
|
|
6
6
|
|
|
7
7
|
module StandupMD
|
|
8
8
|
##
|
|
9
9
|
# Class for handling reading and writing standup files.
|
|
10
10
|
class File
|
|
11
|
-
include StandupMD::File::Helpers
|
|
12
|
-
|
|
13
11
|
class << self
|
|
14
12
|
##
|
|
15
13
|
# Access to the class's configuration.
|
|
@@ -88,6 +86,7 @@ module StandupMD
|
|
|
88
86
|
# @return [StandupMP::File]
|
|
89
87
|
def initialize(file_name)
|
|
90
88
|
@config = self.class.config
|
|
89
|
+
@parser = StandupMD::Parsers::Markdown.new(@config)
|
|
91
90
|
if file_name.include?(::File::SEPARATOR)
|
|
92
91
|
raise ArgumentError,
|
|
93
92
|
"#{file_name} contains directory. Please use `StandupMD.config.file.directory=`"
|
|
@@ -137,40 +136,14 @@ module StandupMD
|
|
|
137
136
|
|
|
138
137
|
##
|
|
139
138
|
# Loads the file's contents.
|
|
140
|
-
# TODO clean up this method.
|
|
141
139
|
#
|
|
142
140
|
# @return [StandupMD::FileList]
|
|
143
141
|
def load
|
|
144
142
|
raise "File #{name} does not exist." unless ::File.file?(name)
|
|
145
143
|
|
|
146
|
-
entry_list = EntryList.new
|
|
147
|
-
record = {}
|
|
148
|
-
section_type = ""
|
|
149
|
-
::File.foreach(name) do |line|
|
|
150
|
-
line.chomp!
|
|
151
|
-
next if line.strip.empty?
|
|
152
|
-
|
|
153
|
-
if header?(line)
|
|
154
|
-
unless record.empty?
|
|
155
|
-
entry_list << new_entry(record)
|
|
156
|
-
record = {}
|
|
157
|
-
end
|
|
158
|
-
record["header"] = line.sub(/^\#{#{@config.header_depth}}\s*/, "")
|
|
159
|
-
section_type = @config.notes_header
|
|
160
|
-
record[section_type] = []
|
|
161
|
-
elsif sub_header?(line)
|
|
162
|
-
section_type = determine_section_type(line)
|
|
163
|
-
record[section_type] = []
|
|
164
|
-
else
|
|
165
|
-
record[section_type] << line.sub(bullet_character_regex, "")
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
entry_list << new_entry(record) unless record.empty?
|
|
169
144
|
@loaded = true
|
|
170
|
-
@entries =
|
|
145
|
+
@entries = @parser.read(name)
|
|
171
146
|
self
|
|
172
|
-
rescue => e
|
|
173
|
-
raise "File malformation: #{e}"
|
|
174
147
|
end
|
|
175
148
|
|
|
176
149
|
##
|
|
@@ -185,20 +158,7 @@ module StandupMD
|
|
|
185
158
|
sorted_entries = entries.sort
|
|
186
159
|
start_date = dates.fetch(:start_date, sorted_entries.first.date)
|
|
187
160
|
end_date = dates.fetch(:end_date, sorted_entries.last.date)
|
|
188
|
-
|
|
189
|
-
sorted_entries.filter(start_date, end_date).sort_reverse.each do |entry|
|
|
190
|
-
f.puts header(entry.date)
|
|
191
|
-
@config.sub_header_order.each do |attr|
|
|
192
|
-
tasks = entry.public_send(attr)
|
|
193
|
-
next if !tasks || tasks.empty?
|
|
194
|
-
|
|
195
|
-
f.puts sub_header(@config.public_send("#{attr}_header").capitalize)
|
|
196
|
-
tasks.each { |task| f.puts "#{@config.bullet_character} #{task}" }
|
|
197
|
-
end
|
|
198
|
-
f.puts
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
true
|
|
161
|
+
@parser.write(name, sorted_entries, start_date: start_date, end_date: end_date)
|
|
202
162
|
end
|
|
203
163
|
end
|
|
204
164
|
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "standup_md/entry"
|
|
5
|
+
require "standup_md/entry_list"
|
|
6
|
+
require "standup_md/section"
|
|
7
|
+
require "standup_md/task"
|
|
8
|
+
require "standup_md/title"
|
|
9
|
+
|
|
10
|
+
module StandupMD
|
|
11
|
+
##
|
|
12
|
+
# Namespace for standup file parsers and renderers.
|
|
13
|
+
module Parsers
|
|
14
|
+
##
|
|
15
|
+
# Parser and renderer for the markdown standup format.
|
|
16
|
+
class Markdown
|
|
17
|
+
##
|
|
18
|
+
# Access to file configuration.
|
|
19
|
+
#
|
|
20
|
+
# @return [StandupMD::Config::File]
|
|
21
|
+
attr_reader :config
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Constructs an instance of +StandupMD::Parsers::Markdown+.
|
|
25
|
+
#
|
|
26
|
+
# @param [StandupMD::Config::File] config
|
|
27
|
+
def initialize(config = StandupMD.config.file)
|
|
28
|
+
@config = config
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Reads entries from a markdown standup file.
|
|
33
|
+
#
|
|
34
|
+
# @param [String] file_name
|
|
35
|
+
#
|
|
36
|
+
# @return [StandupMD::EntryList]
|
|
37
|
+
def read(file_name)
|
|
38
|
+
entry_list = EntryList.new
|
|
39
|
+
record = nil
|
|
40
|
+
section = nil
|
|
41
|
+
|
|
42
|
+
::File.foreach(file_name) do |line|
|
|
43
|
+
line.chomp!
|
|
44
|
+
next if line.strip.empty?
|
|
45
|
+
|
|
46
|
+
if header?(line)
|
|
47
|
+
entry_list << entry(record) if record
|
|
48
|
+
record = {title: title(line), sections: {}}
|
|
49
|
+
section = section(:notes)
|
|
50
|
+
record[:sections][:notes] = section
|
|
51
|
+
elsif sub_header?(line)
|
|
52
|
+
section = section(section_type(line))
|
|
53
|
+
record[:sections][section.type] = section
|
|
54
|
+
else
|
|
55
|
+
section << task(line)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
entry_list << entry(record) if record
|
|
60
|
+
entry_list.sort
|
|
61
|
+
rescue => e
|
|
62
|
+
raise "File malformation: #{e}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Writes entries to a markdown standup file.
|
|
67
|
+
#
|
|
68
|
+
# @param [String] file_name
|
|
69
|
+
# @param [StandupMD::EntryList] entries
|
|
70
|
+
# @param [Date] start_date
|
|
71
|
+
# @param [Date] end_date
|
|
72
|
+
#
|
|
73
|
+
# @return [Boolean]
|
|
74
|
+
def write(file_name, entries, start_date:, end_date:)
|
|
75
|
+
::File.open(file_name, "w") do |f|
|
|
76
|
+
entries.filter(start_date, end_date).sort_reverse.each do |entry|
|
|
77
|
+
f.puts Title.new(entry.date).to_markdown
|
|
78
|
+
config.sub_header_order.each do |attr|
|
|
79
|
+
section = Section.new(attr, entry.public_send("#{attr}_tasks"))
|
|
80
|
+
next if section.empty?
|
|
81
|
+
|
|
82
|
+
f.puts section.to_markdown
|
|
83
|
+
end
|
|
84
|
+
f.puts
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Renders a task as a markdown list item.
|
|
92
|
+
#
|
|
93
|
+
# @param [String, StandupMD::Task] task
|
|
94
|
+
#
|
|
95
|
+
# @return [String]
|
|
96
|
+
def task_line(task)
|
|
97
|
+
build_task(task).to_markdown
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def header?(line)
|
|
103
|
+
line.match?(header_regex)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def sub_header?(line)
|
|
107
|
+
line.match?(sub_header_regex)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def header_regex
|
|
111
|
+
/^#{"#" * config.header_depth}\s+/
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def sub_header_regex
|
|
115
|
+
/^#{"#" * config.sub_header_depth}\s+/
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def title(line)
|
|
119
|
+
line.sub(/^\#{#{config.header_depth}}\s*/, "")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def section(type)
|
|
123
|
+
Section.new(type)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def section_type(line)
|
|
127
|
+
sub_header = line.sub(/^\#{#{config.sub_header_depth}}\s*/, "")
|
|
128
|
+
Entry::SECTION_TYPES.each do |type|
|
|
129
|
+
header = config.public_send("#{type}_header")
|
|
130
|
+
return type if sub_header.include?(header)
|
|
131
|
+
end
|
|
132
|
+
raise "Unrecognized header [#{sub_header}]"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def task(line)
|
|
136
|
+
match = line.match(task_regex)
|
|
137
|
+
return Task.new(line) unless match
|
|
138
|
+
|
|
139
|
+
Task.new(
|
|
140
|
+
match[:text],
|
|
141
|
+
indent_level: match[:indent].size / config.indent_width
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def task_regex
|
|
146
|
+
/\A(?<indent>\s*)#{Regexp.escape(config.bullet_character)}\s*(?<text>.*)\z/
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def entry(record)
|
|
150
|
+
Entry.new(
|
|
151
|
+
Date.strptime(record[:title], config.header_date_format),
|
|
152
|
+
tasks(record, :current),
|
|
153
|
+
tasks(record, :previous),
|
|
154
|
+
tasks(record, :impediments),
|
|
155
|
+
tasks(record, :notes)
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def tasks(record, type)
|
|
160
|
+
record[:sections].fetch(type, Section.new(type)).tasks
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def build_task(task)
|
|
164
|
+
return task if task.is_a?(Task)
|
|
165
|
+
|
|
166
|
+
Task.new(task)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "standup_md/task"
|
|
4
|
+
|
|
5
|
+
module StandupMD
|
|
6
|
+
##
|
|
7
|
+
# A named section of a standup entry, such as current, previous,
|
|
8
|
+
# impediments, or notes.
|
|
9
|
+
class Section
|
|
10
|
+
##
|
|
11
|
+
# The semantic section type.
|
|
12
|
+
#
|
|
13
|
+
# @return [Symbol]
|
|
14
|
+
attr_reader :type
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Tasks for the section.
|
|
18
|
+
#
|
|
19
|
+
# @return [Array<StandupMD::Task>]
|
|
20
|
+
attr_reader :tasks
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Constructs an instance of +StandupMD::Section+.
|
|
24
|
+
#
|
|
25
|
+
# @param [Symbol, String] type
|
|
26
|
+
# @param [Array<String, StandupMD::Task>] tasks
|
|
27
|
+
def initialize(type, tasks = [])
|
|
28
|
+
@type = type.to_sym
|
|
29
|
+
@tasks = tasks.map { |task| build_task(task) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Adds a task to the section.
|
|
34
|
+
#
|
|
35
|
+
# @param [String, StandupMD::Task] task
|
|
36
|
+
#
|
|
37
|
+
# @return [Array<StandupMD::Task>]
|
|
38
|
+
def <<(task)
|
|
39
|
+
tasks << build_task(task)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Is the section empty?
|
|
44
|
+
#
|
|
45
|
+
# @return [Boolean]
|
|
46
|
+
def empty?
|
|
47
|
+
tasks.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# The configured section heading.
|
|
52
|
+
#
|
|
53
|
+
# @return [String]
|
|
54
|
+
def to_s
|
|
55
|
+
StandupMD.config.file.public_send("#{type}_header")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# The section rendered as markdown lines.
|
|
60
|
+
#
|
|
61
|
+
# @return [Array<String>]
|
|
62
|
+
def to_markdown
|
|
63
|
+
[
|
|
64
|
+
"#" * StandupMD.config.file.sub_header_depth + " " + to_s,
|
|
65
|
+
*tasks.map(&:to_markdown)
|
|
66
|
+
]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def build_task(task)
|
|
72
|
+
return task if task.is_a?(Task)
|
|
73
|
+
|
|
74
|
+
Task.new(task)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StandupMD
|
|
4
|
+
##
|
|
5
|
+
# A single standup task. The text stays format-neutral, while indentation
|
|
6
|
+
# level lets parsers render nested tasks for their own formats.
|
|
7
|
+
class Task
|
|
8
|
+
##
|
|
9
|
+
# The task text.
|
|
10
|
+
#
|
|
11
|
+
# @return [String]
|
|
12
|
+
attr_reader :text
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# The nesting level of the task.
|
|
16
|
+
#
|
|
17
|
+
# @return [Integer]
|
|
18
|
+
attr_reader :indent_level
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Constructs an instance of +StandupMD::Task+.
|
|
22
|
+
#
|
|
23
|
+
# @param [String] text
|
|
24
|
+
# @param [Integer] indent_level
|
|
25
|
+
def initialize(text, indent_level: 0)
|
|
26
|
+
unless indent_level.is_a?(Integer) && !indent_level.negative?
|
|
27
|
+
raise ArgumentError, "Indent level must be a non-negative integer"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@text = text.to_s
|
|
31
|
+
@indent_level = indent_level
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# The format-neutral task text.
|
|
36
|
+
#
|
|
37
|
+
# @return [String]
|
|
38
|
+
def to_s
|
|
39
|
+
text
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# The task rendered as a markdown list item.
|
|
44
|
+
#
|
|
45
|
+
# @return [String]
|
|
46
|
+
def to_markdown
|
|
47
|
+
indent = " " * StandupMD.config.file.indent_width * indent_level
|
|
48
|
+
"#{indent}#{StandupMD.config.file.bullet_character} #{text}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Compares task contents.
|
|
53
|
+
def ==(other)
|
|
54
|
+
return text == other if other.is_a?(String)
|
|
55
|
+
return false unless other.is_a?(Task)
|
|
56
|
+
|
|
57
|
+
text == other.text && indent_level == other.indent_level
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
|
|
5
|
+
module StandupMD
|
|
6
|
+
##
|
|
7
|
+
# The title for a standup entry.
|
|
8
|
+
class Title
|
|
9
|
+
##
|
|
10
|
+
# The entry date.
|
|
11
|
+
#
|
|
12
|
+
# @return [Date]
|
|
13
|
+
attr_reader :date
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Constructs an instance of +StandupMD::Title+.
|
|
17
|
+
#
|
|
18
|
+
# @param [Date] date
|
|
19
|
+
def initialize(date)
|
|
20
|
+
raise ArgumentError, "Must be a Date object" unless date.is_a?(Date)
|
|
21
|
+
|
|
22
|
+
@date = date
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# The configured title text.
|
|
27
|
+
#
|
|
28
|
+
# @return [String]
|
|
29
|
+
def to_s
|
|
30
|
+
date.strftime(StandupMD.config.file.header_date_format)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# The title rendered as markdown.
|
|
35
|
+
#
|
|
36
|
+
# @return [String]
|
|
37
|
+
def to_markdown
|
|
38
|
+
"#" * StandupMD.config.file.header_depth + " " + to_s
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/standup_md/version.rb
CHANGED
|
@@ -9,19 +9,19 @@ module StandupMD
|
|
|
9
9
|
# Major version.
|
|
10
10
|
#
|
|
11
11
|
# @return [Integer]
|
|
12
|
-
MAJOR =
|
|
12
|
+
MAJOR = 1
|
|
13
13
|
|
|
14
14
|
##
|
|
15
15
|
# Minor version.
|
|
16
16
|
#
|
|
17
17
|
# @return [Integer]
|
|
18
|
-
MINOR =
|
|
18
|
+
MINOR = 0
|
|
19
19
|
|
|
20
20
|
##
|
|
21
21
|
# Patch version.
|
|
22
22
|
#
|
|
23
23
|
# @return [Integer]
|
|
24
|
-
PATCH =
|
|
24
|
+
PATCH = 1
|
|
25
25
|
|
|
26
26
|
##
|
|
27
27
|
# Version as +[MAJOR, MINOR, PATCH]+
|
|
@@ -48,5 +48,9 @@ module StandupMD
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
##
|
|
52
|
+
# Full gem version string.
|
|
53
|
+
#
|
|
54
|
+
# @return [String]
|
|
51
55
|
VERSION = StandupMD::Version.to_s
|
|
52
56
|
end
|
data/lib/standup_md.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "standup_md/version"
|
|
4
|
-
require "standup_md/
|
|
4
|
+
require "standup_md/config"
|
|
5
|
+
require "standup_md/task"
|
|
6
|
+
require "standup_md/title"
|
|
7
|
+
require "standup_md/section"
|
|
5
8
|
require "standup_md/entry"
|
|
6
9
|
require "standup_md/entry_list"
|
|
10
|
+
require "standup_md/parsers/markdown"
|
|
11
|
+
require "standup_md/file"
|
|
7
12
|
require "standup_md/cli"
|
|
8
|
-
require "standup_md/config"
|
|
9
13
|
|
|
10
14
|
##
|
|
11
15
|
# The main module for the gem. Provides access to configuration classes.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: standup_md
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Gray
|
|
@@ -119,7 +119,10 @@ files:
|
|
|
119
119
|
- lib/standup_md/entry.rb
|
|
120
120
|
- lib/standup_md/entry_list.rb
|
|
121
121
|
- lib/standup_md/file.rb
|
|
122
|
-
- lib/standup_md/
|
|
122
|
+
- lib/standup_md/parsers/markdown.rb
|
|
123
|
+
- lib/standup_md/section.rb
|
|
124
|
+
- lib/standup_md/task.rb
|
|
125
|
+
- lib/standup_md/title.rb
|
|
123
126
|
- lib/standup_md/version.rb
|
|
124
127
|
- standup_md.gemspec
|
|
125
128
|
homepage: https://evanthegrayt.github.io/standup_md/
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module StandupMD
|
|
4
|
-
class File
|
|
5
|
-
##
|
|
6
|
-
# Module responsible for reading and writing standup files.
|
|
7
|
-
module Helpers # :nodoc:
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
def header?(line) # :nodoc:
|
|
11
|
-
line.match(header_regex)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def sub_header?(line) # :nodoc:
|
|
15
|
-
line.match(sub_header_regex)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def header_regex # :nodoc:
|
|
19
|
-
/^#{"#" * StandupMD.config.file.header_depth}\s+/
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def sub_header_regex # :nodoc:
|
|
23
|
-
/^#{"#" * StandupMD.config.file.sub_header_depth}\s+/
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def bullet_character_regex # :nodoc:
|
|
27
|
-
/\s*#{StandupMD.config.file.bullet_character}\s*/
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def determine_section_type(line) # :nodoc:
|
|
31
|
-
line = line.sub(/^\#{#{StandupMD.config.file.sub_header_depth}}\s*/, "")
|
|
32
|
-
[
|
|
33
|
-
StandupMD.config.file.current_header,
|
|
34
|
-
StandupMD.config.file.previous_header,
|
|
35
|
-
StandupMD.config.file.impediments_header,
|
|
36
|
-
StandupMD.config.file.notes_header
|
|
37
|
-
].each { |header| return header if line.include?(header) }
|
|
38
|
-
raise "Unrecognized header [#{line}]"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def new_entry(record) # :nodoc:
|
|
42
|
-
Entry.new(
|
|
43
|
-
Date.strptime(
|
|
44
|
-
record["header"],
|
|
45
|
-
StandupMD.config.file.header_date_format
|
|
46
|
-
),
|
|
47
|
-
record[StandupMD.config.file.current_header],
|
|
48
|
-
record[StandupMD.config.file.previous_header],
|
|
49
|
-
record[StandupMD.config.file.impediments_header],
|
|
50
|
-
record[StandupMD.config.file.notes_header]
|
|
51
|
-
)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def header(date)
|
|
55
|
-
"#" * StandupMD.config.file.header_depth +
|
|
56
|
-
" " +
|
|
57
|
-
date.strftime(StandupMD.config.file.header_date_format)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def sub_header(subhead)
|
|
61
|
-
"#" * StandupMD.config.file.sub_header_depth + " " + subhead
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|