Class: Howzit::BuildNote

Inherits:
Object
  • Object
show all
Defined in:
lib/howzit/buildnote.rb

Overview

BuildNote Class

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file: nil, meta: nil) ⇒ BuildNote

Initialize a build note

Parameters:

  • file (String) (defaults to: nil)

    The path to the build note file



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/howzit/buildnote.rb', line 15

def initialize(file: nil, meta: nil)
  # Track if an explicit file was provided
  @explicit_file = file ? File.expand_path(file) : nil

  # Set @note_file if an explicit file was provided, before calling note_file getter
  if file
    @note_file = File.expand_path(file)
    file = @note_file # Use expanded path for reading
  else
    file = note_file
  end

  @topics = []
  create_note(prompt: true) if file.nil?

  content = Util.read_file(file)
  raise "{br}No content found in build note (#{file}){x}".c if content.nil? || content.empty?

  # Global metadata from config (e.g. ~/.config/howzit/howzit.yml)
  # Expecting something like:
  # metadata:
  #   author: Brett Terpstra
  #   license: MIT
  global_meta = {}
  raw_global = Howzit.options[:metadata] || Howzit.options['metadata']
  if raw_global.is_a?(Hash)
    raw_global.each do |k, v|
      global_meta[k.to_s.downcase] = v
    end
  end

  # Metadata defined in the build note itself (before the first heading)
  this_meta = content.split(/^#/)[0].strip.

  # Merge order:
  # 1. Global config metadata (lowest precedence)
  # 2. Metadata passed in via meta argument (e.g. templates)
  # 3. Build note metadata (highest precedence)
  combined_meta = global_meta.dup
  combined_meta.merge!(meta) if meta
  combined_meta.merge!(this_meta) if this_meta

  @metadata = combined_meta

  read_help(file)
end

Instance Attribute Details

#metadataObject (readonly)

Returns the value of attribute metadata.



8
9
10
# File 'lib/howzit/buildnote.rb', line 8

def 
  @metadata
end

#titleObject (readonly)

Returns the value of attribute title.



8
9
10
# File 'lib/howzit/buildnote.rb', line 8

def title
  @title
end

#topicsObject

Returns the value of attribute topics.



6
7
8
# File 'lib/howzit/buildnote.rb', line 6

def topics
  @topics
end

Instance Method Details

#create_note(prompt: false) ⇒ Object

Create a buildnotes skeleton



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/howzit/buildnote.rb', line 300

def create_note(prompt: false)
  trap('SIGINT') do
    Howzit.console.info "\nCancelled"
    exit!
  end

  default = !$stdout.isatty || Howzit.options[:default]

  if prompt && !default
    res = Prompt.yn('No build notes file found, create one?', default: true)
    Process.exit 0 unless res
  end

  # First make sure there isn't already a buildnotes file
  if note_file
    fname = "{by}#{note_file}{bw}".c
    unless default
      res = Prompt.yn("#{fname} exists and appears to be a build note, continue anyway?", default: false)
      Process.exit 0 unless res
    end
  end

  title = File.basename(Dir.pwd)
  # prompt = TTY::Prompt.new
  title = Prompt.get_line('{bw}Project name{x}'.c, default: title) unless default
  summary = ''
  summary = Prompt.get_line('{bw}Project summary{x}'.c) unless default

  # Template selection
  selected_templates = []
   = {}
  selected_templates,  = select_templates_for_note(title) unless default

  fname = 'buildnotes.md'
  unless default
    fname = Prompt.get_line("{bw}Build notes filename{x}\n(must begin with 'howzit' or 'build')".c, default: fname)
  end

  # Build metadata section
   = []
   << "template: #{selected_templates.join(',')}" unless selected_templates.empty?
  .each do |key, value|
     << "#{key}: #{value}"
  end
   = .empty? ? '' : "#{.join("\n")}\n\n"

  note = <<~EOBUILDNOTES
    #{}# #{title}

    #{summary}

    ## File Structure

    Where are the main editable files? Is there a dist/build folder that should be ignored?

    ## Build

    What build system/parameters does this use?

    @run(./build command)

    ## Deploy

    What are the procedures/commands to deploy this project?

    ## Other

    Version control notes, additional gulp/rake/make/etc tasks...

  EOBUILDNOTES

  if File.exist?(fname) && !default
    file = "{by}#{fname}".c
    unless Prompt.yn("Are you absolutely sure you want to overwrite #{file}", default: false)
      Howzit.console.info('Canceled')
      Process.exit 0
    end
  end

  File.open(fname, 'w') do |f|
    f.puts note
    Howzit.console.info("{by}Build notes for {bw}#{title}{by} written to {bw}#{fname}{x}".c)
  end

  if File.exist?(fname) && !default && Prompt.yn("{bg}Do you want to open {bw}#{fname} {bg}for editing?{x}".c,
                                                 default: false)
    edit_note
  end

  Process.exit 0
end

#create_template_file(file, prompt: false) ⇒ Object

Create a template file

Parameters:

  • file (String)

    file path

  • prompt (Boolean) (defaults to: false)

    confirm file creation?



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/howzit/buildnote.rb', line 256

def create_template_file(file, prompt: false)
  trap('SIGINT') do
    Howzit.console.info "\nCancelled"
    exit!
  end

  default = !$stdout.isatty || Howzit.options[:default]

  if prompt && !default && !File.exist?(file)
    res = Prompt.yn("{bg}Template {bw}#{File.basename(file)}{bg} not found, create it?{x}".c, default: true)
    Process.exit 0 unless res
  end

  title = File.basename(file, '.md')

  note = <<~EOBUILDNOTES
    # #{title}

    ## Template Topic

  EOBUILDNOTES

  if File.exist?(file) && !default
    file = "{by}#{file}".c
    unless Prompt.yn("Are you sure you want to overwrite #{file}", default: false)
      Howzit.console.info('Cancelled')
      Process.exit 0
    end
  end

  File.open(file, 'w') do |f|
    f.puts note
    Howzit.console.info("{by}Template {bw}#{title}{by} written to {bw}#{file}{x}".c)
  end

  if File.exist?(file) && !default && Prompt.yn("{bg}Do you want to open {bw}#{file} {bg}for editing?{x}".c,
                                                default: false)
    edit_template_file(file)
  end

  Process.exit 0
end

#editObject

Public method to open build note in editor



81
82
83
# File 'lib/howzit/buildnote.rb', line 81

def edit
  edit_note
end

#edit_template(template) ⇒ Object

Public method to open a template in the editor

Parameters:

  • template (String)

    The template title



90
91
92
93
94
# File 'lib/howzit/buildnote.rb', line 90

def edit_template(template)
  file = template.sub(/(\.md)?$/i, '.md')
  file = File.join(Howzit.config.template_folder, file)
  edit_template_file(file)
end

#find_topic(term = nil) ⇒ Object

Find a topic based on a fuzzy match

Parameters:

  • term (String) (defaults to: nil)

    The search term



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/howzit/buildnote.rb', line 101

def find_topic(term = nil)
  return @topics if term.nil?

  rx = term.to_rx

  @topics.filter do |topic|
    title = topic.title.downcase.sub(/ *\(.*?\) *$/, '')
    match = title =~ rx

    if !match && term =~ /[,:]/
      normalized = title.gsub(/\s*([,:])\s*/, '\1')
      match = normalized =~ rx
    end

    match
  end
end

#find_topic_exact(term = nil) ⇒ Array

Find a topic with an exact whole-word match

Parameters:

  • term (String) (defaults to: nil)

    The search term

Returns:

  • (Array)

    Array of topics that exactly match the term



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/howzit/buildnote.rb', line 126

def find_topic_exact(term = nil)
  return [] if term.nil?

  @topics.filter do |topic|
    title = topic.title.downcase.sub(/ *\(.*?\) *$/, '').strip
    # Split both the title and search term into words
    title_words = title.split
    search_words = term.split

    # Check if all search words match the title words exactly (case-insensitive)
    search_words.map(&:downcase) == title_words.map(&:downcase)
  end
end

#grep(term) ⇒ Object

Call grep on all topics, filtering out those that don’t match

Parameters:

  • term (String)

    The search pattern



156
157
158
# File 'lib/howzit/buildnote.rb', line 156

def grep(term)
  @topics.filter { |topic| topic.grep(term) }
end

#hookObject

Copy a link to the main build note file to clipboard (macOS only)



143
144
145
146
147
148
149
# File 'lib/howzit/buildnote.rb', line 143

def hook
  title = Util.read_file(note_file).note_title(note_file, 20)
  title = "#{title} project notes"
  url = "[#{title}](file://#{note_file})"
  Util.os_copy(url)
  Howzit.console.info('Link copied to clipboard.')
end

#inspectObject

Inspect

Returns:

  • description



67
68
69
# File 'lib/howzit/buildnote.rb', line 67

def inspect
  "#<Howzit::BuildNote @topics=[#{@topics.count}]>"
end

#listString

Output a list of topic titles

Returns:

  • (String)

    formatted list of topics in build note



164
165
166
167
168
169
170
171
# File 'lib/howzit/buildnote.rb', line 164

def list
  output = []
  output.push("{bg}Topics:{x}\n".c)
  @topics.each do |topic|
    output.push("- {bw}#{topic.title}{x}".c)
  end
  output.join("\n")
end

#list_completionsString

Return a list of topic titles suitable for shell completion

Returns:

  • (String)

    newline-separated list of topic titles



189
190
191
# File 'lib/howzit/buildnote.rb', line 189

def list_completions
  list_topics.join("\n")
end

#list_runnableString

Return a formatted list of topics containing directives suitable for console output

Returns:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/howzit/buildnote.rb', line 216

def list_runnable
  output = []
  output.push(%({bg}"Runnable" Topics:{x}\n).c)

  find_topic(Howzit.options[:for_topic]).each do |topic|
    s_out = []

    topic.tasks.each { |task| s_out.push(task.to_list) }

    next if s_out.empty?

    title = topic.title
    # Show argument definitions with colorized formatting
    unless topic.arg_definitions.nil? || topic.arg_definitions.empty?
      formatted_args = topic.arg_definitions.map { |arg| topic.format_arg_definition(arg) }.join('{l}, '.c)
      title += " {l}({x}#{formatted_args}{l}){x}".c
    end

    output.push("- {g}#{title}{x}".c)
    output.push(s_out.join("\n"))
  end

  output.join("\n")
end

#list_runnable_completionsString

Return a list of topics containing @directives, suitable for shell completion

Returns:

  • (String)

    newline-separated list of topic titles



200
201
202
203
204
205
206
207
208
# File 'lib/howzit/buildnote.rb', line 200

def list_runnable_completions
  output = []
  @topics.each do |topic|
    next unless topic.tasks.count.positive?

    output.push(topic.title)
  end
  output.join("\n")
end

#list_topicsArray

Return an array of topic titles

Returns:

  • (Array)

    array of topic titles



178
179
180
181
182
# File 'lib/howzit/buildnote.rb', line 178

def list_topics
  @topics.map do |topic|
    topic.title
  end
end

#note_fileString

Accessor method for note_file (path to located build note)

Returns:



397
398
399
# File 'lib/howzit/buildnote.rb', line 397

def note_file
  @note_file ||= find_note_file
end

#read_file(file) ⇒ Object

Read the help file contents

Parameters:

  • file (String)

    The filepath



246
247
248
# File 'lib/howzit/buildnote.rb', line 246

def read_file(file)
  read_help_file(file)
end

#runObject

Public method to begin processing the build note based on command line options



74
75
76
# File 'lib/howzit/buildnote.rb', line 74

def run
  process
end