jtag 0.1.21 → 0.2.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.
data/bin/jtag CHANGED
@@ -10,85 +10,123 @@ program_desc "Autotagging for Jekyll"
10
10
 
11
11
  version Jtag::VERSION
12
12
 
13
+ @config_files = %w{blacklist.txt config.yml stopwords.txt synonyms.yml}
14
+ @config_target = File.expand_path("~/.config/jtag")
13
15
  @piped_content = nil
14
16
 
15
- ##
16
- ## Define global flags and switches
17
- ##
18
- desc "Expect input from STDIN to be null (0)-delimited: Useful for piping file lists from `find` or `grep`"
19
- default_value false
20
- switch %i[0 null], negatable: false
17
+ def config_files_complete?
18
+ @config_files.each do |file|
19
+ return false unless File.exist?(File.join(@config_target, file))
20
+ end
21
+ true
22
+ end
21
23
 
22
- desc "Path to alternate configuration directory"
23
- arg_name "config_dir"
24
- flag %i[c config_dir], :type => String
24
+ class ::String
25
+ def file_list?
26
+ self.strip.split("\n").each do |line|
27
+ return false unless File.exist?(line)
28
+ end
29
+ true
30
+ end
31
+ end
25
32
 
26
- desc "Debug level: Set the debug level for the application. 0 is silent, 1 is errors only, 2 is errors and warnings, 3 is errors, warnings and info"
33
+ desc "Debug level"
34
+ default_value "0"
27
35
  arg_name "debug_level"
28
- flag %i[d debug], :must_match => /\d+/, :type => Integer, :default_value => 3
36
+ flag %i[d debug], :must_match => /\d+/, :type => Integer, :default_value => 0
29
37
 
30
- desc "Perform case-insensitive matches and searches: Useful for case-insensitive filesystems or when you're not sure of the case of your tags"
31
- switch %i[i case_insensitive], negatable: false
38
+ desc "Run silently"
39
+ switch %i[s silent]
32
40
 
33
- desc "Run silently: Suppress all output to STDOUT, same as debug level 0"
34
- switch %i[s silent], negatable: false
41
+ desc "Perform case-insensitive matches and searches"
42
+ switch %i[i case_insensitive]
35
43
 
36
- desc "Test (dry run, don't update files): Run all commands and show results on the command line, but don't overwrite/update any files"
44
+ desc "Test (dry run, don't update files)"
45
+ long_desc "Run all commands and show results on the command line, but don't overwrite/update any files"
37
46
  default_value false
38
- switch %i[t test], negatable: false
47
+ switch %i[t test]
39
48
 
40
- # Add a command to update and notify the user of the configuration files location
41
- desc "Update and notify user of configuration files location"
42
- command :config do |c|
43
- # Add a switch to reset all configuration files to default values
44
- c.desc "Reset all configuration files to default values"
45
- c.switch %i[r reset]
49
+ def console_log(msg = "", options = {})
50
+ err = options[:err] || false
51
+ options[:log] ||= false
46
52
 
47
- c.desc "Ignore prompts and answer yes to all questions."
48
- c.switch %i[y yes], negatable: false
53
+ if options[:log]
54
+ if err
55
+ @log.warn(msg)
56
+ else
57
+ @log.info(msg)
58
+ end
59
+ end
49
60
 
50
- c.desc "Set a specific configuration value"
51
- c.arg_name "key"
52
- c.flag %i[s set]
61
+ return if @silent
53
62
 
54
- c.desc "The value to set for key"
55
- c.arg_name "value"
56
- c.flag %i[v value]
63
+ unless err
64
+ $stdout.puts msg
65
+ else
66
+ warn msg
67
+ end
68
+ end
57
69
 
58
- # Skip the pre block for this command
59
- c.skips_pre
70
+ def migrate_old_config
71
+ old_config = File.expand_path("~/.jtag")
72
+ new_config = File.expand_path("~/.config/jtag")
73
+
74
+ if File.directory?(old_config) && !File.directory?(new_config)
75
+ # Create parent directory if needed
76
+ FileUtils.mkdir_p(File.dirname(new_config))
77
+
78
+ # Move the directory
79
+ FileUtils.mv(old_config, new_config)
80
+
81
+ # ANSI color codes: yellow=\033[0;33m, boldyellow=\033[1;33m, default=\033[0;39m
82
+ console_log "\033[0;33mConfiguration directory has been moved from \033[1;33m~/.jtag\033[0;33m to \033[1;33m~/.config/jtag\033[0;33m.\033[0;39m", :err => true
83
+ console_log "\033[0;33mYour existing configuration and files have been preserved.\033[0;39m", :err => true
84
+ end
85
+ end
86
+
87
+ desc "Update and notify user of configuration files location"
88
+ command :config do |c|
89
+ c.desc "Reset all configuration files to default values"
90
+ c.switch [:r, "reset"]
60
91
 
