standup_md 0.3.17 → 1.0.0
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 +3 -0
- data/completion/zsh/_standup +1 -0
- data/lib/standup_md/cli/helpers.rb +15 -2
- data/lib/standup_md/config/file.rb +21 -0
- data/lib/standup_md/entry.rb +46 -28
- data/lib/standup_md/file.rb +4 -44
- data/lib/standup_md/parsers/markdown.rb +168 -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 +3 -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: 2734d31b2ce72d31aceeb789f562b015c4f0e7422b3a755992df7c1f293a7051
|
|
4
|
+
data.tar.gz: 651d1f6f9a613366b90e2b26ba83b8e05dd80dbc5d8bde3a0fcc7d07b57acef1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5df3037a20d107f82cc34533f89ab8c31a2ba13eb8180b5931b052c500c5c1f7c4922d22ffed254c5fb9e9d7623b5bb207ed5d5c0c22773bcc972a91bba061d4
|
|
7
|
+
data.tar.gz: c99e5cf930d4dcf84e6089d458321b00f427ab0c124ecf3b02b8e98d79ba416d5c629793f6f77a8cf1a0337b05ed620600833f1ad8fa2775d7e6eb7205ec22ed
|
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.0)
|
|
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.0)
|
|
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
|
@@ -180,6 +180,7 @@ StandupMD.configure do |c|
|
|
|
180
180
|
c.file.sub_header_order = %w[previous current impediments notes]
|
|
181
181
|
c.file.directory = ::File.join(ENV["HOME"], ".cache", "standup_md")
|
|
182
182
|
c.file.bullet_character = "-"
|
|
183
|
+
c.file.indent_width = 2
|
|
183
184
|
c.file.name_format = "%Y_%m.md"
|
|
184
185
|
c.file.create = true
|
|
185
186
|
|
|
@@ -218,6 +219,7 @@ Some of these options can be changed at runtime. They are as follows.
|
|
|
218
219
|
--impediments ARRAY List of impediments for current entry
|
|
219
220
|
--notes ARRAY List of notes for current entry
|
|
220
221
|
--sub-header-order ARRAY The order of the sub-headers when writing the file
|
|
222
|
+
--indent-width INTEGER Number of spaces used for each nested task level
|
|
221
223
|
-f, --file-name-format STRING Date-formattable string to use for standup file name
|
|
222
224
|
-E, --editor EDITOR Editor to use for opening standup files
|
|
223
225
|
-d, --directory DIRECTORY The directories where standup files are located
|
|
@@ -275,6 +277,7 @@ StandupMD.configure do |c|
|
|
|
275
277
|
c.file.previous_header = "Yesterday"
|
|
276
278
|
c.file.impediments_header = "Hold-ups"
|
|
277
279
|
c.file.bullet_character = "*"
|
|
280
|
+
c.file.indent_width = 2
|
|
278
281
|
c.file.header_date_format = "%m/%d/%Y"
|
|
279
282
|
c.file.sub_header_order = %w[current previous impediments notes]
|
|
280
283
|
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
|
##
|
|
@@ -20,7 +22,9 @@ module StandupMD
|
|
|
20
22
|
next if !tasks || tasks.empty?
|
|
21
23
|
|
|
22
24
|
puts sub_header(header_type)
|
|
23
|
-
|
|
25
|
+
entry.public_send("#{header_type}_tasks").each do |task|
|
|
26
|
+
puts parser.task_line(task)
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
puts
|
|
26
30
|
end
|
|
@@ -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"
|
|
@@ -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
|
|
@@ -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,8 @@ module StandupMD
|
|
|
9
10
|
class Entry
|
|
10
11
|
include Comparable
|
|
11
12
|
|
|
13
|
+
SECTION_TYPES = %i[current previous impediments notes].freeze
|
|
14
|
+
|
|
12
15
|
##
|
|
13
16
|
# Access to the class's configuration.
|
|
14
17
|
#
|
|
@@ -25,30 +28,6 @@ module StandupMD
|
|
|
25
28
|
# @return [Date]
|
|
26
29
|
attr_accessor :date
|
|
27
30
|
|
|
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
31
|
##
|
|
53
32
|
# Creates a generic entry. Default values can be set via configuration.
|
|
54
33
|
# Yields the entry if a block is passed so you can change values.
|
|
@@ -81,10 +60,43 @@ module StandupMD
|
|
|
81
60
|
|
|
82
61
|
@config = self.class.config
|
|
83
62
|
@date = date
|
|
84
|
-
@
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
63
|
+
@sections = {}
|
|
64
|
+
self.current = current
|
|
65
|
+
self.previous = previous
|
|
66
|
+
self.impediments = impediments
|
|
67
|
+
self.notes = notes
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
SECTION_TYPES.each do |type|
|
|
71
|
+
define_method(type) do
|
|
72
|
+
section(type).tasks.map(&:to_s)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
define_method("#{type}=") do |tasks|
|
|
76
|
+
set_section(type, tasks)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
define_method("#{type}_tasks") do
|
|
80
|
+
section(type).tasks
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# Sections for this entry.
|
|
86
|
+
#
|
|
87
|
+
# @return [Array<StandupMD::Section>]
|
|
88
|
+
def sections
|
|
89
|
+
SECTION_TYPES.map { |type| section(type) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Find a section by type.
|
|
94
|
+
#
|
|
95
|
+
# @param [Symbol, String] type
|
|
96
|
+
#
|
|
97
|
+
# @return [StandupMD::Section]
|
|
98
|
+
def section(type)
|
|
99
|
+
@sections[type.to_sym] ||= Section.new(type)
|
|
88
100
|
end
|
|
89
101
|
|
|
90
102
|
##
|
|
@@ -115,5 +127,11 @@ module StandupMD
|
|
|
115
127
|
def to_json
|
|
116
128
|
to_h.to_json
|
|
117
129
|
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def set_section(type, tasks)
|
|
134
|
+
@sections[type.to_sym] = Section.new(type, tasks || [])
|
|
135
|
+
end
|
|
118
136
|
end
|
|
119
137
|
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,168 @@
|
|
|
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
|
+
module Parsers
|
|
12
|
+
##
|
|
13
|
+
# Parser and renderer for the markdown standup format.
|
|
14
|
+
class Markdown
|
|
15
|
+
##
|
|
16
|
+
# Access to file configuration.
|
|
17
|
+
#
|
|
18
|
+
# @return [StandupMD::Config::File]
|
|
19
|
+
attr_reader :config
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Constructs an instance of +StandupMD::Parsers::Markdown+.
|
|
23
|
+
#
|
|
24
|
+
# @param [StandupMD::Config::File] config
|
|
25
|
+
def initialize(config = StandupMD.config.file)
|
|
26
|
+
@config = config
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Reads entries from a markdown standup file.
|
|
31
|
+
#
|
|
32
|
+
# @param [String] file_name
|
|
33
|
+
#
|
|
34
|
+
# @return [StandupMD::EntryList]
|
|
35
|
+
def read(file_name)
|
|
36
|
+
entry_list = EntryList.new
|
|
37
|
+
record = nil
|
|
38
|
+
section = nil
|
|
39
|
+
|
|
40
|
+
::File.foreach(file_name) do |line|
|
|
41
|
+
line.chomp!
|
|
42
|
+
next if line.strip.empty?
|
|
43
|
+
|
|
44
|
+
if header?(line)
|
|
45
|
+
entry_list << entry(record) if record
|
|
46
|
+
record = {title: title(line), sections: {}}
|
|
47
|
+
section = section(:notes)
|
|
48
|
+
record[:sections][:notes] = section
|
|
49
|
+
elsif sub_header?(line)
|
|
50
|
+
section = section(section_type(line))
|
|
51
|
+
record[:sections][section.type] = section
|
|
52
|
+
else
|
|
53
|
+
section << task(line)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
entry_list << entry(record) if record
|
|
58
|
+
entry_list.sort
|
|
59
|
+
rescue => e
|
|
60
|
+
raise "File malformation: #{e}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# Writes entries to a markdown standup file.
|
|
65
|
+
#
|
|
66
|
+
# @param [String] file_name
|
|
67
|
+
# @param [StandupMD::EntryList] entries
|
|
68
|
+
# @param [Date] start_date
|
|
69
|
+
# @param [Date] end_date
|
|
70
|
+
#
|
|
71
|
+
# @return [Boolean]
|
|
72
|
+
def write(file_name, entries, start_date:, end_date:)
|
|
73
|
+
::File.open(file_name, "w") do |f|
|
|
74
|
+
entries.filter(start_date, end_date).sort_reverse.each do |entry|
|
|
75
|
+
f.puts Title.new(entry.date).to_markdown
|
|
76
|
+
config.sub_header_order.each do |attr|
|
|
77
|
+
section = Section.new(attr, entry.public_send("#{attr}_tasks"))
|
|
78
|
+
next if section.empty?
|
|
79
|
+
|
|
80
|
+
f.puts section.to_markdown
|
|
81
|
+
end
|
|
82
|
+
f.puts
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Renders a task as a markdown list item.
|
|
90
|
+
#
|
|
91
|
+
# @param [String, StandupMD::Task] task
|
|
92
|
+
#
|
|
93
|
+
# @return [String]
|
|
94
|
+
def task_line(task)
|
|
95
|
+
build_task(task).to_markdown
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def header?(line)
|
|
101
|
+
line.match?(header_regex)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def sub_header?(line)
|
|
105
|
+
line.match?(sub_header_regex)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def header_regex
|
|
109
|
+
/^#{"#" * config.header_depth}\s+/
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def sub_header_regex
|
|
113
|
+
/^#{"#" * config.sub_header_depth}\s+/
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def title(line)
|
|
117
|
+
line.sub(/^\#{#{config.header_depth}}\s*/, "")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def section(type)
|
|
121
|
+
Section.new(type)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def section_type(line)
|
|
125
|
+
sub_header = line.sub(/^\#{#{config.sub_header_depth}}\s*/, "")
|
|
126
|
+
Entry::SECTION_TYPES.each do |type|
|
|
127
|
+
header = config.public_send("#{type}_header")
|
|
128
|
+
return type if sub_header.include?(header)
|
|
129
|
+
end
|
|
130
|
+
raise "Unrecognized header [#{sub_header}]"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def task(line)
|
|
134
|
+
match = line.match(task_regex)
|
|
135
|
+
return Task.new(line) unless match
|
|
136
|
+
|
|
137
|
+
Task.new(
|
|
138
|
+
match[:text],
|
|
139
|
+
indent_level: match[:indent].size / config.indent_width
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def task_regex
|
|
144
|
+
/\A(?<indent>\s*)#{Regexp.escape(config.bullet_character)}\s*(?<text>.*)\z/
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def entry(record)
|
|
148
|
+
Entry.new(
|
|
149
|
+
Date.strptime(record[:title], config.header_date_format),
|
|
150
|
+
tasks(record, :current),
|
|
151
|
+
tasks(record, :previous),
|
|
152
|
+
tasks(record, :impediments),
|
|
153
|
+
tasks(record, :notes)
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def tasks(record, type)
|
|
158
|
+
record[:sections].fetch(type, Section.new(type)).tasks
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def build_task(task)
|
|
162
|
+
return task if task.is_a?(Task)
|
|
163
|
+
|
|
164
|
+
Task.new(task)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
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 = 0
|
|
25
25
|
|
|
26
26
|
##
|
|
27
27
|
# Version as +[MAJOR, MINOR, PATCH]+
|
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.0
|
|
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
|