standup_md 1.0.1 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74c06f0aa4db099f1e64131fdd7e184dd8270db7ee99dd97f9c2cd97374ccef1
4
- data.tar.gz: 71e038fce40a7f972b058baee23a7cfbbc1b2eabdbb8f0ccee466ea6fdf70345
3
+ metadata.gz: 47c9542a3be072fc9067823aeeaf6c41741e657f7c628476cf1811c3699978f0
4
+ data.tar.gz: 165f18178d4e7038f2c2511e0b7e0e907671c6819f9158fc601c29ebd18ac3be
5
5
  SHA512:
6
- metadata.gz: 6321c1cf53d23f8a2cd05f191f87059bc9ac308951373ab391ef53c844289098b1edcd4fcb6ef107d7f5bea9f124f7cbd105d75c3d40c2f3fe689427be783264
7
- data.tar.gz: 8cefeb0ac1a75a8313ebdaabed8edfdf5b928b73c625c3f3bafebe1b330fafe6d44de2ebc26aee9379ce811b376eac601edd23dc4870c6fafc9b5acc0babf1aa
6
+ metadata.gz: e56ad3a3e3e633f235af7078708ebb27f1a184cd6a0bd94a1b52299e82de2b75842f169e9495003f26c49907a9f0eb9b5c8d52511bf6cc39e1978f60848a2ca8
7
+ data.tar.gz: 043a1a0119e0921196840fae06143f33a87dbbe4e9b62c381c671defd1695c6e1d5694174872a15932b63c42119dd19764cff992cd3c0f456220f0c9474d15d6
@@ -10,6 +10,8 @@ on:
10
10
  jobs:
11
11
  test:
12
12
  runs-on: ubuntu-22.04
13
+ env:
14
+ BUNDLER_VERSION: 4.0.10
13
15
 
14
16
  strategy:
15
17
  fail-fast: false
@@ -17,14 +19,13 @@ jobs:
17
19
  ruby-version: ['3.2', '3.3', '4.0']
18
20
 
19
21
  steps:
20
- - uses: actions/checkout@v3
22
+ - uses: actions/checkout@v4
21
23
 
22
- # Set up Ruby and Bundler
23
24
  - name: Set up Ruby ${{ matrix.ruby-version }}
24
25
  uses: ruby/setup-ruby@v1
25
26
  with:
26
27
  ruby-version: ${{ matrix.ruby-version }}
27
- bundler: ${{ matrix.ruby-version == '4.0' && '4.0.9' || '2.4.13' }}
28
+ bundler: ${{ env.BUNDLER_VERSION }}
28
29
  bundler-cache: true
29
30
 
30
31
  - name: Print Ruby and Bundler versions
@@ -32,8 +33,5 @@ jobs:
32
33
  ruby -v
33
34
  bundle -v
34
35
 
35
- - name: Install dependencies
36
- run: bundle install --jobs 4 --retry 3
37
-
38
36
  - name: Run tests