61
- # Define the action to be taken when the command is executed
92
+ c.skips_pre
62
93
  c.action do |global_options, options, args|
63
- if options[:set]
64
- config = YAML.load_file(File.join(@util.config_target, "config.yml"))
65
- config[options[:set]] = options[:value]
66
- File.open(File.join(@util.config_target, "config.yml"), "w") do |f|
67
- f.write(config.to_yaml)
94
+ if options[:r]
95
+ print "Are you sure you want to reset all config files? y/N: "
96
+ response = STDIN.gets.strip
97
+ if response =~ /^y/i
98
+ write_config(true)
99
+ console_log "Config files reset"
68
100
  end
69
- return
101
+ else
102
+ write_config(false)
70
103
  end
104
+ end
105
+ end
71
106
 
72
- if options[:reset]
73
- # Prompt the user for confirmation before resetting config files
74
- if options[:yes]
75
- res = true
76
- else
77
- print "Are you sure you want to reset all config files? y/N: "
78
- response = STDIN.gets.strip
79
- res = response =~ /^y/i
80
- end
107
+ def write_config(atomic = false)
108
+ gem_root = Gem.loaded_specs["jtag"].full_gem_path
109
+ gem_lib = File.join(gem_root, "lib")
110
+ config_source = File.join(gem_lib, "/jtag/config_files")
81
111
 
82
- if res
83
- # Reset config files to default values
84
- @util.write_config(true)
85
- @util.console_log "Config files reset"
86
- end
87
- else
88
- # Update config files without overwriting existing files
89
- @util.write_config(false)
112
+ unless File.directory?(@config_target) || atomic
113
+ FileUtils.cp_r(config_source, @config_target)
114
+ console_log "Configuration files are located in the folder: " + @config_target
115
+ console_log %Q{Make sure that "tags_location" in config.yml is set to your tags.json url.}
116
+ console_log "Configuration files written to #{@config_target}"
117
+ end
118
+
119
+ @config_files.each do |file|
120
+ unless File.exist?(File.join(@config_target, file))
121
+ source_file = File.join(config_source, file)
122
+ target_file = File.join(@config_target, file)
123
+ FileUtils.cp(source_file, target_file)
124
+ console_log "Config file #{file} added."
90
125
  end
91
126
  end
127
+ console_log
128
+ console_log "Configuration files are located in the folder: " + @config_target
129
+ console_log %Q{Make sure that "tags_location" in the config.yml file is set to your tags json file.}
92
130
  end
93
131
 
94
132
  desc "List tags, optionally filter for keywords/regular expressions (OR)"
@@ -105,125 +143,32 @@ command :search do |c|
105
143
  c.default_value false
106
144
  c.switch %i[c counts]
107
145
 
108
- c.desc "Boolean operator for multiple tags (AND/OR/NOT)"
109
- c.arg_name "bool"
110
- c.default_value "OR"
111
- c.flag %i[b bool], :must_match => /^([aon])(?:.*)$/i, :type => String
112
-
113
- c.desc "Match type: exact, fuzzy, starts_with, contains"
114
- c.arg_name "match_type"
115
- c.default_value "contains"
116
- c.flag %i[m match], :must_match => /^([exfsc])(?:.*)$/i, :type => String
117
-
118
- c.desc "Case-sensitive matching"
119
- c.switch %i[I s case_sensitive], negatable: true
120
-
121
- c.desc "Fuzzy match distance"
122
- c.arg_name "distance"
123
- c.default_value 2
124
- c.flag %i[d distance], :must_match => /^\d+$/, :type => Integer
125
-
126
146
  c.action do |global_options, options, args|
