jtag 0.1.18 → 0.1.21
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/.irbrc +6 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +3 -0
- data/Jekyll/plugins/autotag_gen.rb +58 -0
- data/Jekyll/source/_layouts/tags_json.html +11 -0
- data/README.md +159 -0
- data/Rakefile +113 -0
- data/bin/jtag +519 -359
- data/jtag.completion.bash +9 -0
- data/jtag.gemspec +25 -0
- data/lib/jtag/array.rb +48 -0
- data/lib/jtag/config_files/config.yml +3 -2
- data/lib/jtag/errors.rb +31 -0
- data/lib/jtag/hash.rb +103 -0
- data/lib/jtag/jekylltag.rb +267 -225
- data/lib/jtag/string.rb +238 -42
- data/lib/jtag/stupid_json.rb +319 -0
- data/lib/jtag/util.rb +237 -0
- data/lib/jtag/version.rb +1 -1
- data/lib/jtag.rb +19 -13
- data/mise.toml +2 -0
- metadata +34 -9
data/bin/jtag
CHANGED
@@ -1,236 +1,331 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "gli"
|
4
|
+
require "jtag"
|
5
|
+
require "fcntl"
|
5
6
|
|
6
7
|
include GLI::App
|
7
8
|
|
8
|
-
program_desc
|
9
|
+
program_desc "Autotagging for Jekyll"
|
9
10
|
|
10
11
|
version Jtag::VERSION
|
11
12
|
|
12
|
-
@config_files = %w{blacklist.txt config.yml stopwords.txt synonyms.yml}
|
13
|
-
@config_target = File.expand_path("~/.jtag")
|
14
13
|
@piped_content = nil
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
21
|
|
23
|
-
desc
|
24
|
-
|
25
|
-
|
26
|
-
flag %i[d debug], :must_match => /\d+/, :type => Integer, :default_value => 0
|
22
|
+
desc "Path to alternate configuration directory"
|
23
|
+
arg_name "config_dir"
|
24
|
+
flag %i[c config_dir], :type => String
|
27
25
|
|
28
|
-
desc
|
29
|
-
|
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"
|
27
|
+
arg_name "debug_level"
|
28
|
+
flag %i[d debug], :must_match => /\d+/, :type => Integer, :default_value => 3
|
30
29
|
|
31
|
-
desc
|
32
|
-
switch %i[i case_insensitive]
|
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
|
33
32
|
|
34
|
-
desc "
|
35
|
-
|
36
|
-
default_value false
|
37
|
-
switch %i[t test]
|
33
|
+
desc "Run silently: Suppress all output to STDOUT, same as debug level 0"
|
34
|
+
switch %i[s silent], negatable: false
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
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"
|
37
|
+
default_value false
|
38
|
+
switch %i[t test], negatable: false
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
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]
|
50
46
|
|
51
|
-
|
47
|
+
c.desc "Ignore prompts and answer yes to all questions."
|
48
|
+
c.switch %i[y yes], negatable: false
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
warn msg
|
57
|
-
end
|
58
|
-
end
|
50
|
+
c.desc "Set a specific configuration value"
|
51
|
+
c.arg_name "key"
|
52
|
+
c.flag %i[s set]
|
59
53
|
|
60
|
-
desc
|
61
|
-
|
62
|
-
c.
|
63
|
-
c.switch [:r,'reset']
|
54
|
+
c.desc "The value to set for key"
|
55
|
+
c.arg_name "value"
|
56
|
+
c.flag %i[v value]
|
64
57
|
|
58
|
+
# Skip the pre block for this command
|
65
59
|
c.skips_pre
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
60
|
+
|
61
|
+
# Define the action to be taken when the command is executed
|
62
|
+
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)
|
73
68
|
end
|
74
|
-
|
75
|
-
write_config(false)
|
69
|
+
return
|
76
70
|
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def write_config(atomic=false)
|
81
|
-
gem_root = Gem.loaded_specs['jtag'].full_gem_path
|
82
|
-
gem_lib = File.join(gem_root, 'lib')
|
83
|
-
config_source = File.join(gem_lib,'/jtag/config_files')
|
84
71
|
|
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
|
85
81
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
@config_files.each do |file|
|
95
|
-
unless File.exist?(File.join(@config_target,file))
|
96
|
-
source_file = File.join(config_source,file)
|
97
|
-
target_file = File.join(@config_target,file)
|
98
|
-
FileUtils.cp(source_file, target_file)
|
99
|
-
console_log "Config file #{file} added."
|
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)
|
100
90
|
end
|
101
91
|
end
|
102
|
-
console_log
|
103
|
-
console_log "Configuration files are located in the folder: " + @config_target
|
104
|
-
console_log %Q{Make sure that "tags_location" in the config.yml file is set to your tags json file.}
|
105
92
|
end
|
106
93
|
|
107
|
-
desc
|
108
|
-
long_desc
|
109
|
-
arg_name
|
94
|
+
desc "List tags, optionally filter for keywords/regular expressions (OR)"
|
95
|
+
long_desc "This command can be used to find the exact format for a given tag to keep spaces, underscores, capitalization and pluralization consistent"
|
96
|
+
arg_name "keyword", :multiple
|
110
97
|
command :search do |c|
|
111
|
-
c.desc
|
112
|
-
c.arg_name
|
113
|
-
c.default_value
|
98
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
99
|
+
c.arg_name "output_format"
|
100
|
+
c.default_value "yaml"
|
114
101
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/i, :type => String
|
115
102
|
|
116
|
-
c.desc
|
117
|
-
c.arg_name
|
103
|
+
c.desc "Include tag counts"
|
104
|
+
c.arg_name "counts"
|
118
105
|
c.default_value false
|
119
106
|
c.switch %i[c counts]
|
120
107
|
|
121
|
-
c.
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
+
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}"))
|
130
156
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
134
170
|
else
|
135
|
-
tags.
|
171
|
+
tags = @jt.tags({ :counts => true, :files => files })
|
136
172
|
end
|
137
|
-
output_tags(tags,{ :format => options[:format] })
|
138
173
|
else
|
139
|
-
tags.
|
140
|
-
|
141
|
-
|
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) } }
|
142
206
|
else
|
143
|
-
|
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) } }
|
144
210
|
end
|
145
|
-
output_tags(tags,{ :format => options[:format] })
|
146
211
|
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] })
|
147
227
|
end
|
148
228
|
end
|
149
229
|
|
150
|
-
desc
|
151
|
-
arg_name
|
230
|
+
desc "List posts with tag(s)"
|
231
|
+
arg_name "tags", :multiple
|
152
232
|
command :posts_tagged do |c|
|
153
|
-
c.desc
|
154
|
-
c.arg_name
|
155
|
-
c.default_value
|
233
|
+
c.desc "Boolean operator for multiple tags (AND/OR/NOT)"
|
234
|
+
c.arg_name "bool"
|
235
|
+
c.default_value "OR"
|
156
236
|
c.flag %i[b bool], :must_match => /(AND|OR|NOT)/i, :type => String
|
157
237
|
|
158
|
-
c.desc
|
159
|
-
c.arg_name
|
160
|
-
c.default_value
|
238
|
+
c.desc "Format to use when outputting file list: list, json, plist, csv or yaml. Defaults to list."
|
239
|
+
c.arg_name "output_format"
|
240
|
+
c.default_value "list"
|
161
241
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
162
242
|
|
163
|
-
c.desc
|
243
|
+
c.desc "If output format is list, print without newlines."
|
164
244
|
c.switch [:print0]
|
165
245
|
|
166
|
-
c.action do |global_options,options,args|
|
246
|
+
c.action do |global_options, options, args|
|
247
|
+
# Convert the boolean operator to uppercase for consistency
|
167
248
|
bool = options[:bool].upcase
|
249
|
+
|
250
|
+
# Initialize arrays to store files, tags, and matches
|
168
251
|
files = []
|
169
252
|
tags = []
|
170
253
|
matches = []
|
254
|
+
|
255
|
+
# Separate files and tags from the arguments
|
171
256
|
args.length.times do
|
172
257
|
arg = args.pop
|
173
258
|
if File.exist?(arg)
|
174
|
-
files.push(arg)
|
259
|
+
files.push(arg) # Add existing files to the files array
|
175
260
|
else
|
176
|
-
tags.push(arg)
|
261
|
+
tags.push(arg) # Add non-file arguments to the tags array
|
177
262
|
end
|
178
263
|
end
|
264
|
+
|
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])
|
267
|
+
|
268
|
+
# If no files are provided, use the default post location
|
179
269
|
if files.empty?
|
180
|
-
if @jt.default_post_location && File.exist?(
|
181
|
-
files
|
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}"))
|
182
274
|
end
|
183
275
|
end
|
184
|
-
|
185
|
-
|
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|
|
186
282
|
if File.exist?(file)
|
187
|
-
post_tags = @jt.post_tags(file)
|
283
|
+
post_tags = @jt.post_tags(file) # Get tags for the current file
|
188
284
|
|
189
285
|
if bool == "AND"
|
286
|
+
# Check if all tags are present in the post tags
|
190
287
|
matched = 0
|
191
|
-
tags.each {|tag|
|
192
|
-
matched += 1 if post_tags.include?(tag)
|
193
|
-
}
|
288
|
+
tags.each { |tag| matched += 1 if post_tags.include?(tag) }
|
194
289
|
matches.push(file) if matched == tags.length
|
195
290
|
elsif bool == "NOT"
|
291
|
+
# Check if none of the tags are present in the post tags
|
196
292
|
matched = false
|
197
|
-
tags.each {|tag|
|
198
|
-
matched = true if post_tags.include?(tag)
|
199
|
-
}
|
293
|
+
tags.each { |tag| matched = true if post_tags.include?(tag) }
|
200
294
|
matches.push(file) unless matched
|
201
295
|
else
|
202
|
-
tags
|
203
|
-
|
204
|
-
matches.push(file) unless matches.include?(file)
|
205
|
-
end
|
206
|
-
}
|
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) }
|
207
298
|
end
|
208
299
|
else
|
209
|
-
|
300
|
+
# Exit if the file is not found
|
301
|
+
raise FileNotFound, "File not found: #{file}"
|
210
302
|
end
|
211
|
-
|
303
|
+
end
|
212
304
|
|
305
|
+
# Create a search string from the tags and boolean operator
|
213
306
|
search_string = tags.join(" #{bool} ")
|
307
|
+
|
308
|
+
# Output the results
|
214
309
|
if matches.empty?
|
215
|
-
console_log "No matching files found for #{search_string}"
|
310
|
+
@util.console_log "No matching files found for #{search_string}"
|
216
311
|
else
|
217
|
-
console_log "(#{search_string})", {:
|
218
|
-
output_tags(matches, { :
|
312
|
+
@util.console_log "(#{search_string})", { err: true }
|
313
|
+
@util.output_tags(matches, { format: options[:format], print0: options[:print0], grouping: "files" })
|
219
314
|
end
|
220
315
|
end
|
221
316
|
end
|
222
317
|
|
223
|
-
desc
|
224
|
-
arg_name
|
318
|
+
desc "Show tags with fewer than X posts attached to them, optionally removing them from specified posts"
|
319
|
+
arg_name "file_pattern", :multiple
|
225
320
|
command :loners do |c|
|
226
|
-
c.desc
|
227
|
-
c.arg_name
|
228
|
-
c.default_value
|
321
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
322
|
+
c.arg_name "output_format"
|
323
|
+
c.default_value "yaml"
|
229
324
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
230
325
|
|
231
|
-
c.desc
|
232
|
-
c.arg_name
|
233
|
-
c.default_value
|
326
|
+
c.desc "Upper limit for how many posts a tag can be attached to and still be a loner"
|
327
|
+
c.arg_name "max"
|
328
|
+
c.default_value "2"
|
234
329
|
c.flag %i[m max], :default_value => 2, :must_match => /^\d+$/
|
235
330
|
|
236
331
|
c.desc "Remove tags with fewer than X posts attached"
|
@@ -239,20 +334,53 @@ command :loners do |c|
|
|
239
334
|
c.desc "Display output without attached occurence counts"
|
240
335
|
c.switch [:no_counts], :default_value => false
|
241
336
|
|
242
|
-
c.desc
|
243
|
-
c.arg_name
|
337
|
+
c.desc "Output a file list of tags that can be edited and passed back in for removing"
|
338
|
+
c.arg_name "filename"
|
244
339
|
c.flag %i[e edit], :default_value => false, :type => String
|
245
340
|
|
246
|
-
c.action do |global_options,options,args|
|
341
|
+
c.action do |global_options, options, args|
|
342
|
+
# Convert the max option to an integer
|
247
343
|
max = options[:m].to_i
|
248
|
-
|
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)
|
249
373
|
loner_tags.delete_if { |tag|
|
250
|
-
tag.class == FalseClass || tag[
|
374
|
+
tag.class == FalseClass || tag["count"] > max
|
251
375
|
}
|
252
|
-
loner_tags.sort_by! {|tag| tag['count']}
|
253
376
|
|
377
|
+
# Sort loner tags by count
|
378
|
+
loner_tags.sort_by! { |tag| tag["count"] }
|
379
|
+
|
380
|
+
# Exit if no tags matched the criteria
|
254
381
|
exit_now! "No tags matched the criteria" if loner_tags.empty? || loner_tags.nil?
|
255
382
|
|
383
|
+
# If the edit option is provided, write the loner tags to a file for editing
|
256
384
|
if options[:e]
|
257
385
|
path = File.expand_path(options[:e])
|
258
386
|
while File.exist?(path)
|
@@ -261,45 +389,51 @@ command :loners do |c|
|
|
261
389
|
$1.next! + $2
|
262
390
|
end
|
263
391
|
else
|
264
|
-
path.sub!(/(\.[^\.]+?)?$/,'01\1')
|
392
|
+
path.sub!(/(\.[^\.]+?)?$/, '01\1')
|
265
393
|
end
|
266
394
|
end
|
267
|
-
File.open(path,
|
395
|
+
File.open(path, "w+") do |f|
|
268
396
|
f.puts "# Edit this file to remove tags you want to keep,"
|
269
397
|
f.puts "# then run `jtag remove -p '#{path}' [/path/to/posts/*.md]`"
|
270
|
-
f.puts "# to remove any tags left in
|
398
|
+
f.puts "# to remove any tags left in this file."
|
271
399
|
f.puts "#"
|
272
400
|
f.puts "# The post counts are included for your convenience, and will"
|
273
401
|
f.puts "# be automatically ignored when reading the list back in."
|
274
402
|
f.puts "#"
|
275
403
|
f.puts "# Lines beginning with a # are comments (ignored), but you probably figured that out."
|
276
|
-
loner_tags.each{ |t|
|
277
|
-
f.printf "% 3d |\t%s\n", t[
|
404
|
+
loner_tags.each { |t|
|
405
|
+
f.printf "% 3d |\t%s\n", t["count"], t["name"]
|
278
406
|
}
|
279
407
|
end
|
280
408
|
|
281
|
-
console_log "A list of results and instructions for use have been written to #{path}."
|
282
|
-
if ENV[
|
283
|
-
console_log
|
284
|
-
print "Would you like to open the file in #{ENV[
|
409
|
+
@util.console_log "A list of results and instructions for use have been written to #{path}."
|
410
|
+
if ENV["EDITOR"]
|
411
|
+
@util.console_log
|
412
|
+
print "Would you like to open the file in #{ENV["EDITOR"]} now? (y/N) "
|
285
413
|
input = STDIN.gets
|
286
414
|
if input =~ /^y/i
|
287
|
-
system "#{ENV[
|
415
|
+
system "#{ENV["EDITOR"]} '#{path}'"
|
288
416
|
end
|
289
417
|
end
|
418
|
+
|
419
|
+
# If the remove option is provided, remove the loner tags from the specified files
|
290
420
|
elsif options[:r]
|
291
421
|
files = []
|
292
422
|
args.length.times do
|
293
423
|
arg = args.pop
|
294
424
|
files.push(arg) if File.exist?(arg)
|
295
425
|
end
|
426
|
+
|
427
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
428
|
+
|
296
429
|
if files.empty?
|
297
|
-
if @jt.default_post_location && File.exist?(
|
298
|
-
files = Dir.glob(@jt.default_post_location)
|
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}"))
|
299
432
|
end
|
300
433
|
end
|
301
434
|
exit_now! "No valid filename in arguments" if files.empty?
|
302
|
-
|
435
|
+
|
436
|
+
files.each { |file|
|
303
437
|
tags = @jt.post_tags(file)
|
304
438
|
loner_tags.each { |d|
|
305
439
|
tags.delete_if { |tag|
|
@@ -312,99 +446,108 @@ command :loners do |c|
|
|
312
446
|
}
|
313
447
|
unless global_options[:t]
|
314
448
|
@jt.update_file_tags(file, tags)
|
315
|
-
console_log "Updated tags for #{file}", :log => true
|
449
|
+
@util.console_log "Updated tags for #{file}", :log => true, level: :warn
|
316
450
|
end
|
317
451
|
|
318
|
-
console_log
|
319
|
-
console_log File.basename(file) + ":"
|
320
|
-
output_tags(tags, :format => options[:format], :filename => file
|
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)
|
321
455
|
}
|
456
|
+
|
457
|
+
# Otherwise, output the loner tags
|
322
458
|
else
|
323
|
-
output_tags(loner_tags.map{|tag|
|
324
|
-
count = options[:no_counts] ? "" : " (#{tag[
|
325
|
-
"#{tag[
|
459
|
+
@util.output_tags(loner_tags.map { |tag|
|
460
|
+
count = options[:no_counts] ? "" : " (#{tag["count"]})"
|
461
|
+
"#{tag["name"]}#{count}"
|
462
|
+
}, :format => options[:format])
|
326
463
|
end
|
327
464
|
end
|
328
465
|
end
|
329
466
|
|
330
|
-
|
331
|
-
|
332
|
-
arg_name 'file_pattern', :multiple
|
467
|
+
desc "Show the current tags for posts"
|
468
|
+
arg_name "file_pattern", :multiple
|
333
469
|
command :tags do |c|
|
334
|
-
c.desc
|
335
|
-
c.arg_name
|
336
|
-
c.default_value
|
470
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
471
|
+
c.arg_name "output_format"
|
472
|
+
c.default_value "yaml"
|
337
473
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
338
474
|
|
339
|
-
c.action do |global_options,options,args|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
if
|
344
|
-
|
345
|
-
console_log 'STDIN:'
|
346
|
-
end
|
347
|
-
if tags.empty? || tags.nil?
|
348
|
-
console_log "No tags in post", {:err => true}
|
349
|
-
else
|
350
|
-
output_tags(tags,{ :format => options[:format] })
|
475
|
+
c.action do |global_options, options, args|
|
476
|
+
files = []
|
477
|
+
args.length.times do
|
478
|
+
arg = args.pop
|
479
|
+
if File.exist?(arg)
|
480
|
+
files.push(arg)
|
351
481
|
end
|
352
482
|
end
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
output_tags(tags,{ :format => options[:format], :filename => file })
|
364
|
-
end
|
365
|
-
else
|
366
|
-
raise "File not found: #{file}"
|
483
|
+
|
484
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
485
|
+
|
486
|
+
tags = []
|
487
|
+
files.each do |file|
|
488
|
+
tags.concat(@jt.post_tags(file)) if File.exist?(file)
|
489
|
+
|
490
|
+
if args.length > 0
|
491
|
+
@util.console_log level: :info
|
492
|
+
@util.console_log "STDIN:", level: :info
|
367
493
|
end
|
368
|
-
|
494
|
+
end
|
495
|
+
|
496
|
+
if tags.empty? || tags.nil?
|
497
|
+
@util.console_log "No tags in post", err: true, level: :info
|
498
|
+
else
|
499
|
+
tags.sort!
|
500
|
+
tags.uniq!
|
501
|
+
@util.output_tags(tags, { :format => options[:format] })
|
502
|
+
end
|
369
503
|
end
|
370
504
|
end
|
371
505
|
|
372
|
-
|
373
|
-
|
374
|
-
arg_name 'file_pattern', :multiple
|
506
|
+
desc "Sort the existing tags for posts"
|
507
|
+
arg_name "file_pattern", :multiple
|
375
508
|
command :sort do |c|
|
376
|
-
c.desc
|
377
|
-
c.arg_name
|
378
|
-
c.default_value
|
509
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
510
|
+
c.arg_name "output_format"
|
511
|
+
c.default_value "yaml"
|
379
512
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
380
513
|
|
381
|
-
c.action do |global_options,options,args|
|
382
|
-
|
514
|
+
c.action do |global_options, options, args|
|
515
|
+
files = []
|
516
|
+
args.length.times do
|
517
|
+
arg = args.pop
|
518
|
+
if File.exist?(arg)
|
519
|
+
files.push(arg)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
524
|
+
|
525
|
+
files.each do |file|
|
383
526
|
tags = @jt.post_tags(file)
|
384
527
|
tags.uniq!
|
385
528
|
tags.sort!
|
386
529
|
unless global_options[:t]
|
387
530
|
@jt.update_file_tags(file, tags)
|
388
531
|
end
|
389
|
-
console_log
|
390
|
-
console_log File.basename(file)
|
532
|
+
@util.console_log level: :info
|
533
|
+
@util.console_log "#{File.basename(file)}:", level: :info
|
391
534
|
|
392
535
|
if tags.empty? || tags.nil?
|
393
|
-
console_log "No tags in post",
|
536
|
+
@util.console_log "No tags in post", err: true, level: :info
|
394
537
|
else
|
395
|
-
output_tags(tags,{ :format => options[:format], :filename => file })
|
538
|
+
@util.output_tags(tags, { :format => options[:format], :filename => file })
|
396
539
|
end
|
397
|
-
|
540
|
+
end
|
398
541
|
end
|
399
542
|
end
|
400
543
|
|
401
|
-
desc
|
402
|
-
long_desc
|
403
|
-
arg_name
|
544
|
+
desc "Merge multiple tags into one"
|
545
|
+
long_desc "Scans the specified posts for any of the tags, merging any found into the last one in the list"
|
546
|
+
arg_name "tags to merge merge_tag"
|
404
547
|
command :merge do |c|
|
405
|
-
c.desc
|
406
|
-
c.arg_name
|
407
|
-
c.default_value
|
548
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
549
|
+
c.arg_name "output_format"
|
550
|
+
c.default_value "yaml"
|
408
551
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
409
552
|
|
410
553
|
c.action do |global_options, options, args|
|
@@ -418,62 +561,62 @@ command :merge do |c|
|
|
418
561
|
tags.push(arg)
|
419
562
|
end
|
420
563
|
end
|
564
|
+
|
565
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
566
|
+
|
421
567
|
if files.empty?
|
422
|
-
if @jt.default_post_location && File.exist?(
|
423
|
-
files = Dir.glob(@jt.default_post_location)
|
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}"))
|
424
570
|
end
|
425
571
|
end
|
426
572
|
exit_now! "No valid filename in arguments" if files.empty?
|
427
573
|
exit_now! "Needs at least two tag inputs, one or more to merge, one to merge to" if tags.length < 2
|
428
574
|
tags.reverse!
|
429
575
|
merge_tag = tags.pop
|
430
|
-
console_log %
|
431
|
-
files.each
|
432
|
-
new_tags = @jt.merge_tags(tags,merge_tag,file)
|
576
|
+
@util.console_log %(Merging #{tags.join(", ")} to #{merge_tag}), level: :info
|
577
|
+
files.each do |file|
|
578
|
+
new_tags = @jt.merge_tags(tags, merge_tag, file)
|
433
579
|
next unless new_tags
|
434
|
-
unless global_options[:
|
580
|
+
unless global_options[:test]
|
435
581
|
@jt.update_file_tags(file, new_tags)
|
436
|
-
console_log
|
437
|
-
console_log "Updated tags for #{file}", :
|
582
|
+
@util.console_log level: :info
|
583
|
+
@util.console_log "Updated tags for #{file}", log: true, level: :warn
|
438
584
|
end
|
439
585
|
|
440
|
-
console_log
|
441
|
-
console_log File.basename(file)
|
442
|
-
output_tags(new_tags,{ :
|
443
|
-
|
444
|
-
}
|
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
|
445
590
|
end
|
446
591
|
end
|
447
592
|
|
448
|
-
|
449
|
-
|
450
|
-
arg_name 'tag [tag2...]'
|
593
|
+
desc "Blacklist a specific tag"
|
594
|
+
arg_name "tag [tag2...]"
|
451
595
|
command :blacklist do |c|
|
452
596
|
c.desc "Remove (unblacklist) the arguments"
|
453
|
-
c.switch [:r,
|
597
|
+
c.switch [:r, "remove"]
|
454
598
|
|
455
|
-
c.action do |global_options,options,args|
|
599
|
+
c.action do |global_options, options, args|
|
456
600
|
if options[:r]
|
457
601
|
@jt.unblacklist(args)
|
458
|
-
console_log "Removed #{args.join(", ")} from blacklist."
|
602
|
+
@util.console_log "Removed #{args.join(", ")} from blacklist.", level: :info, err: true
|
459
603
|
else
|
460
604
|
@jt.blacklist(args)
|
461
|
-
console_log "Blacklisted #{args.join(", ")}."
|
605
|
+
@util.console_log "Blacklisted #{args.join(", ")}.", level: :info, err: true
|
462
606
|
end
|
463
607
|
end
|
464
608
|
end
|
465
609
|
|
466
|
-
desc
|
467
|
-
arg_name
|
468
|
-
arg_name 'file_pattern'
|
610
|
+
desc "Add tags to post(s)"
|
611
|
+
arg_name "tags... file(s)", :multiple
|
469
612
|
command :add do |c|
|
470
|
-
c.desc
|
471
|
-
c.
|
472
|
-
c.
|
473
|
-
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."
|
615
|
+
c.arg_name "output_format"
|
616
|
+
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
|
474
618
|
|
475
|
-
|
476
|
-
c.action do |global_options,options,args|
|
619
|
+
c.action do |global_options, options, args|
|
477
620
|
files = []
|
478
621
|
new_tags = []
|
479
622
|
args.length.times do
|
@@ -484,45 +627,58 @@ command :add do |c|
|
|
484
627
|
new_tags.push(arg)
|
485
628
|
end
|
486
629
|
end
|
487
|
-
if files.empty?
|
488
|
-
if @jt.default_post_location && File.exist?(File.dirname(@jt.default_post_location))
|
489
|
-
files = Dir.glob(@jt.default_post_location)
|
490
|
-
end
|
491
|
-
end
|
492
|
-
exit_now! "No valid filename in arguments" if files.empty?
|
493
|
-
exit_now! "No tags found in arguments" if new_tags.empty?
|
494
630
|
|
495
|
-
files.
|
496
|
-
|
631
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
632
|
+
|
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?
|
640
|
+
|
641
|
+
if @piped_content && !@piped_content.file_list?(global_options[:null])
|
642
|
+
tags = @jt.post_tags(@piped_content, true)
|
497
643
|
tags.concat(new_tags)
|
498
|
-
tags.uniq!
|
499
644
|
tags.sort!
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
645
|
+
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
|
504
659
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
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)
|
663
|
+
end
|
664
|
+
end
|
509
665
|
end
|
510
666
|
end
|
511
667
|
|
512
|
-
desc
|
513
|
-
arg_name
|
668
|
+
desc "Remove tags from post(s)"
|
669
|
+
arg_name "tags", :multiple
|
514
670
|
command :remove do |c|
|
515
|
-
c.desc
|
516
|
-
c.arg_name
|
517
|
-
c.default_value
|
671
|
+
c.desc "Format to use when outputting tags to console: list, json, plist, csv or yaml. Defaults to yaml."
|
672
|
+
c.arg_name "output_format"
|
673
|
+
c.default_value "yaml"
|
518
674
|
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist)$/, :type => String
|
519
675
|
|
520
|
-
c.desc
|
521
|
-
c.long_desc
|
522
|
-
c.arg_name
|
676
|
+
c.desc "A filepath to a list of tags to be removed"
|
677
|
+
c.long_desc "One tag per line, and leading numbers and pipes (|) will be ignored. This file format is generated automatically by the `loners` command, but any text file will do the trick."
|
678
|
+
c.arg_name "input_file"
|
523
679
|
c.flag %i[p path], :type => String
|
524
680
|
|
525
|
-
c.action do |global_options,options,args|
|
681
|
+
c.action do |global_options, options, args|
|
526
682
|
files = []
|
527
683
|
remove_tags = []
|
528
684
|
args.length.times do
|
@@ -533,9 +689,12 @@ command :remove do |c|
|
|
533
689
|
remove_tags.push(arg) unless options[:p]
|
534
690
|
end
|
535
691
|
end
|
692
|
+
|
693
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
694
|
+
|
536
695
|
if files.empty?
|
537
|
-
if @jt.default_post_location && File.exist?(
|
538
|
-
files = Dir.glob(@jt.default_post_location)
|
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}"))
|
539
698
|
end
|
540
699
|
end
|
541
700
|
exit_now! "No valid filename in arguments" if files.empty?
|
@@ -543,18 +702,18 @@ command :remove do |c|
|
|
543
702
|
if options[:p]
|
544
703
|
path = File.expand_path(options[:p])
|
545
704
|
exit_now! "Input file does not appear to be where you think it is." unless File.exist?(path)
|
546
|
-
IO.read(path).each_line {|l|
|
705
|
+
IO.read(path).each_line { |l|
|
547
706
|
next if l =~ /^\s*#/
|
548
707
|
if l =~ /^(?:[\s\d])*(?:\|\s*)?(\S.*?)$/
|
549
708
|
remove_tags.push($1.strip)
|
550
709
|
end
|
551
710
|
}
|
552
|
-
console_log "Found #{remove_tags.length} tags in #{File.basename(path)}..."
|
711
|
+
@util.console_log "Found #{remove_tags.length} tags in #{File.basename(path)}...", level: :info, err: true
|
553
712
|
end
|
554
713
|
|
555
|
-
|
714
|
+
raise NoTagsFound if remove_tags.empty?
|
556
715
|
|
557
|
-
files.each {|file|
|
716
|
+
files.each { |file|
|
558
717
|
tags = @jt.post_tags(file)
|
559
718
|
remove_tags.each { |d|
|
560
719
|
tags.delete_if { |tag|
|
@@ -567,98 +726,80 @@ command :remove do |c|
|
|
567
726
|
}
|
568
727
|
unless global_options[:t]
|
569
728
|
@jt.update_file_tags(file, tags)
|
570
|
-
console_log "Updated tags for #{file}", :
|
729
|
+
@util.console_log "Updated tags for #{file}", log: true, level: :warn, err: true
|
571
730
|
end
|
572
731
|
|
573
|
-
console_log
|
574
|
-
console_log File.basename(file)
|
575
|
-
output_tags(tags,{ :format => options[:format], :filename => file })
|
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 })
|
576
735
|
}
|
577
736
|
end
|
578
737
|
end
|
579
738
|
|
580
|
-
desc
|
581
|
-
arg_name
|
739
|
+
desc "Generate a list of recommended tags, updating the file (unless dry run)"
|
740
|
+
arg_name "file_pattern", :multiple
|
582
741
|
command :tag do |c|
|
583
742
|
c.desc 'Format to use when outputting tags to console: list, json, plist, csv or yaml. Use "complete" to output full text when input is STDIN.'
|
584
|
-
c.arg_name
|
585
|
-
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist|complete)$/, :type => String, :default_value =>
|
743
|
+
c.arg_name "output_format"
|
744
|
+
c.flag %i[f format], :must_match => /^(csv|list|yaml|json|plist|complete)$/, :type => String, :default_value => "yaml"
|
586
745
|
|
587
746
|
c.action do |global_options, options, args|
|
588
|
-
if @piped_content
|
747
|
+
if @piped_content && !@piped_content.file_list?(global_options[:null])
|
589
748
|
suggestions = @jt.suggest(@piped_content)
|
590
749
|
if args.length > 0
|
591
|
-
console_log
|
592
|
-
console_log
|
593
|
-
end
|
594
|
-
if options[:format] == 'complete'
|
595
|
-
@jt.update_file_tags(@piped_content, suggestions, true)
|
596
|
-
else
|
597
|
-
output_tags(suggestions, :format => options[:format], :filename => nil)
|
750
|
+
@util.console_log level: :info
|
751
|
+
@util.console_log "STDIN:", level: info, err: true
|
598
752
|
end
|
753
|
+
|
754
|
+
@util.output_tags(suggestions, :format => options[:format], :filename => nil)
|
599
755
|
end
|
600
|
-
|
756
|
+
|
757
|
+
files = []
|
758
|
+
args.length.times do
|
759
|
+
arg = args.pop
|
760
|
+
files.push(arg) if File.exist?(arg)
|
761
|
+
end
|
762
|
+
|
763
|
+
files.concat(@piped_content.split(global_options[:null] ? "\x00" : "\n")) if @piped_content && @piped_content.file_list?(global_options[:null])
|
764
|
+
|
765
|
+
files.each do |file|
|
601
766
|
if File.exist?(File.expand_path(file))
|
602
767
|
input = IO.read(File.expand_path(file))
|
603
768
|
suggestions = @jt.suggest(input)
|
604
769
|
|
605
770
|
unless global_options[:t]
|
606
771
|
if @jt.update_file_tags(file, suggestions)
|
607
|
-
console_log
|
608
|
-
console_log "Updated file #{file} with:", :
|
772
|
+
@util.console_log level: :info
|
773
|
+
@util.console_log "Updated file #{file} with:", log: true, level: :warn
|
609
774
|
else
|
610
|
-
console_log
|
611
|
-
console_log "Failed to update #{file} with:", :
|
775
|
+
@util.console_log level: :info
|
776
|
+
@util.console_log "Failed to update #{file} with:", log: true, level: :error
|
612
777
|
end
|
613
778
|
end
|
614
779
|
if !global_options[:s] || global_options[:t]
|
615
780
|
if args.length > 1
|
616
|
-
console_log
|
617
|
-
console_log File.basename(file)
|
781
|
+
@util.console_log level: :info
|
782
|
+
@util.console_log "#{File.basename(file)}:", err: true, level: :info
|
618
783
|
end
|
619
|
-
output_tags(suggestions, :format => options[:format], :filename => file
|
784
|
+
@util.output_tags(suggestions, :format => options[:format], :filename => file)
|
620
785
|
end
|
621
786
|
suggestions = nil
|
622
787
|
else
|
623
|
-
raise "No such file: #{file}"
|
788
|
+
raise FileNotFound, "No such file: #{file}"
|
624
789
|
end
|
625
|
-
|
790
|
+
end
|
626
791
|
end
|
627
792
|
end
|
628
793
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
console_log tags.map {|tag|
|
639
|
-
if tag.strip =~ /\b\s\b/
|
640
|
-
%Q{"#{tag.strip}"}
|
641
|
-
else
|
642
|
-
tag.strip
|
643
|
-
end
|
644
|
-
}.join(" ")
|
645
|
-
end
|
646
|
-
when 'csv'
|
647
|
-
console_log tags.to_csv
|
648
|
-
when 'json'
|
649
|
-
out = {}
|
650
|
-
out['tags'] = tags
|
651
|
-
out['path'] = filename if filename
|
652
|
-
console_log out.to_json
|
653
|
-
when 'plist'
|
654
|
-
out = {}
|
655
|
-
out['path'] = filename if filename
|
656
|
-
console_log tags.to_plist
|
657
|
-
else
|
658
|
-
out = {}
|
659
|
-
options[:grouping] ||= "tags"
|
660
|
-
out[options[:grouping]] = tags
|
661
|
-
console_log out.to_yaml
|
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
|
662
803
|
end
|
663
804
|
end
|
664
805
|
|
@@ -673,40 +814,59 @@ end
|
|
673
814
|
# end
|
674
815
|
|
675
816
|
pre do |global, command, options, args|
|
676
|
-
|
677
|
-
|
678
|
-
@
|
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
|
825
|
+
|
826
|
+
# if global[:debug] == 0 or global[:silent], run silently
|
827
|
+
@util.silent = global[:debug] == 0 || global[:silent]
|
828
|
+
|
829
|
+
# set up logging
|
830
|
+
logfile = File.open(File.join(Dir.tmpdir, "jtag_actions.log"), "a")
|
679
831
|
|
680
|
-
|
832
|
+
# set up logger
|
833
|
+
@util.log = Logger.new(logfile, shift_age = 7, shift_size = 1048576)
|
681
834
|
|
682
|
-
|
835
|
+
# set log level based on flags
|
836
|
+
@util.debug_level = @silent ? 0 : global[:debug]
|
683
837
|
|
684
|
-
|
685
|
-
|
686
|
-
|
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
|
687
842
|
return false
|
688
843
|
end
|
689
844
|
|
690
|
-
configfile = File.
|
845
|
+
configfile = File.join(@util.config_target, "config.yml")
|
691
846
|
|
692
|
-
|
693
|
-
|
847
|
+
# load config file
|
848
|
+
config = YAML.load_file(configfile)
|
849
|
+
global[:config] = config.symbolize_keys
|
694
850
|
|
695
|
-
@piped_content =
|
851
|
+
@piped_content = $stdin.read if $stdin.fcntl(Fcntl::F_GETFL, 0) == 0 || $stdin.stat.pipe?
|
696
852
|
|
697
|
-
@jt = JTag.new(
|
853
|
+
@jt = JekyllTag::JTag.new(@util.config_target, global[:config])
|
698
854
|
|
699
855
|
true
|
700
856
|
end
|
701
857
|
|
702
|
-
post do |global,command,options,args|
|
858
|
+
post do |global, command, options, args|
|
703
859
|
# Post logic here
|
704
860
|
# Use skips_post before a command to skip this
|
705
861
|
# block on that command only
|
706
862
|
end
|
707
863
|
|
708
864
|
on_error do |exception|
|
709
|
-
|
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
|
+
|
710
870
|
# return false to skip default error handling
|
711
871
|
true
|
712
872
|
end
|