howzit 2.1.30 → 2.1.32
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/CHANGELOG.md +29 -0
- data/bin/howzit +6 -1
- data/lib/howzit/buildnote.rb +25 -7
- data/lib/howzit/config.rb +8 -0
- data/lib/howzit/script_support.rb +94 -1
- data/lib/howzit/stringutils.rb +47 -5
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +6 -1
- data/spec/buildnote_spec.rb +33 -4
- data/spec/stringutils_spec.rb +41 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 816754b14a92ca32bab9ae753a03926d0163e2af860164032b7d1fdfff58f716
|
|
4
|
+
data.tar.gz: 32fb6fee068779ba312051bd2613c8d47e24777760ecce28da796f2c35899fdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2741d38466727329dc19755f910acc88a67068b66c1665256365fecffd31ed64a9d0040b97f26140444676a74bec53b1e33c96048dd9b7044b8aecafa0e3a845
|
|
7
|
+
data.tar.gz: fbe6e5aa03de0eadbc3d4af7a34d67b0a1628d0926dbf0d908ab9ae8f49b31236b5b6d89d5433ef88785e0d78a9066a969ad7a757b5c81e33f7ada0a923bbc24
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
### 2.1.32
|
|
2
|
+
|
|
3
|
+
2026-01-06 05:21
|
|
4
|
+
|
|
5
|
+
#### IMPROVED
|
|
6
|
+
|
|
7
|
+
- Completion output for howzit -L and howzit -T now shows topic titles without parenthetical variable names, making completion lists cleaner and easier to read while still preserving variable information in verbose output (howzit -R).
|
|
8
|
+
|
|
9
|
+
### 2.1.31
|
|
10
|
+
|
|
11
|
+
2026-01-06 04:57
|
|
12
|
+
|
|
13
|
+
#### CHANGED
|
|
14
|
+
|
|
15
|
+
- Updated Howzit to use XDG_CONFIG_HOME/howzit (or ~/.config/howzit if XDG_CONFIG_HOME is not set) for all configuration files, templates, themes, and script support files instead of ~/.local/share/howzit.
|
|
16
|
+
|
|
17
|
+
#### NEW
|
|
18
|
+
|
|
19
|
+
- Added automatic migration prompt that detects existing ~/.local/share/howzit directory and offers to migrate all files to the new config location, merging contents and overwriting existing files in the new location while preserving files that only exist in the new location.
|
|
20
|
+
- Added --migrate flag to explicitly trigger migration of legacy ~/.local/share/howzit directory to the new config location.
|
|
21
|
+
|
|
22
|
+
#### IMPROVED
|
|
23
|
+
|
|
24
|
+
- Migration prompt now appears during config initialization to catch legacy directories before creating new config files, preventing confusion about file locations.
|
|
25
|
+
|
|
26
|
+
#### FIXED
|
|
27
|
+
|
|
28
|
+
- Fixed ArgumentError when topic titles were longer than terminal width by ensuring horizontal rule width calculation never goes negative, clamping to zero when title exceeds available space.
|
|
29
|
+
|
|
1
30
|
### 2.1.30
|
|
2
31
|
|
|
3
32
|
2026-01-06 03:55
|
data/bin/howzit
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
1
|
+
#!/usr/bin/env ruby -W0
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
|
@@ -248,6 +248,11 @@ OptionParser.new do |opts|
|
|
|
248
248
|
Process.exit 0
|
|
249
249
|
end
|
|
250
250
|
|
|
251
|
+
opts.on('--migrate', 'Migrate legacy files from ~/.local/share/howzit to the new config location') do
|
|
252
|
+
Howzit::ScriptSupport.migrate_legacy_support(early_init: false)
|
|
253
|
+
Process.exit 0
|
|
254
|
+
end
|
|
255
|
+
|
|
251
256
|
opts.on('-v', '--version', 'Display version number') do
|
|
252
257
|
puts "#{File.basename(__FILE__)} v#{Howzit::VERSION}"
|
|
253
258
|
Process.exit 0
|
data/lib/howzit/buildnote.rb
CHANGED
|
@@ -21,9 +21,31 @@ module Howzit
|
|
|
21
21
|
content = Util.read_file(file)
|
|
22
22
|
raise "{br}No content found in build note (#{file}){x}".c if content.nil? || content.empty?
|
|
23
23
|
|
|
24
|
+
# Global metadata from config (e.g. ~/.config/howzit/howzit.yml)
|
|
25
|
+
# Expecting something like:
|
|
26
|
+
# metadata:
|
|
27
|
+
# author: Brett Terpstra
|
|
28
|
+
# license: MIT
|
|
29
|
+
global_meta = {}
|
|
30
|
+
raw_global = Howzit.options[:metadata] || Howzit.options['metadata']
|
|
31
|
+
if raw_global.is_a?(Hash)
|
|
32
|
+
raw_global.each do |k, v|
|
|
33
|
+
global_meta[k.to_s.downcase] = v
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Metadata defined in the build note itself (before the first heading)
|
|
24
38
|
this_meta = content.split(/^#/)[0].strip.metadata
|
|
25
39
|
|
|
26
|
-
|
|
40
|
+
# Merge order:
|
|
41
|
+
# 1. Global config metadata (lowest precedence)
|
|
42
|
+
# 2. Metadata passed in via meta argument (e.g. templates)
|
|
43
|
+
# 3. Build note metadata (highest precedence)
|
|
44
|
+
combined_meta = global_meta.dup
|
|
45
|
+
combined_meta.merge!(meta) if meta
|
|
46
|
+
combined_meta.merge!(this_meta) if this_meta
|
|
47
|
+
|
|
48
|
+
@metadata = combined_meta
|
|
27
49
|
|
|
28
50
|
read_help(file)
|
|
29
51
|
end
|
|
@@ -146,9 +168,7 @@ module Howzit
|
|
|
146
168
|
##
|
|
147
169
|
def list_topics
|
|
148
170
|
@topics.map do |topic|
|
|
149
|
-
|
|
150
|
-
title += "(#{topic.named_args.keys.join(', ')})" unless topic.named_args.empty?
|
|
151
|
-
title
|
|
171
|
+
topic.title
|
|
152
172
|
end
|
|
153
173
|
end
|
|
154
174
|
|
|
@@ -173,9 +193,7 @@ module Howzit
|
|
|
173
193
|
@topics.each do |topic|
|
|
174
194
|
next unless topic.tasks.count.positive?
|
|
175
195
|
|
|
176
|
-
|
|
177
|
-
title += "(#{topic.named_args.keys.join(', ')})" unless topic.named_args.empty?
|
|
178
|
-
output.push(title)
|
|
196
|
+
output.push(topic.title)
|
|
179
197
|
end
|
|
180
198
|
output.join("\n")
|
|
181
199
|
end
|
data/lib/howzit/config.rb
CHANGED
|
@@ -251,6 +251,14 @@ module Howzit
|
|
|
251
251
|
## @param default [Hash] default configuration to write
|
|
252
252
|
##
|
|
253
253
|
def create_config(default)
|
|
254
|
+
# If a legacy ~/.local/share/howzit directory exists, offer to migrate it
|
|
255
|
+
# into the new config root before creating any new files to avoid confusion
|
|
256
|
+
# about where Howzit stores its configuration.
|
|
257
|
+
# Use early_init=true since we're called during config initialization and can't access Howzit.options yet
|
|
258
|
+
if defined?(Howzit::ScriptSupport) && File.directory?(File.expand_path(Howzit::ScriptSupport::LEGACY_SUPPORT_DIR))
|
|
259
|
+
Howzit::ScriptSupport.migrate_legacy_support(early_init: true)
|
|
260
|
+
end
|
|
261
|
+
|
|
254
262
|
unless File.directory?(config_dir)
|
|
255
263
|
Howzit::ConsoleLogger.new(1).info "Creating config directory at #{config_dir}"
|
|
256
264
|
FileUtils.mkdir_p(config_dir)
|
|
@@ -8,7 +8,15 @@ module Howzit
|
|
|
8
8
|
# Handles helper script installation and injection for run blocks
|
|
9
9
|
# rubocop:disable Metrics/ModuleLength
|
|
10
10
|
module ScriptSupport
|
|
11
|
-
|
|
11
|
+
# Prefer XDG_CONFIG_HOME/howzit/support if set, otherwise ~/.config/howzit/support.
|
|
12
|
+
# For backwards compatibility, we detect an existing ~/.local/share/howzit directory
|
|
13
|
+
# and can optionally migrate it to the new location.
|
|
14
|
+
LEGACY_SUPPORT_DIR = File.join(Dir.home, '.local', 'share', 'howzit')
|
|
15
|
+
SUPPORT_DIR = if ENV['XDG_CONFIG_HOME'] && !ENV['XDG_CONFIG_HOME'].empty?
|
|
16
|
+
File.join(ENV['XDG_CONFIG_HOME'], 'howzit', 'support')
|
|
17
|
+
else
|
|
18
|
+
File.join(Dir.home, '.config', 'howzit', 'support')
|
|
19
|
+
end
|
|
12
20
|
|
|
13
21
|
class << self
|
|
14
22
|
##
|
|
@@ -25,11 +33,96 @@ module Howzit
|
|
|
25
33
|
##
|
|
26
34
|
def ensure_support_dir
|
|
27
35
|
dir = support_dir
|
|
36
|
+
legacy_dir = File.expand_path(LEGACY_SUPPORT_DIR)
|
|
37
|
+
new_root = File.expand_path(File.join(SUPPORT_DIR, '..'))
|
|
38
|
+
|
|
39
|
+
# If legacy files exist, always offer to migrate them before proceeding.
|
|
40
|
+
# Use early_init=false here since config is already loaded by the time we reach this point
|
|
41
|
+
migrate_legacy_support(early_init: false) if File.directory?(legacy_dir)
|
|
42
|
+
|
|
28
43
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
29
44
|
install_helper_scripts
|
|
30
45
|
dir
|
|
31
46
|
end
|
|
32
47
|
|
|
48
|
+
##
|
|
49
|
+
## Simple Y/N prompt that doesn't depend on Howzit.options (for use during early config initialization)
|
|
50
|
+
##
|
|
51
|
+
def simple_yn_prompt(prompt, default: true)
|
|
52
|
+
return default unless $stdout.isatty
|
|
53
|
+
|
|
54
|
+
tty_state = `stty -g`
|
|
55
|
+
yn = default ? '[Y/n]' : '[y/N]'
|
|
56
|
+
$stdout.syswrite "\e[1;37m#{prompt} #{yn}\e[1;37m? \e[0m"
|
|
57
|
+
system 'stty raw -echo cbreak isig'
|
|
58
|
+
res = $stdin.sysread 1
|
|
59
|
+
res.chomp!
|
|
60
|
+
puts
|
|
61
|
+
system 'stty cooked'
|
|
62
|
+
system "stty #{tty_state}"
|
|
63
|
+
res.empty? ? default : res =~ /y/i
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
##
|
|
67
|
+
## Migrate legacy ~/.local/share/howzit directory into the new config root,
|
|
68
|
+
## merging contents and removing the legacy directory when complete.
|
|
69
|
+
##
|
|
70
|
+
## @param early_init [Boolean] If true, use simple prompt that doesn't access Howzit.options
|
|
71
|
+
##
|
|
72
|
+
def migrate_legacy_support(early_init: false)
|
|
73
|
+
legacy_dir = File.expand_path(LEGACY_SUPPORT_DIR)
|
|
74
|
+
new_root = File.expand_path(File.join(SUPPORT_DIR, '..'))
|
|
75
|
+
|
|
76
|
+
unless File.directory?(legacy_dir)
|
|
77
|
+
if early_init
|
|
78
|
+
return
|
|
79
|
+
else
|
|
80
|
+
Howzit.console.info "No legacy Howzit directory found at #{legacy_dir}; nothing to migrate."
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
prompt = "Migrate Howzit files from #{legacy_dir} to #{new_root}? This will overwrite files in the new location with legacy versions, " \
|
|
86
|
+
'add new files, and leave any extra files in the new location in place.'
|
|
87
|
+
|
|
88
|
+
if early_init
|
|
89
|
+
unless simple_yn_prompt(prompt, default: true)
|
|
90
|
+
$stderr.puts 'Migration cancelled; no changes made.'
|
|
91
|
+
return
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
unless Prompt.yn(prompt, default: true)
|
|
95
|
+
Howzit.console.info 'Migration cancelled; no changes made.'
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
FileUtils.mkdir_p(new_root) unless File.directory?(new_root)
|
|
101
|
+
|
|
102
|
+
# Merge legacy into new_root:
|
|
103
|
+
# - overwrite files in new_root with versions from legacy
|
|
104
|
+
# - add files that do not yet exist
|
|
105
|
+
# - leave files that exist only in new_root untouched
|
|
106
|
+
Dir.children(legacy_dir).each do |entry|
|
|
107
|
+
src = File.join(legacy_dir, entry)
|
|
108
|
+
dest = File.join(new_root, entry)
|
|
109
|
+
|
|
110
|
+
if File.directory?(src)
|
|
111
|
+
FileUtils.mkdir_p(dest)
|
|
112
|
+
FileUtils.cp_r(File.join(src, '.'), dest)
|
|
113
|
+
else
|
|
114
|
+
FileUtils.cp(src, dest)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
FileUtils.rm_rf(legacy_dir)
|
|
119
|
+
if early_init
|
|
120
|
+
$stderr.puts "Migrated Howzit files from #{legacy_dir} to #{new_root}."
|
|
121
|
+
else
|
|
122
|
+
Howzit.console.info "Migrated Howzit files from #{legacy_dir} to #{new_root}."
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
33
126
|
##
|
|
34
127
|
## Detect interpreter from hashbang line
|
|
35
128
|
##
|
data/lib/howzit/stringutils.rb
CHANGED
|
@@ -407,15 +407,47 @@ module Howzit
|
|
|
407
407
|
end
|
|
408
408
|
|
|
409
409
|
##
|
|
410
|
-
## Examine text for
|
|
410
|
+
## Examine text for metadata and return key/value pairs
|
|
411
|
+
##
|
|
412
|
+
## Supports:
|
|
413
|
+
## - YAML front matter (starting with --- and ending with --- or ...)
|
|
414
|
+
## - MultiMarkdown-style key: value lines (up to first blank line)
|
|
411
415
|
##
|
|
412
416
|
## @return [Hash] The metadata as key/value pairs
|
|
413
417
|
##
|
|
414
418
|
def metadata
|
|
415
419
|
data = {}
|
|
416
|
-
|
|
417
|
-
|
|
420
|
+
lines = to_s.lines
|
|
421
|
+
first_idx = lines.index { |l| l !~ /^\s*$/ }
|
|
422
|
+
return {} unless first_idx
|
|
423
|
+
|
|
424
|
+
first = lines[first_idx]
|
|
425
|
+
|
|
426
|
+
if first =~ /^---\s*$/
|
|
427
|
+
# YAML front matter: between first --- and closing --- or ...
|
|
428
|
+
closing_rel = lines[(first_idx + 1)..].index { |l| l =~ /^(---|\.\.\.)\s*$/ }
|
|
429
|
+
closing_idx = closing_rel ? first_idx + 1 + closing_rel : lines.length
|
|
430
|
+
yaml_body = lines[(first_idx + 1)...closing_idx].join
|
|
431
|
+
raw = yaml_body.strip.empty? ? {} : YAML.load(yaml_body) || {}
|
|
432
|
+
if raw.is_a?(Hash)
|
|
433
|
+
raw.each do |k, v|
|
|
434
|
+
data[k.to_s.downcase] = v
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
else
|
|
438
|
+
# MultiMarkdown-style: key: value lines up to first blank line
|
|
439
|
+
header_lines = []
|
|
440
|
+
lines[first_idx..].each do |l|
|
|
441
|
+
break if l =~ /^\s*$/
|
|
442
|
+
|
|
443
|
+
header_lines << l
|
|
444
|
+
end
|
|
445
|
+
header = header_lines.join
|
|
446
|
+
header.scan(/(?mi)^(\S[\s\S]+?): ([\s\S]*?)(?=\n\S[\s\S]*?:|\Z)/).each do |m|
|
|
447
|
+
data[m[0].strip.downcase] = m[1]
|
|
448
|
+
end
|
|
418
449
|
end
|
|
450
|
+
|
|
419
451
|
out = normalize_metadata(data)
|
|
420
452
|
Howzit.named_arguments ||= {}
|
|
421
453
|
Howzit.named_arguments = out.merge(Howzit.named_arguments)
|
|
@@ -491,11 +523,21 @@ module Howzit
|
|
|
491
523
|
cols = Howzit.options[:wrap] if Howzit.options[:wrap].positive? && cols > Howzit.options[:wrap]
|
|
492
524
|
title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")
|
|
493
525
|
|
|
526
|
+
# Calculate remaining width for horizontal rule, ensuring it is never negative
|
|
527
|
+
remaining = cols - title.uncolor.length
|
|
528
|
+
if should_mark_iterm?
|
|
529
|
+
# Reserve some space for the iTerm mark escape sequence in the visual layout
|
|
530
|
+
remaining -= 15
|
|
531
|
+
end
|
|
532
|
+
remaining = 0 if remaining.negative?
|
|
533
|
+
|
|
534
|
+
hr_tail = options[:hr] * remaining
|
|
494
535
|
tail = if should_mark_iterm?
|
|
495
|
-
"#{
|
|
536
|
+
"#{hr_tail}#{options[:mark] ? iterm_marker : ''}"
|
|
496
537
|
else
|
|
497
|
-
|
|
538
|
+
hr_tail
|
|
498
539
|
end
|
|
540
|
+
|
|
499
541
|
Color.template("\n\n#{title}#{tail}{x}\n\n")
|
|
500
542
|
end
|
|
501
543
|
end
|
data/lib/howzit/version.rb
CHANGED
data/lib/howzit.rb
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Main config dir
|
|
4
|
-
|
|
4
|
+
# Prefer XDG_CONFIG_HOME if set, otherwise fall back to ~/.config/howzit
|
|
5
|
+
CONFIG_DIR = if ENV['XDG_CONFIG_HOME'] && !ENV['XDG_CONFIG_HOME'].empty?
|
|
6
|
+
File.join(ENV['XDG_CONFIG_HOME'], 'howzit')
|
|
7
|
+
else
|
|
8
|
+
File.join(Dir.home, '.config', 'howzit')
|
|
9
|
+
end
|
|
5
10
|
|
|
6
11
|
# Config file name
|
|
7
12
|
CONFIG_FILE = 'howzit.yaml'
|
data/spec/buildnote_spec.rb
CHANGED
|
@@ -8,7 +8,7 @@ describe Howzit::BuildNote do
|
|
|
8
8
|
describe ".note_file" do
|
|
9
9
|
it "locates a build note file" do
|
|
10
10
|
expect(how.note_file).not_to be_empty
|
|
11
|
-
expect(how.note_file).to match
|
|
11
|
+
expect(how.note_file).to match(/builda.md$/)
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -16,6 +16,7 @@ describe Howzit::BuildNote do
|
|
|
16
16
|
it "finds topic containing 'bermuda'" do
|
|
17
17
|
expect(how.grep('bermuda').map { |topic| topic.title }).to include('Topic Tropic')
|
|
18
18
|
end
|
|
19
|
+
|
|
19
20
|
it "does not return non-matching topic" do
|
|
20
21
|
expect(how.grep('bermuda').map { |topic| topic.title }).not_to include('Topic Balogna')
|
|
21
22
|
end
|
|
@@ -116,8 +117,9 @@ describe Howzit::BuildNote do
|
|
|
116
117
|
it "contains 7 topics" do
|
|
117
118
|
expect(how.list_topics.count).to eq 7
|
|
118
119
|
end
|
|
120
|
+
|
|
119
121
|
it "outputs a newline-separated string for completion" do
|
|
120
|
-
expect(how.list_completions.scan(
|
|
122
|
+
expect(how.list_completions.scan("\n").count).to eq 6
|
|
121
123
|
end
|
|
122
124
|
end
|
|
123
125
|
|
|
@@ -159,7 +161,7 @@ describe Howzit::BuildNote do
|
|
|
159
161
|
|
|
160
162
|
it "falls back to fuzzy match when no exact match" do
|
|
161
163
|
Howzit.options[:matching] = 'fuzzy'
|
|
162
|
-
search_terms = ['trpc']
|
|
164
|
+
search_terms = ['trpc'] # fuzzy for 'tropic'
|
|
163
165
|
output = []
|
|
164
166
|
matches = how.send(:collect_topic_matches, search_terms, output)
|
|
165
167
|
expect(matches.count).to eq 1
|
|
@@ -228,7 +230,7 @@ describe Howzit::BuildNote do
|
|
|
228
230
|
|
|
229
231
|
it "parses required variables from template metadata" do
|
|
230
232
|
vars = how.send(:parse_template_required_vars, template_with_required.path)
|
|
231
|
-
expect(vars).to eq([
|
|
233
|
+
expect(vars).to eq(%w[repo_url author])
|
|
232
234
|
end
|
|
233
235
|
|
|
234
236
|
it "returns empty array when no required metadata" do
|
|
@@ -236,4 +238,31 @@ describe Howzit::BuildNote do
|
|
|
236
238
|
expect(vars).to eq([])
|
|
237
239
|
end
|
|
238
240
|
end
|
|
241
|
+
|
|
242
|
+
describe 'global metadata from config' do
|
|
243
|
+
it 'merges config metadata under metadata key before file metadata' do
|
|
244
|
+
# Simulate config metadata in Howzit.options
|
|
245
|
+
Howzit.options[:metadata] = { 'author' => 'Config Author', 'license' => 'MIT' }
|
|
246
|
+
|
|
247
|
+
note = <<~EONOTE
|
|
248
|
+
author: Note Author
|
|
249
|
+
|
|
250
|
+
# Test Note
|
|
251
|
+
|
|
252
|
+
## Section
|
|
253
|
+
EONOTE
|
|
254
|
+
|
|
255
|
+
Tempfile.create(['buildnote_meta', '.md']) do |f|
|
|
256
|
+
f.write(note)
|
|
257
|
+
f.flush
|
|
258
|
+
|
|
259
|
+
build = Howzit::BuildNote.new(file: f.path)
|
|
260
|
+
|
|
261
|
+
expect(build.metadata['author']).to eq('Note Author')
|
|
262
|
+
expect(build.metadata['license']).to eq('MIT')
|
|
263
|
+
end
|
|
264
|
+
ensure
|
|
265
|
+
Howzit.options[:metadata] = nil
|
|
266
|
+
end
|
|
267
|
+
end
|
|
239
268
|
end
|
data/spec/stringutils_spec.rb
CHANGED
|
@@ -78,5 +78,45 @@ describe 'StringUtils' do
|
|
|
78
78
|
expect(result).to eq('echo ${BASH_VAR}')
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
|
-
end
|
|
82
81
|
|
|
82
|
+
describe '#metadata' do
|
|
83
|
+
before do
|
|
84
|
+
Howzit.named_arguments = {}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'parses MultiMarkdown-style metadata up to first blank line' do
|
|
88
|
+
text = <<~META
|
|
89
|
+
author: Note Author
|
|
90
|
+
license: MIT
|
|
91
|
+
|
|
92
|
+
This is the rest of the file.
|
|
93
|
+
author: Should not override
|
|
94
|
+
META
|
|
95
|
+
|
|
96
|
+
meta = text.metadata
|
|
97
|
+
expect(meta['author']).to eq('Note Author')
|
|
98
|
+
expect(meta['license']).to eq('MIT')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'parses YAML front matter when starting with ---' do
|
|
102
|
+
text = <<~META
|
|
103
|
+
---
|
|
104
|
+
author: Brett Terpstra
|
|
105
|
+
license: MIT
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
# Title
|
|
109
|
+
META
|
|
110
|
+
|
|
111
|
+
meta = text.metadata
|
|
112
|
+
expect(meta['author']).to eq('Brett Terpstra')
|
|
113
|
+
expect(meta['license']).to eq('MIT')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'returns empty hash when there is no metadata' do
|
|
117
|
+
text = "Just some content\nWithout metadata\n"
|
|
118
|
+
meta = text.metadata
|
|
119
|
+
expect(meta).to eq({})
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|