127
- # Initialize arrays to store files and keywords
128
- files = []
129
- keywords = []
130
- bool_op = options[:bool].to_bool
131
-
132
- # Iterate over the arguments and separate files from keywords
133
- args.length.times do
134
- arg = args.pop
135
- if File.file?(File.expand_path(arg))
136
- files.push(File.expand_path(arg)) # Add existing files to the files array
137
- else
138
- keywords.push(arg) # Add non-file arguments to the keywords array
139
- end
140
- end
141
-
142
- # If piped content is provided and it is a list of files, add them to the files array
143
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
144
-
145
- # If no files are provided, use the default post location
146
- if files.empty?
147
- if File.exist?(File.expand_path(@jt.tags_loc))
148
- files = nil
149
- else
150
- if @jt.default_post_location && File.exist?(File.expand_path(@jt.default_post_location))
151
- # Retrieves a list of files from the default post location with the specified post extension.
152
- # The files are located using a glob pattern that matches all files with the given extension.
153
- #
154
- # @return [Array<String>] an array of file paths matching the specified pattern
155
- files = Dir.glob(File.join(File.expand_path(@jt.default_post_location), "**", "*.#{@jt.post_extension}"))
147
+ tags = @jt.get_tags({ :counts => true })
148
+ if args.length > 0
149
+ re = args.join("|")
150
+ tags.delete_if { |tag|
151
+ if tag && tag["name"] =~ /(#{re})/i
152
+ false
153
+ else
154
+ true
156
155
  end
157
- end
158
- else
159
- tags = {}
160
- end
161
-
162
- # Get tags with counts for the specified files
163
- if @piped_content && !@piped_content.file_list?(global_options[:null])
164
- if @piped_content.yaml?
165
- tags = YAML.load(@piped_content).to_counts
166
- elsif @piped_content.json?
167
- tags = JSON.parse(@piped_content).to_counts
168
- elsif @piped_content.tag_list?
169
- tags = @piped_content.split("\n").map(&:strip).to_counts
156
+ }
157
+ if options[:c]
158
+ tags.map! { |tag| "#{tag["name"]} (#{tag["count"]})" }
170
159
  else
171
- tags = @jt.tags({ :counts => true, :files => files })
160
+ tags.map! { |tag| tag["name"] }
172
161
  end
162
+ output_tags(tags, { :format => options[:format] })
173
163
  else
174
- tags = @jt.tags({ :counts => true, :files => files })
175
- end
176
-
177
- exit_now! "No source tag list found" if !tags || tags.empty?
178
-
179
- match_options = {
180
- exact: false,
181
- contains: false,
182
- starts_with: false,
183
- fuzzy: false,
184
- case_sensitive: options[:case_sensitive],
185
- distance: options[:distance],
186
- }
187
-
188
- if options[:match] =~ /^[ex]/i
189
- match_options[:exact] = true
190
- elsif options[:match] =~ /^s/i
191
- match_options[:starts_with] = true
192
- elsif options[:match] =~ /^f/i
193
- match_options[:fuzzy] = true
194
- else
195
- match_options[:contains] = true
196
- end
197
-
198
- # If keywords are provided, filter tags based on the keywords
199
- if keywords.length > 0
200
- if bool_op == :and
201
- tags.delete_if { |tag| tag && !keywords.all? { |keyword| tag["name"].match_keyword(keyword, match_options) } }
202
- elsif bool_op == :not
203
- re = keywords.join("|") # Join keywords with OR operator
204
- # Remove tags that match the keywords
205
- tags.delete_if { |tag| tag && keywords.any? { |keyword| tag["name"].match_keyword(keyword, match_options) } }
164
+ tags.delete_if { |tag| !tag }
165
+ if options[:c]
166
+ tags.map! { |tag| "#{tag["name"]} (#{tag["count"]})" }
206
167
  else
207
- # :or
208
- # Filter tags based on any keywords
209
- tags.delete_if { |tag| tag && !keywords.any? { |keyword| tag["name"].match_keyword(keyword, match_options) } }
168
+ tags.map! { |tag| tag["name"] }
210
169
  end
170
+ output_tags(tags, { :format => options[:format] })
211
171
  end
212
-
213
- # Remove nil and false values from the tags array
214
- tags.delete_if { |tag| !tag }
215
-
216
- exit_now! "No matching tags found" if tags.nil? || tags.empty?
217
-
218
- # Format tags with counts if the counts option is enabled
219
- if options[:count]
220
- tags.map! { |tag| "#{tag["name"]} (#{tag["count"]})" }
221
- else
222
- tags.map! { |tag| tag["name"] }
223
- end
224
-
225
- # Output the formatted tags
226
- @util.output_tags(tags, { :format => options[:format] })
227
172
  end
228
173
  end
229
174
 
@@ -244,73 +189,61 @@ command :posts_tagged do |c|
244
189
  c.switch [:print0]
245
190
 
246
191
  c.action do |global_options, options, args|
247
- # Convert the boolean operator to uppercase for consistency
248
192
  bool = options[:bool].upcase
249
-
250
- # Initialize arrays to store files, tags, and matches
251
193
  files = []
252
194
  tags = []
253
195
  matches = []
254
-
255
- # Separate files and tags from the arguments
256
196
  args.length.times do
257
197
  arg = args.pop
258
198
  if File.exist?(arg)
259
- files.push(arg) # Add existing files to the files array
199
+ files.push(arg)
260
200
  else
261
- tags.push(arg) # Add non-file arguments to the tags array
201
+ tags.push(arg)
262
202
  end
263
203
  end
264
204
 
265
- # If piped content is provided and it is a list of files, add them to the files array
266
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
205
+ files.concat(@piped_content.split("\n")) if @piped_content
267
206
 
268
- # If no files are provided, use the default post location
269
207
  if files.empty?
270
- if @jt.default_post_location && File.exist?(@jt.default_post_location)
271
- # Retrieves a list of files from the default post location with the specified post extension.
272
- # The files are located using a glob pattern that matches all files with the given extension.
273
- files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
208
+ if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
209
+ files = Dir.glob(@jt.default_post_location)
274
210
  end
275
211
  end
276
-
277
- # Exit if no valid filenames are found in the arguments
278
- raise NoValidFile if files.empty?
279
-
280
- # Iterate over each file to find matches based on the boolean operator
281
- files.each do |file|
212
+ exit_now! "No valid filename in arguments" if files.empty?
213
+ files.each { |file|
282
214
  if File.exist?(file)
283
- post_tags = @jt.post_tags(file) # Get tags for the current file
215
+ post_tags = @jt.post_tags(file)
284
216
 
285
217
  if bool == "AND"
286
- # Check if all tags are present in the post tags
287
218
  matched = 0
288
- tags.each { |tag| matched += 1 if post_tags.include?(tag) }
219
+ tags.each { |tag|
220
+ matched += 1 if post_tags.include?(tag)
221
+ }
289
222
  matches.push(file) if matched == tags.length
290
223
  elsif bool == "NOT"
291
- # Check if none of the tags are present in the post tags
292
224
  matched = false
293
- tags.each { |tag| matched = true if post_tags.include?(tag) }
225
+ tags.each { |tag|
226
+ matched = true if post_tags.include?(tag)
227
+ }
294
228
  matches.push(file) unless matched
295
229
  else
296
- # Default to OR: Check if any of the tags are present in the post tags
297
- tags.each { |tag| matches.push(file) unless matches.include?(file) if post_tags.include?(tag) }
230
+ tags.each { |tag|
231
+ if post_tags.include?(tag)
232
+ matches.push(file) unless matches.include?(file)
233
+ end
234
+ }
298
235
  end
299
236
  else
300
- # Exit if the file is not found
301
- raise FileNotFound, "File not found: #{file}"
237
+ raise "File not found: #{file}"
302
238
  end
303
- end
239
+ }
304
240
 
305
- # Create a search string from the tags and boolean operator
306
241
  search_string = tags.join(" #{bool} ")
307
-
308
- # Output the results
309
242
  if matches.empty?
310
- @util.console_log "No matching files found for #{search_string}"
243
+ console_log "No matching files found for #{search_string}"
311
244
  else
312
- @util.console_log "(#{search_string})", { err: true }
313
- @util.output_tags(matches, { format: options[:format], print0: options[:print0], grouping: "files" })
245
+ console_log "(#{search_string})", { :err => true }
246
+ output_tags(matches, { :format => options[:format], :print0 => options[:print0], :grouping => "files" })
314
247
  end
315
248
  end
316
249
  end
@@ -339,48 +272,15 @@ command :loners do |c|
339
272
  c.flag %i[e edit], :default_value => false, :type => String
340
273
 
341
274
  c.action do |global_options, options, args|
342
- # Convert the max option to an integer
343
275
  max = options[:m].to_i
344
-
345
- # Initialize arrays to store files and keywords
346
- files = []
347
- keywords = []
348
-
349
- # Separate files and keywords from the arguments
350
- args.length.times do
351
- arg = args.pop
352
- if File.exist?(arg)
353
- files.push(arg) # Add existing files to the files array
354
- else
355
- keywords.push(arg) # Add non-file arguments to the keywords array
356
- end
357
- end
358
-
359
- # If piped content is provided and it is a list of files, add them to the files array
360
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
361
-
362
- # If no files are provided, use the default post location
363
- if files.empty?
364
- if @jt.default_post_location && File.exist?(@jt.default_post_location)
365
- files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
366
- end
367
- end
368
-
369
- # Get tags with counts for the specified files
370
- loner_tags = @jt.tags({ :counts => true, :files => files })
371
-
372
- # Remove tags that are not loners (i.e., tags with counts greater than max)
276
+ loner_tags = @jt.get_tags({ :counts => true })
373
277
  loner_tags.delete_if { |tag|
374
278
  tag.class == FalseClass || tag["count"] > max
375
279
  }
376
-
377
- # Sort loner tags by count
378
280
  loner_tags.sort_by! { |tag| tag["count"] }
379
281
 
380
- # Exit if no tags matched the criteria
381
282
  exit_now! "No tags matched the criteria" if loner_tags.empty? || loner_tags.nil?
382
283
 
383
- # If the edit option is provided, write the loner tags to a file for editing
384
284
  if options[:e]
385
285
  path = File.expand_path(options[:e])
386
286
  while File.exist?(path)
@@ -395,7 +295,7 @@ command :loners do |c|
395
295
  File.open(path, "w+") do |f|
396
296
  f.puts "# Edit this file to remove tags you want to keep,"
397
297
  f.puts "# then run `jtag remove -p '#{path}' [/path/to/posts/*.md]`"
398
- f.puts "# to remove any tags left in this file."
298
+ f.puts "# to remove any tags left in the file."
399
299
  f.puts "#"
400
300
  f.puts "# The post counts are included for your convenience, and will"
401
301
  f.puts "# be automatically ignored when reading the list back in."
@@ -406,17 +306,15 @@ command :loners do |c|
406
306
  }
