standup_md 0.2.1 → 0.3.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/.gitignore +1 -0
- data/Gemfile.lock +7 -1
- data/README.md +142 -116
- data/bin/standup +1 -1
- data/doc/README_md.html +143 -96
- data/doc/StandupMD.html +96 -1322
- data/doc/StandupMD/Cli.html +124 -479
- data/doc/StandupMD/Cli/Helpers.html +167 -0
- data/doc/StandupMD/Config.html +230 -0
- data/doc/StandupMD/Config/Cli.html +355 -0
- data/doc/StandupMD/Config/Entry.html +284 -0
- data/doc/StandupMD/Config/EntryList.html +197 -0
- data/doc/StandupMD/Config/File.html +609 -0
- data/doc/StandupMD/Entry.html +478 -0
- data/doc/StandupMD/EntryList.html +759 -0
- data/doc/StandupMD/File.html +574 -0
- data/doc/created.rid +15 -5
- data/doc/index.html +153 -94
- data/doc/js/search_index.js +1 -1
- data/doc/js/search_index.js.gz +0 -0
- data/doc/table_of_contents.html +221 -72
- data/lib/standup_md.rb +27 -544
- data/lib/standup_md/cli.rb +63 -246
- data/lib/standup_md/cli/helpers.rb +165 -0
- data/lib/standup_md/config.rb +45 -0
- data/lib/standup_md/config/cli.rb +106 -0
- data/lib/standup_md/config/entry.rb +61 -0
- data/lib/standup_md/config/entry_list.rb +26 -0
- data/lib/standup_md/config/file.rb +199 -0
- data/lib/standup_md/entry.rb +121 -0
- data/lib/standup_md/entry_list.rb +166 -0
- data/lib/standup_md/file.rb +172 -0
- data/lib/standup_md/file/helpers.rb +62 -0
- data/lib/standup_md/version.rb +5 -3
- data/standup_md.gemspec +1 -0
- metadata +35 -2
@@ -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,172 @@
|
|
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
|
+
# Find standup file in directory by file name.
|
24
|
+
#
|
25
|
+
# @param [String] File_naem
|
26
|
+
def self.find(file_name)
|
27
|
+
file = Dir.entries(config.directory).bsearch { |f| f == file_name }
|
28
|
+
if file.nil? && !config.create
|
29
|
+
raise "File #{file_name} not found." unless config.create
|
30
|
+
end
|
31
|
+
new(file_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Find standup file in directory by Date object.
|
36
|
+
#
|
37
|
+
# @param [Date] date
|
38
|
+
def self.find_by_date(date)
|
39
|
+
unless date.is_a?(Date)
|
40
|
+
raise ArgumentError, "Argument must be a Date object"
|
41
|
+
end
|
42
|
+
find(date.strftime(config.name_format))
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# The list of entries in the file.
|
47
|
+
#
|
48
|
+
# @return [StandupMP::EntryList]
|
49
|
+
attr_reader :entries
|
50
|
+
|
51
|
+
##
|
52
|
+
# The name of the file.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
attr_reader :name
|
56
|
+
|
57
|
+
##
|
58
|
+
# Constructs the instance.
|
59
|
+
#
|
60
|
+
# @param [String] file_name
|
61
|
+
#
|
62
|
+
# @return [StandupMP::File]
|
63
|
+
def initialize(file_name)
|
64
|
+
@config = self.class.config
|
65
|
+
if file_name.include?(::File::SEPARATOR)
|
66
|
+
raise ArgumentError,
|
67
|
+
"#{file_name} contains directory. Please use `StandupMD.config.file.directory=`"
|
68
|
+
end
|
69
|
+
|
70
|
+
unless ::File.directory?(@config.directory)
|
71
|
+
raise "Dir #{@config.directory} not found." unless @config.create
|
72
|
+
FileUtils.mkdir_p(@config.directory)
|
73
|
+
end
|
74
|
+
|
75
|
+
@name = ::File.expand_path(::File.join(@config.directory, file_name))
|
76
|
+
|
77
|
+
unless ::File.file?(@name)
|
78
|
+
raise "File #{@name} not found." unless @config.create
|
79
|
+
FileUtils.touch(@name)
|
80
|
+
end
|
81
|
+
|
82
|
+
@new = ::File.zero?(@name)
|
83
|
+
@loaded = false
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Was the file just now created?
|
88
|
+
#
|
89
|
+
# @return [Boolean] true if new
|
90
|
+
def new?
|
91
|
+
@new
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Has the file been loaded?
|
96
|
+
#
|
97
|
+
# @return [Boolean] true if loaded
|
98
|
+
def loaded?
|
99
|
+
@loaded
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Does the file exist?
|
104
|
+
#
|
105
|
+
# @return [Boolean] true if exists
|
106
|
+
def exist?
|
107
|
+
::File.exist?(name)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Loads the file's contents.
|
112
|
+
# TODO clean up this method.
|
113
|
+
#
|
114
|
+
# @return [StandupMD::FileList]
|
115
|
+
def load
|
116
|
+
raise "File #{name} does not exist." unless ::File.file?(name)
|
117
|
+
entry_list = EntryList.new
|
118
|
+
record = {}
|
119
|
+
section_type = ''
|
120
|
+
::File.foreach(name) do |line|
|
121
|
+
line.chomp!
|
122
|
+
next if line.strip.empty?
|
123
|
+
if is_header?(line)
|
124
|
+
unless record.empty?
|
125
|
+
entry_list << new_entry(record)
|
126
|
+
record = {}
|
127
|
+
end
|
128
|
+
record['header'] = line.sub(%r{^\#{#{@config.header_depth}}\s*}, '')
|
129
|
+
section_type = @config.notes_header
|
130
|
+
record[section_type] = []
|
131
|
+
elsif is_sub_header?(line)
|
132
|
+
section_type = determine_section_type(line)
|
133
|
+
record[section_type] = []
|
134
|
+
else
|
135
|
+
record[section_type] << line.sub(bullet_character_regex, '')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
entry_list << new_entry(record) unless record.empty?
|
139
|
+
@loaded = true
|
140
|
+
@entries = entry_list.sort
|
141
|
+
rescue => e
|
142
|
+
raise "File malformation: #{e}"
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Writes a new entry to the file if the first entry in the file isn't today.
|
147
|
+
# This method is destructive; if a file for entries in the date range
|
148
|
+
# already exists, it will be clobbered with the entries in the range.
|
149
|
+
#
|
150
|
+
# @param [Hash] start_and_end_date
|
151
|
+
#
|
152
|
+
# @return [Boolean] true if successful
|
153
|
+
def write(dates = {})
|
154
|
+
sorted_entries = entries.sort
|
155
|
+
start_date = dates.fetch(:start_date, sorted_entries.first.date)
|
156
|
+
end_date = dates.fetch(:end_date, sorted_entries.last.date)
|
157
|
+
::File.open(name, 'w') do |f|
|
158
|
+
sorted_entries.filter(start_date, end_date).sort_reverse.each do |entry|
|
159
|
+
f.puts header(entry.date)
|
160
|
+
@config.sub_header_order.each do |attr|
|
161
|
+
tasks = entry.send(attr)
|
162
|
+
next if !tasks || tasks.empty?
|
163
|
+
f.puts sub_header(@config.send("#{attr}_header").capitalize)
|
164
|
+
tasks.each { |task| f.puts @config.bullet_character + ' ' + task }
|
165
|
+
end
|
166
|
+
f.puts
|
167
|
+
end
|
168
|
+
end
|
169
|
+
true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
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
|
data/lib/standup_md/version.rb
CHANGED
data/standup_md.gemspec
CHANGED
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.
|
4
|
+
version: 0.3.0
|
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-
|
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,6 +83,15 @@ files:
|
|
69
83
|
- doc/README_md.html
|
70
84
|
- doc/StandupMD.html
|
71
85
|
- doc/StandupMD/Cli.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
|
72
95
|
- doc/created.rid
|
73
96
|
- doc/css/fonts.css
|
74
97
|
- doc/css/rdoc.css
|
@@ -115,6 +138,16 @@ files:
|
|
115
138
|
- doc/table_of_contents.html
|
116
139
|
- lib/standup_md.rb
|
117
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
|
118
151
|
- lib/standup_md/version.rb
|
119
152
|
- standup_md.gemspec
|
120
153
|
homepage: https://evanthegrayt.github.io/standup_md/
|