39
37
  run: bundle exec rake
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- standup_md (1.0.1)
4
+ standup_md (2.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 (1.0.1)
118
+ standup_md (2.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
@@ -14,8 +14,8 @@ You can view the documentation
14
14
  [here](https://evanthegrayt.github.io/standup_md/).
15
15
 
16
16
  ## About
17
- I've now been at two separate companies where we post our daily standups in a
18
- chat client, such as Slack, Mattermost, or Riot. Typing out my standup every day
17
+ I've now been at multiple companies where we post our daily standups in a chat
18
+ client, such as Slack, Mattermost, or Riot. Typing out my standup every day
19
19
  became tedious, as I'd have to look up what I did the day before, copy and paste
20
20
  yesterday's work into a new entry, and add today's tasks. This gem automates
21
21
  most of this process, along with providing means of opening the file in your
@@ -30,7 +30,9 @@ entry exists, it will be added to today's entry as your previous day's work. See
30
30
  this in your own code somehow.
31
31
 
32
32
  ## Installation
33
- If you don't have the permissions to install system-wide gems, you're probabaly
33
+ Requires Ruby 3.2 or newer.
34
+
35
+ If you don't have the permissions to install system-wide gems, you're probably
34
36
  also running an older version of ruby. I recommend installing
35
37
  [rbenv](https://github.com/rbenv/rbenv#installation), and then installing an
36
38
  up-to-date version of ruby.
@@ -132,6 +134,28 @@ your clipboard without even opening your editor.
132
134
  standup -p | pbcopy
133
135
  ```
134
136
 
137
+ #### Post the entry for today to Slack
138
+ You can also post today's entry directly to a chat client. Slack is the default
139
+ adapter, so `standup -P` and `standup -P slack` are equivalent. Tokens are read
140
+ from environment variables at post time, and are not stored in `~/.standuprc`.
141
+ You can set a default channel via the
142
+ [config](#available-config-file-options-and-defaults) via
143
+ `c.post.configure_adapter(:slack, channel: "C123456")`.
144
+
145
+ ```sh
146
+ export STANDUP_MD_SLACK_TOKEN="xoxb-your-token"
147
+ standup -P slack --post-channel C123456
148
+ ```
149
+
150
+ The same posting path is available from Ruby:
151
+
152
+ ```ruby
153
+ file = StandupMD::File.find_by_date(Date.today).load
154
+ entry = file.entries.find(Date.today)
155
+
156
+ StandupMD::Post.post(entry, adapter: :slack, channel: "C123456")
157
+ ```
158
+
135
159
  #### Add entry to file without opening it
136
160
  You can add an entry for today without even opening your editor. Note that, if
137
161
  you have multiple entries, you must separate them with a comma and *no spaces*.
@@ -142,17 +166,17 @@ standup --no-edit --current "Work on this thing","And another thing"
142
166
 
143
167
  ### Customization and Runtime Options
144
168
  You can create a file in your home directory called `~/.standuprc`. Settings
145
- located in this file will override default behavior. This file can also have
146
- settings overwritten at runtime by the use of options. You can view [my config
147
- file](https://github.com/evanthegrayt/dotfiles/blob/master/dotfiles/standuprc)
148
- as an example. Any setting in this file can still be overridden at runtime by
149
- passing flags to the executable.
169
+ located in this file define your application defaults. CLI flags can override
170
+ those defaults for a single invocation, but they do not persist back into
171
+ `StandupMD.config` or `~/.standuprc`. You can view
172
+ [my config file](https://github.com/evanthegrayt/dotfiles/blob/master/dotfiles/standuprc)
173
+ as an example.
150
174
 
151
175
  You'll notice, a lot of settings don't have the ability to be changed at runtime
152
- when calling the executable. This is because the file structure is very
176
+ when calling the executable. This is because the markdown structure is very
153
177
  important, and changing values that affect formatting will cause problems with
154
- the file parser. If you don't want to use a default, make the change in your
155
- config file before you start editing standups.
178
+ the markdown parser. If you don't want to use a default, make the change in
179
+ your config file before you start editing standups.
156
180
 
157
181
  #### Available Config File Options and Defaults
158
182
  For command-line usage, this file needs to be named `~/.standuprc`. To use in a
@@ -161,7 +185,7 @@ rails project, create an initializer (`config/initializers/standup_md.rb`).
161
185
  ```ruby
162
186
  StandupMD.configure do |c|
163
187
  # Defaults for how the file is formatted.
164
- # See https://evanthegrayt.github.io/standup_md/doc/StandupMD/Config/Cli.html
188
+ # See https://evanthegrayt.github.io/standup_md/doc/StandupMD/Config/File.html
165
189
  c.file.header_date_format = "%Y-%m-%d"
166
190
  c.file.header_depth = 1
167
191
  c.file.sub_header_depth = 2
@@ -191,38 +215,143 @@ StandupMD.configure do |c|
191
215
  c.cli.edit = true
192
216
  c.cli.write = true
193
217
  c.cli.print = false
218
+ c.cli.post = false
219
+ c.cli.post_adapter = nil
220
+ c.cli.post_channel = nil
194
221
  c.cli.auto_fill_previous = true
195
222
  c.cli.preference_file = ::File.expand_path(::File.join(ENV["HOME"], ".standuprc"))
223
+
224
+ # Defaults for posting standups to chat clients.
225
+ c.post.default_adapter = :slack
226
+ c.post.title = nil
227
+ c.post.configure_adapter(
228
+ :slack,
229
+ channel: "C123456",
230
+ token_env: "STANDUP_MD_SLACK_TOKEN"
231
+ )
196
232
  end
197
233
  ```
198
234
 
199
235
  Any options not set in this file will retain their default values. Note that if
200
- you change `file_name_format`, and don't use a month or year, there will only
236
+ you change `name_format`, and don't use a month or year, there will only
201
237
  ever be one standup file. This could cause issues long-term, as the files will
202
238
  get large over time and possibly cause performance issues.
203
239
 
204
240
 
205
241
  #### Executable Flags
206
- Some of these options can be changed at runtime. They are as follows.
242
+ Some defaults can be overridden for a single CLI invocation. They are as
243
+ follows.
207
244
 
208
- ```
245
+ ```text
209
246
  --current ARRAY List of current entry's tasks
210
247
  --previous ARRAY List of previous entry's tasks
211
248
  --impediments ARRAY List of impediments for current entry
212
249
  --notes ARRAY List of notes for current entry
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
215
- -f, --file-name-format STRING Date-formattable string to use for standup file name
216
250
  -E, --editor EDITOR Editor to use for opening standup files
217
- -d, --directory DIRECTORY The directories where standup files are located
218
- -w --[no-]write Write current entry if it doesn't exist. Default is true
219
- -a --[no-]auto-fill-previous Auto-generate 'previous' tasks for new entries
220
- -e --[no-]edit Open the file in the editor. Default is true
251
+ -d, --directory DIRECTORY The directory where standup files are located
252
+ -w, --[no-]write Write current entry if it doesn't exist. Default is true
253
+ -a, --[no-]auto-fill-previous Auto-generate 'previous' tasks for new entries
254
+ -e, --[no-]edit Open the file in the editor. Default is true
221
255
  -v, --[no-]verbose Verbose output. Default is false.
222
256
  --zsh-completion Print zsh completion setup instructions
223
257
  -p, --print [DATE] Print current entry.
224
258
  If DATE is passed, will print entry for DATE, if it exists.
225
- DATE must be in the same format as file-name-format
259
+ DATE must be in the same format as the entry header date.
260
+ -P, --post [PLATFORM] Post current entry to a chat client. Defaults to Slack.
261
+ If PLATFORM is passed, use that post adapter.
262
+ --post-channel CHANNEL Channel, room, or conversation to post to
263
+ ```
264
+
265
+ #### Posting and Secrets
266
+ The built-in Slack adapter sends the rendered markdown entry to Slack's
267
+ `chat.postMessage` API. It needs a Slack token with the `chat:write` scope and a
268
+ channel or conversation. Channel-like IDs such as `C123456`, `G123456`, and
269
+ `D123456` are the most reliable values to use. By default, the token is read
270
+ from `STANDUP_MD_SLACK_TOKEN`.
271
+
272
+ The recommended pattern is to keep secret values in the environment and store
273
+ only non-secret adapter defaults in `~/.standuprc`:
274
+
275
+ ```ruby
276
+ StandupMD.configure do |c|
277
+ c.post.configure_adapter(:slack, channel: "C123456")
278
+ end
279
+ ```
280
+
281
+ Most chat clients now prefer messages to come from an installed app or bot
282
+ instead of a long-lived user token. That keeps permissions clearer, but it also
283
+ means the visible sender might be a general name like "StandupMD" rather than
284
+ the person whose update is being posted. Set `c.post.title` to identify the
285
+ standup owner in the message title without changing the markdown files that
286
+ StandupMD parses.
287
+
288
+ ```ruby
289
+ StandupMD.configure do |c|
290
+ c.post.title = "%s - Evan Gray"
291
+ end
292
+ ```
293
+
294
+ The `%s` placeholder is replaced with the normal entry title, usually the entry
295
+ date, so a stored `# 2026-06-27` entry posts as
296
+ `# 2026-06-27 - Evan Gray`.
297
+
298
+ To use a different token variable, set `token_env`.
299
+
300
+ ```ruby
301
+ StandupMD.configure do |c|
302
+ c.post.configure_adapter(
303
+ :slack,
304
+ channel: "C123456",
305
+ token_env: "WORK_SLACK_TOKEN"
306
+ )
307
+ end
308
+ ```
309
+
310
+ #### Custom Post Adapters
311
+ Adapters are registered in `~/.standuprc`. They receive a
312
+ `StandupMD::Post::Message`, which includes the rendered markdown text and the
313
+ channel passed through `StandupMD::Post.post` or `--post-channel`.
314
+
315
+ ```ruby
316
+ class TeamsAdapter
317
+ def initialize(options = {})
318
+ @options = options
319
+ end
320
+
321
+ def post(message)
322
+ channel = message.channel || @options[:channel]
323
+ token = ENV.fetch("TEAMS_TOKEN")
324
+
325
+ # Send message.text to channel with token.
326
+
327
+ StandupMD::Post::Result.success(
328
+ adapter: message.adapter,
329
+ channel: channel
330
+ )
331
+ end
332
+ end
333
+
334
+ StandupMD.configure do |c|
335
+ c.post.register_adapter(:teams, TeamsAdapter)
336
+ c.post.configure_adapter(:teams, channel: "team-channel-id")
337
+ end
338
+ ```
339
+
340
+ Custom adapters can be used from either the CLI or the Ruby API:
341
+
342
+ ```ruby
343
+ StandupMD::Post.post(entry, adapter: :teams, channel: "team-channel-id")
344
+ ```
345
+
346
+ For request-scoped API usage, copy the global defaults and pass the copy into
347
+ the operation:
348
+
349
+ ```ruby
350
+ runtime = StandupMD.config.copy
351
+ runtime.post.default_adapter = :teams
352
+ runtime.post.configure_adapter(:teams, channel: "team-channel-id")
353
+
354
+ StandupMD::Post.post(entry, config: runtime)
226
355
  ```
227
356
 
228
357
  #### Using Existing Standup Files
@@ -242,8 +371,8 @@ they must be in a format that the parser can understand. The default is:
242
371
  ```
243
372
 
244
373
  The order, words, date format, and header level are all customizable, but the
245
- overall format must be the same. If customization is necessary, this must be
246
- done in `~/.standuprc` before execution, or else the parser will error.
374
+ overall format must be the same. If customization is necessary, set the defaults
375
+ or pass a runtime config before reading the file, or else the parser will error.
247
376
 
248
377
  For example, if you wanted the format to be as follows:
249
378
 
@@ -279,9 +408,30 @@ end
279
408
  The API is fully documented in the
280
409
  [RDoc Documentation](https://evanthegrayt.github.io/standup_md/).
281
410
 
282
- This was mainly written as a command line utility, but the API is very robust,
283
- and is available for use in your own projects. A quick example of how to write a
284
- new entry via code could look like the following:
411
+ This was mainly written as a command line utility, but the API is available for
412
+ use in your own projects. `StandupMD.config` stores application defaults. For
413
+ web requests, jobs, or any other multi-call environment, copy those defaults and
414
+ pass the copy into the operation you are running.
415
+
416
+ `StandupMD::File` handles reading and writing files on disk. The markdown parser
417
+ handles markdown strings:
418
+
419
+ ```ruby
420
+ parser = StandupMD::Parsers::Markdown.new
421
+ entries = parser.parse(File.read("2026_06.md"))
422
+ markdown = parser.render(entries, start_date: entries.first.date, end_date: entries.last.date)
423
+ ```
424
+
425
+ ```ruby
426
+ runtime = StandupMD.config.copy
427
+ runtime.file.directory = "/tmp/request-standups"
428
+ runtime.entry.current = ["Work scoped to this request"]
429
+
430
+ file = StandupMD::File.find_by_date(Date.today, config: runtime.file).load
431
+ entry = StandupMD::Entry.create(config: runtime.entry)
432
+ file.entries << entry
433
+ file.write
434
+ ```
285
435
 
286
436
  ### API Examples
287
437
  #### Adding an entry for today
@@ -289,27 +439,29 @@ new entry via code could look like the following:
289
439
  require "standup_md"
290
440
 
291
441
  StandupMD.configure do |c|
292
- c.file.current_header = "Today",
442
+ c.file.current_header = "Today"
293
443
  end
294
444
 
295
- file = StandupMD::File.find_by_date(Date.today)
296
- entry = StandupMD::Entry.create { |e| e.current = ["Stuff I will do today"] }
445
+ file = StandupMD::File.find_by_date(Date.today).load
446
+ entry = StandupMD::Entry.create(current: ["Stuff I will do today"])
297
447
  file.entries << entry
298
448
  file.write
299
449
  ```
300
450
 
301
- The above example was written as such to show how the different pieces of the
302
- API fit together. The code can actually be simplified to the following.
451
+ The above example uses global defaults. To keep runtime choices scoped to one
452
+ request, copy the defaults and pass the copy into each operation.
303
453
 
304
454
  ```ruby
305
455
  require "standup_md"
306
456
 
307
- StandupMD.configure do |c|
308
- c.file.current_header = "Today",
309
- c.entry.current = ["Stuff I will do today"]
310
- end
457
+ runtime = StandupMD.config.copy
458
+ runtime.file.current_header = "Today"
459
+ runtime.entry.current = ["Stuff I will do today"]
311
460
 
312
- StandupMD::File.find_by_date(Date.today).load.write
461
+ file = StandupMD::File.find_by_date(Date.today, config: runtime.file).load
462
+ entry = StandupMD::Entry.create(config: runtime.entry)
463
+ file.entries << entry
464
+ file.write
313
465
  ```
314
466
 
315
467
  #### Finding a past entry
@@ -349,7 +501,7 @@ command, that file will be opened. There's tab completion for this. Lastly,
349
501
  it allows for a few variables to be set for customization.
350
502
 
351
503
  ```vim
352
- g:standup_dir = $HOME . '/.cache/standup_md' " the directory where your file are
504
+ g:standup_dir = $HOME . '/.cache/standup_md' " the directory where your files are
353
505
  g:standup_file = strftime('%Y_%m.md') " the file format to use
354
506
  ```
355
507
 
@@ -4,19 +4,6 @@ _standup_array_values() {
4
4
  _message -e values 'comma-separated list'
5
5
  }
6
6
 
7
- _standup_sub_header_order() {
8
- local -a headers
9
-
10
- headers=(
11
- 'previous:previous entry tasks'
12
- 'current:current entry tasks'
13
- 'impediments:current entry impediments'
14
- 'notes:current entry notes'
15
- )
16
-
17
- _values -s , 'sub-header' $headers
18
- }
19
-
20
7
  _standup_dates() {
21
8
  local directory="${STANDUP_MD_DIR:-$HOME/.cache/standup_md}"
22
9
  local index
@@ -53,6 +40,16 @@ _standup_dates() {
53
40
  fi
54
41
  }
55
42
 
43
+ _standup_post_adapters() {
44
+ local -a adapters
45
+
46
+ adapters=(
47
+ 'slack:Slack chat.postMessage adapter'
48
+ )
49
+
50
+ _describe -t adapters 'post adapter' adapters
51
+ }
52
+
56
53
  _arguments -s -S \
57
54
  '(- *)'{-h,--help}'[show help]' \
58
55
  '(- *)--version[show version]' \
@@ -61,10 +58,6 @@ _arguments -s -S \
61
58
  '--previous[List of previous entry tasks]:tasks:_standup_array_values' \
62
59
  '--impediments[List of impediments for current entry]:impediments:_standup_array_values' \
63
60
  '--notes[List of notes for current entry]:notes:_standup_array_values' \
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:' \
66
- '(-f --file-name-format)-f[Date-formattable string to use for standup file name]:strftime format:' \
67
- '(-f --file-name-format)--file-name-format[Date-formattable string to use for standup file name]:strftime format:' \
68
61
  '(-E --editor)-E[Editor to use for opening standup files]:editor:_path_commands' \
69
62
  '(-E --editor)--editor[Editor to use for opening standup files]:editor:_path_commands' \
70
63
  '(-d --directory)-d[The directory where standup files are located]:directory:_directories' \
@@ -78,4 +71,6 @@ _arguments -s -S \
78
71
  '(-v --verbose --no-verbose)'{-v,--verbose}'[use verbose output]' \
79
72
  '(-v --verbose --no-verbose)--no-verbose[disable verbose output]' \
80
73
  '(-p --print)'{-p,--print}'[print current entry]::date:_standup_dates' \
74
+ '(-P --post)'{-P,--post}'[post current entry to a chat client]::platform:_standup_post_adapters' \
75
+ '--post-channel[channel, room, or conversation to post to]:channel:' \
81
76
  '1:standup file date:_standup_dates'
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "standup_md/parsers/markdown"
4
+ require "standup_md/post"
4
5
 
5
6
  module StandupMD
6
7
  class Cli
7
8
  ##
8
- # Module responsible for reading and writing standup files.
9
+ # Helpers for CLI commands and option handling.
9
10
  module Helpers
10
11
  ##
11
12
  # Print an entry to the command line.
@@ -16,17 +17,26 @@ module StandupMD
16
17
  def print(entry)
17
18
  return puts "No record found for #{config.cli.date}" if entry.nil?
18
19
 
19
- puts header(entry)
20
- config.file.sub_header_order.each do |header_type|
21
- tasks = entry.public_send("#{header_type}_tasks")
22
- next if tasks.empty?
20
+ $stdout.print markdown.render_entry(entry)
21
+ end
23
22
 
24
- puts sub_header(header_type)
25
- tasks.each do |task|
26
- puts parser.task_line(task)
27
- end
28
- end
29
- puts
23
+ ##
24
+ # Post an entry to the configured chat adapter.
25
+ #
26
+ # @param [StandupMD::Entry] entry
27
+ #
28
+ # @return [StandupMD::Post::Result, nil]
29
+ def post(entry)
30
+ return puts "No record found for #{config.cli.date}" if entry.nil?
31
+
32
+ result = StandupMD::Post.post(
33
+ entry,
34
+ adapter: config.cli.post_adapter,
35
+ channel: config.cli.post_channel,
36
+ config: config
37
+ )
38
+ puts "Could not post to #{result.adapter}: #{result.error}" if result.failure?
39
+ result
30
40
  end
31
41
 
32
42
  private
@@ -36,12 +46,12 @@ module StandupMD
36
46
  #
37
47
  # @return [StandupMD::Config]
38
48
  def config # :nodoc:
39
- StandupMD.config
49
+ @config
40
50
  end
41
51
 
42
52
  ##
43
- # Parses options passed at runtime and concatenates them with the options
44
- # in the user's preferences file. Reveal source to see options.
53
+ # Parses options passed at runtime into this CLI invocation's config
54
+ # snapshot. Reveal source to see options.
45
55
  #
46
56
  # @return [Hash]
47
57
  def load_runtime_preferences(options)
@@ -68,21 +78,6 @@ module StandupMD
68
78
  "List of notes for current entry"
69
79
  ) { |v| config.entry.notes = v }
70
80
 
71
- opts.on(
72
- "--sub-header-order ARRAY", Array,
73
- "The order of the sub-headers when writing the file"
74
- ) { |v| config.file.sub_header_order = v }
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
-
81
- opts.on(
82
- "-f", "--file-name-format STRING",
83
- "Date-formattable string to use for standup file name"
84
- ) { |v| config.file.name_format = v }
85
-
86
81
  opts.on(
87
82
  "-E", "--editor EDITOR",
88
83
  "Editor to use for opening standup files"
@@ -90,7 +85,7 @@ module StandupMD
90
85
 
91
86
  opts.on(
92
87
  "-d", "--directory DIRECTORY",
93
- "The directories where standup files are located"
88
+ "The directory where standup files are located"
94
89
  ) { |v| config.file.directory = v }
95
90
 
96
91
  opts.on(
@@ -122,12 +117,26 @@ module StandupMD
122
117
  "-p", "--print [DATE]",
123
118
  "Print current entry.",
124
119
  "If DATE is passed, will print entry for DATE, if it exists.",
125
- "DATE must be in the same format as file-name-format"
120
+ "DATE must be in the same format as the entry header date."
126
121
  ) do |v|
127
122
  config.cli.print = true
128
123
  config.cli.date =
129
124
  v.nil? ? Date.today : Date.strptime(v, config.file.header_date_format)
130
125
  end
126
+
127
+ opts.on(
128
+ "-P", "--post [PLATFORM]",
129
+ "Post current entry to a chat client. Defaults to Slack.",
130
+ "If PLATFORM is passed, use that post adapter."
131
+ ) do |v|
132
+ config.cli.post = true
133
+ config.cli.post_adapter = v.nil? ? config.post.default_adapter : v.to_sym
134
+ end
135
+
136
+ opts.on(
137
+ "--post-channel CHANNEL",
138
+ "Channel, room, or conversation to post to"
139
+ ) { |v| config.cli.post_channel = v }
131
140
  end.parse!(options)
132
141
  if zsh_completion_requested?
133
142
  raise OptionParser::InvalidArgument, options.join(" ") unless options.empty?
@@ -165,7 +174,10 @@ module StandupMD
165
174
  # @return [Array]
166
175
  def previous_entry(file)
167
176
  return config.entry.previous unless config.cli.auto_fill_previous
168
- return prev_entry_tasks(prev_file.load.entries) if file.new? && prev_file
177
+ if file.new?
178
+ previous_file = prev_file_exists?
179
+ return prev_entry_tasks(previous_file.load.entries) if previous_file
180
+ end
169
181
 
170
182
  prev_entry_tasks(file.entries)
171
183
  end
@@ -202,34 +214,24 @@ module StandupMD
202
214
  ##
203
215
  # The previous month's file.
204
216
  #
217
+ # @param [StandupMD::Config::File] config
218
+ #
205
219
  # @return [StandupMD::File]
206
- def prev_file
207
- StandupMD::File.find_by_date(Date.today.prev_month)
220
+ def prev_file(config: self.config.file)
221
+ StandupMD::File.find_by_date(Date.today.prev_month, config: config)
208
222
  end
209
223
 
210
- ##
211
- # The header.
212
- #
213
- # @param [StandupMD::Entry] entry
214
- #
215
- # @return [String]
216
- def header(entry)
217
- "#" * config.file.header_depth + " " +
218
- entry.date.strftime(config.file.header_date_format)
224
+ def prev_file_exists?
225
+ without_file_creation { |file_config| prev_file(config: file_config) }
226
+ rescue StandupMD::File::NotFoundError
227
+ nil
219
228
  end
220
229
 
221
230
  ##
222
- # The sub-header.
231
+ # Markdown renderer used for CLI output.
223
232
  #
224
- # @param [String] header_type
225
- #
226
- # @return [String]
227
- def sub_header(header_type)
228
- "#" * config.file.sub_header_depth + " " +
229
- config.file.public_send("#{header_type}_header")
230
- end
231
-
232
- def parser
233
+ # @return [StandupMD::Parsers::Markdown]
234
+ def markdown
233
235
  StandupMD::Parsers::Markdown.new(config.file)
234
236
  end
235
237
  end