407
307
  end
408
308
 
409
- @util.console_log "A list of results and instructions for use have been written to #{path}."
309
+ console_log "A list of results and instructions for use have been written to #{path}."
410
310
  if ENV["EDITOR"]
411
- @util.console_log
311
+ console_log
412
312
  print "Would you like to open the file in #{ENV["EDITOR"]} now? (y/N) "
413
313
  input = STDIN.gets
414
314
  if input =~ /^y/i
415
315
  system "#{ENV["EDITOR"]} '#{path}'"
416
316
  end
417
317
  end
418
-
419
- # If the remove option is provided, remove the loner tags from the specified files
420
318
  elsif options[:r]
421
319
  files = []
422
320
  args.length.times do
@@ -424,15 +322,14 @@ command :loners do |c|
424
322
  files.push(arg) if File.exist?(arg)
425
323
  end
426
324
 
427
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
325
+ files.concat(@piped_content.split("\n")) if @piped_content
428
326
 
429
327
  if files.empty?
430
- if @jt.default_post_location && File.exist?(@jt.default_post_location)
431
- files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
328
+ if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
329
+ files = Dir.glob(@jt.default_post_location)
432
330
  end
433
331
  end
434
332
  exit_now! "No valid filename in arguments" if files.empty?
435
-
436
333
  files.each { |file|
437
334
  tags = @jt.post_tags(file)
438
335
  loner_tags.each { |d|
@@ -446,17 +343,15 @@ command :loners do |c|
446
343
  }
447
344
  unless global_options[:t]
448
345
  @jt.update_file_tags(file, tags)
449
- @util.console_log "Updated tags for #{file}", :log => true, level: :warn
346
+ console_log "Updated tags for #{file}", :log => true
450
347
  end
451
348
 
452
- @util.console_log level: :info
453
- @util.console_log File.basename(file) + ":", level: :info
454
- @util.output_tags(tags, :format => options[:format], :filename => file)
349
+ console_log
350
+ console_log File.basename(file) + ":"
351
+ output_tags(tags, :format => options[:format], :filename => file)
455
352
  }
456
-
457
- # Otherwise, output the loner tags
458
353
  else
459
- @util.output_tags(loner_tags.map { |tag|
354
+ output_tags(loner_tags.map { |tag|
460
355
  count = options[:no_counts] ? "" : " (#{tag["count"]})"
461
356
  "#{tag["name"]}#{count}"
462
357
  }, :format => options[:format])
@@ -481,24 +376,24 @@ command :tags do |c|
481
376
  end
482
377
  end
483
378
 
484
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
379
+ files.concat(@piped_content.split("\n")) if @piped_content && @piped_content.file_list?
485
380
 
486
381
  tags = []
487
382
  files.each do |file|
488
383
  tags.concat(@jt.post_tags(file)) if File.exist?(file)
489
384
 
490
385
  if args.length > 0
491
- @util.console_log level: :info
492
- @util.console_log "STDIN:", level: :info
386
+ console_log
387
+ console_log "STDIN:"
493
388
  end
494
389
  end
495
390
 
496
391
  if tags.empty? || tags.nil?
497
- @util.console_log "No tags in post", err: true, level: :info
392
+ console_log "No tags in post", { :err => true }
498
393
  else
499
394
  tags.sort!
500
395
  tags.uniq!
501
- @util.output_tags(tags, { :format => options[:format] })
396
+ output_tags(tags, { :format => options[:format] })
502
397
  end
503
398
  end
504
399
  end
@@ -520,7 +415,7 @@ command :sort do |c|
520
415
  end
521
416
  end
522
417
 
523
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
418
+ files.concat(@piped_content.split("\n")) if @piped_content
524
419
 
525
420
  files.each do |file|
526
421
  tags = @jt.post_tags(file)
@@ -529,13 +424,13 @@ command :sort do |c|
529
424
  unless global_options[:t]
530
425
  @jt.update_file_tags(file, tags)
531
426
  end
532
- @util.console_log level: :info
533
- @util.console_log "#{File.basename(file)}:", level: :info
427
+ console_log
428
+ console_log File.basename(file) + ":"
534
429
 
535
430
  if tags.empty? || tags.nil?
536
- @util.console_log "No tags in post", err: true, level: :info
431
+ console_log "No tags in post", { :err => true }
537
432
  else
538
- @util.output_tags(tags, { :format => options[:format], :filename => file })
433
+ output_tags(tags, { :format => options[:format], :filename => file })
539
434
  end
540
435
  end
541
436
  end
@@ -562,31 +457,31 @@ command :merge do |c|
562
457
  end
563
458
  end
564
459
 
565
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
460
+ files.concat(@piped_content.split("\n")) if @piped_content
566
461
 
567
462
  if files.empty?
568
- if @jt.default_post_location && File.exist?(@jt.default_post_location)
569
- files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
463
+ if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
464
+ files = Dir.glob(@jt.default_post_location)
570
465
  end
571
466
  end
572
467
  exit_now! "No valid filename in arguments" if files.empty?
573
468
  exit_now! "Needs at least two tag inputs, one or more to merge, one to merge to" if tags.length < 2
574
469
  tags.reverse!
575
470
  merge_tag = tags.pop
576
- @util.console_log %(Merging #{tags.join(", ")} to #{merge_tag}), level: :info
577
- files.each do |file|
471
+ console_log %Q{Merging #{tags.join(", ")} to #{merge_tag}}
472
+ files.each { |file|
578
473
  new_tags = @jt.merge_tags(tags, merge_tag, file)
579
474
  next unless new_tags
580
- unless global_options[:test]
475
+ unless global_options[:t]
581
476
  @jt.update_file_tags(file, new_tags)
582
- @util.console_log level: :info
583
- @util.console_log "Updated tags for #{file}", log: true, level: :warn
477
+ console_log
478
+ console_log "Updated tags for #{file}", :log => true
584
479
  end
585
480
 
586
- @util.console_log level: :info
587
- @util.console_log "#{File.basename(file)}:", level: :info
588
- @util.output_tags(new_tags, { format: options[:format], filename: file })
589
- end
481
+ console_log
482
+ console_log File.basename(file) + ":"
483
+ output_tags(new_tags, { :format => options[:format], :filename => file })
484
+ }
590
485
  end
591
486
  end
592
487
 
@@ -599,22 +494,22 @@ command :blacklist do |c|
599
494
  c.action do |global_options, options, args|
600
495
  if options[:r]
601
496
  @jt.unblacklist(args)
602
- @util.console_log "Removed #{args.join(", ")} from blacklist.", level: :info, err: true
497
+ console_log "Removed #{args.join(", ")} from blacklist."
603
498
  else
604
499
  @jt.blacklist(args)
605
- @util.console_log "Blacklisted #{args.join(", ")}.", level: :info, err: true
500
+ console_log "Blacklisted #{args.join(", ")}."
606
501
  end
607
502
  end
608
503
  end
609
504
 
610
505
  desc "Add tags to post(s)"
611
- arg_name "tags... file(s)", :multiple
506
+ arg_name "tags", :multiple
507
+ arg_name "file_pattern"
612
508
  command :add do |c|
613
- c.desc "Format to use when outputting tags to console: list, json, plist, csv, yaml, or complete. Defaults to yaml."
614
- c.long_desc "Use 'complete' to output full text when input is STDIN. First two letters of format are also accepted."
509
+ c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
615
510
  c.arg_name "output_format"
616
511
  c.default_value "yaml"
617
- c.flag %i[f format], :must_match => /^(csv?|l(?:i(?:st)?)?|y(?:a(?:ml)?)?|j(?:s(?:on)?)?|p(?:l(?:ist)?)?|co(?:mp(?:lete)?)?)$/, :type => String
512
+ c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
618
513
 
619
514
  c.action do |global_options, options, args|
620
515
  files = []
@@ -628,40 +523,30 @@ command :add do |c|
628
523
  end
629
524
  end
630
525
 
631
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
526
+ files.concat(@piped_content.split("\n")) if @piped_content
632
527
 
633
- # if files.empty?
634
- # if @jt.default_post_location && File.exist?(@jt.default_post_location)
635
- # files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
636
- # end
637
- # end
638
- # raise NoValidFile if files.empty?
639
- raise NoTagsFound, "No tags to add specified" if new_tags.empty?
528
+ if files.empty?
529
+ if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
530
+ files = Dir.glob(@jt.default_post_location)
531
+ end
532
+ end
533
+ exit_now! "No valid filename in arguments" if files.empty?
534
+ exit_now! "No tags found in arguments" if new_tags.empty?
640
535
 
641
- if @piped_content && !@piped_content.file_list?(global_options[:null])
642
- tags = @jt.post_tags(@piped_content, true)
536
+ files.each { |file|
537
+ tags = @jt.post_tags(file)
643
538
  tags.concat(new_tags)
644
- tags.sort!
645
539
  tags.uniq!
646
- @util.console_log "STDIN:", level: :debug
647
- # TODO: Add debug level to console_log
648
- @util.output_tags(tags, format: options[:format], content: @piped_content)
649
- else
650
- files.each do |file|
651
- tags = @jt.post_tags(file)
652
- tags.concat(new_tags)
653
- tags.sort!
654
- tags.uniq!
655
- unless global_options[:t]
656
- @jt.update_file_tags(file, tags)
657
- @util.console_log "Updated tags for #{file}", log: true, level: :warn
658
- end
659
-
660
- @util.console_log level: :info
661
- @util.console_log "#{File.basename(file)}:", level: :info
662
- @util.output_tags(tags, :format => options[:format], :filename => file)
540
+ tags.sort!
541
+ unless global_options[:t]
542
+ @jt.update_file_tags(file, tags)
543
+ console_log "Updated tags for #{file}", :log => true
663
544
  end
664
- end
545
+
546
+ console_log
547
+ console_log File.basename(file) + ":"
548
+ output_tags(tags, :format => options[:format], :filename => file)
549
+ }
665
550
  end
666
551
  end
667
552
 
@@ -690,11 +575,11 @@ command :remove do |c|
690
575
  end
691
576
  end
692
577
 
693
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
578
+ files.concat(@piped_content.split("\n")) if @piped_content
694
579
 
695
580
  if files.empty?
696
- if @jt.default_post_location && File.exist?(@jt.default_post_location)
697
- files = Dir.glob(File.join(@jt.default_post_location, "*.#{@jt.post_extension}"))
581
+ if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
582
+ files = Dir.glob(@jt.default_post_location)
698
583
  end
699
584
  end
700
585
  exit_now! "No valid filename in arguments" if files.empty?
@@ -708,10 +593,10 @@ command :remove do |c|
708
593
  remove_tags.push($1.strip)
709
594
  end
710
595
  }
711
- @util.console_log "Found #{remove_tags.length} tags in #{File.basename(path)}...", level: :info, err: true
596
+ console_log "Found #{remove_tags.length} tags in #{File.basename(path)}..."
712
597
  end
713
598
 
714
- raise NoTagsFound if remove_tags.empty?
599
+ exit_now! "No tags found in input, my work here is done" if remove_tags.empty?
715
600
 
716
601
  files.each { |file|
717
602
  tags = @jt.post_tags(file)
@@ -726,12 +611,12 @@ command :remove do |c|
726
611
  }
727
612
  unless global_options[:t]
728
613
  @jt.update_file_tags(file, tags)
729
- @util.console_log "Updated tags for #{file}", log: true, level: :warn, err: true
614
+ console_log "Updated tags for #{file}", :log => true
730
615
  end
731
616
 
732
- @util.console_log level: :info
733
- @util.console_log "#{File.basename(file)}:", level: :info
734
- @util.output_tags(tags, { :format => options[:format], :filename => file })
617
+ console_log
618
+ console_log File.basename(file) + ":"
619
+ output_tags(tags, { :format => options[:format], :filename => file })
735
620
  }
736
621
  end
737
622
  end
@@ -744,14 +629,17 @@ command :tag do |c|
744
629
  c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist|complete)$/, :type => String, :default_value => "yaml"
745
630
 
746
631
  c.action do |global_options, options, args|
747
- if @piped_content && !@piped_content.file_list?(global_options[:null])
632
+ if @piped_content && !@piped_content.file_list?
748
633
  suggestions = @jt.suggest(@piped_content)
749
634
  if args.length > 0
750
- @util.console_log level: :info
751
- @util.console_log "STDIN:", level: info, err: true
635
+ console_log
636
+ console_log "STDIN:", :err => true
637
+ end
638
+ if options[:format] == "complete"
639
+ @jt.update_file_tags(@piped_content, suggestions, true)
640
+ else
641
+ output_tags(suggestions, :format => options[:format], :filename => nil)
752
642
  end
753
-
754
- @util.output_tags(suggestions, :format => options[:format], :filename => nil)
755
643
  end
756
644
 
757
645
  files = []
@@ -760,7 +648,7 @@ command :tag do |c|
760
648
  files.push(arg) if File.exist?(arg)
761
649
  end
762
650
 
763
- files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
651
+ files.concat(@piped_content.split("\n")) if @piped_content && @piped_content.file_list?
764
652
 
765
653
  files.each do |file|
766
654
  if File.exist?(File.expand_path(file))
@@ -769,37 +657,61 @@ command :tag do |c|
769
657
 
770
658
  unless global_options[:t]
771
659
  if @jt.update_file_tags(file, suggestions)
772
- @util.console_log level: :info
773
- @util.console_log "Updated file #{file} with:", log: true, level: :warn
660
+ console_log
661
+ console_log "Updated file #{file} with:", :log => true
774
662
  else
775
- @util.console_log level: :info
776
- @util.console_log "Failed to update #{file} with:", log: true, level: :error
663
+ console_log
664
+ console_log "Failed to update #{file} with:", :log => true
777
665
  end
778
666
  end
779
667
  if !global_options[:s] || global_options[:t]
780
668
  if args.length > 1
781
- @util.console_log level: :info
782
- @util.console_log "#{File.basename(file)}:", err: true, level: :info
669
+ console_log
670
+ console_log File.basename(file) + ":", :err => true, :log => true
783
671
  end
784
- @util.output_tags(suggestions, :format => options[:format], :filename => file)
672
+ output_tags(suggestions, :format => options[:format], :filename => file)
785
673
  end
786
674
  suggestions = nil
787
675
  else
788
- raise FileNotFound, "No such file: #{file}"
676
+ raise "No such file: #{file}"
789
677
  end
790
678
  end
791
679
  end
792
680
  end
793
681
 
794
- ##
795
- ## Move deprecated config files to new location
796
- ##
797
- ## @return [void]
798
- ##
799
- def update_deprecated_config
800
- if File.exist?(File.expand_path("~/.jtag"))
801
- FileUtils.mv(File.expand_path("~/.jtag"), File.expand_path("~/.config/jtag"))
802
- @util.console_log "Moved ~/.jtag to ~/.config/jtag", level: :info, err: true, log: false
682
+ def output_tags(tags, options)
683
+ format = options[:format] || "yaml"
684
+ print0 = options[:print0] || false
685
+ filename = options[:filename] || false
686
+ case format
687
+ when "list"
688
+ unless print0
689
+ console_log tags.join("\n")
690
+ else
691
+ console_log tags.map { |tag|
692
+ if tag.strip =~ /\b\s\b/
693
+ %Q{"#{tag.strip}"}
694
+ else
695
+ tag.strip
696
+ end
697
+ }.join(" ")
698
+ end
699
+ when "csv"
700
+ console_log tags.to_csv
701
+ when "json"
702
+ out = {}
703
+ out["tags"] = tags
704
+ out["path"] = filename if filename
705
+ console_log out.to_json
706
+ when "plist"
707
+ out = {}
708
+ out["path"] = filename if filename
709
+ console_log tags.to_plist
710
+ else
711
+ out = {}
712
+ options[:grouping] ||= "tags"
713
+ out[options[:grouping]] = tags
714
+ console_log out.to_yaml
803
715
  end
804
716
  end
805
717
 
@@ -814,43 +726,33 @@ end
814
726
  # end
815
727
 
816
728
  pre do |global, command, options, args|
817
- Signal.trap("PIPE", "EXIT")
818
-
819
- @util = JekyllTag::Util.new
820
- @util.config_target = if global[:config_dir]
821
- File.expand_path(global[:config_dir])
822
- else
823
- File.expand_path("~/.config/jtag")
824
- end
729
+ # Use skips_pre before a command to skip this block
730
+ # on that command only
731
+ @silent = global[:silent]
825
732
 
826
- # if global[:debug] == 0 or global[:silent], run silently
827
- @util.silent = global[:debug] == 0 || global[:silent]
733
+ Signal.trap("PIPE", "EXIT")
828
734
 
829
- # set up logging
830
- logfile = File.open(File.join(Dir.tmpdir, "jtag_actions.log"), "a")
735
+ @logfile = File.open(File.join(Dir.tmpdir, "jtag_actions.log"), "a")
831
736
 
832
- # set up logger
833
- @util.log = Logger.new(logfile, shift_age = 7, shift_size = 1048576)
737
+ @log = Logger.new(@logfile, shift_age = 7, shift_size = 1048576)
834
738
 
835
- # set log level based on flags
836
- @util.debug_level = @silent ? 0 : global[:debug]
739
+ # Check for old config directory and migrate if needed
740
+ migrate_old_config
837
741
 
838
- # check for config files, write them if they don't exist
839
- unless @util.config_files_complete?
840
- @util.write_config
841
- @util.console_log "Missing config files written to #{@util.config_target}. Please check your configuration.", err: true, level: :error
742
+ unless config_files_complete?
743
+ write_config
744
+ console_log "Missing config files written to #{@config_target}. Please check your configuration.", { :err => true }
842
745
  return false
843
746
  end
844
747
 
845
- configfile = File.join(@util.config_target, "config.yml")
748
+ configfile = File.expand_path("~/.config/jtag/config.yml")
846
749
 
847
- # load config file
848
- config = YAML.load_file(configfile)
849
- global[:config] = config.symbolize_keys
750
+ global[:config] = YAML::load(File.open(configfile, "r"))
751
+ global[:support] = File.expand_path("~/.config/jtag")
850
752
 
851
753
  @piped_content = $stdin.read if $stdin.fcntl(Fcntl::F_GETFL, 0) == 0 || $stdin.stat.pipe?
852
754
 
853
- @jt = JekyllTag::JTag.new(@util.config_target, global[:config])
755
+ @jt = JTag.new(global[:support], global[:config])
854
756
 
855
757
  true
856
758
  end
@@ -862,11 +764,7 @@ post do |global, command, options, args|
862
764
  end
863
765
 
864
766
  on_error do |exception|
865
- if exception.is_a?(NoValidFile) || exception.is_a?(FileNotFound) || exception.is_a?(NoTagsFound)
866
- @util.console_log "#{@jt.color(:red)}#{exception.message}", err: true, level: :error
867
- return false
868
- end
869
-
767
+ # Error logic here
870
768
  # return false to skip default error handling
871
769
  true
872
770
  end