jtag 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ = jtag
2
+
3
+ jTag is a simple command line application for manipulating Jekyll tags in the YAML headers. It can perform mass operations such as tag addition, removal, merging and sorting. It can also suggest relevant tags based on the content of the post matched against your existing tagset (requires a plugin/template in your jekyll install).
4
+
5
+ Configuration includes a persistent blacklist and synonym definitions. Tags manually added to posts are automatically whitelisted for the auto-tagger. The autotagger can update your posts directly, or be used to provide suggestions that you can manually insert in the post.
6
+
7
+ :include:jtag.rdoc
8
+
@@ -0,0 +1,428 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'jtag'
4
+
5
+ include GLI::App
6
+
7
+ program_desc 'Autotagging for Jekyll'
8
+
9
+ version Jtag::VERSION
10
+
11
+ @config_files = %w{blacklist.txt config.yml stopwords.txt synonyms.yml}
12
+ @config_target = File.expand_path("~/.jtag")
13
+
14
+ def config_files_complete?
15
+ @config_files.each do |file|
16
+ return false unless File.exists?(File.join(@config_target,file))
17
+ end
18
+ true
19
+ end
20
+
21
+ desc 'Debug level'
22
+ default_value '0'
23
+ arg_name 'debug_level'
24
+ flag [:d,:debug]
25
+
26
+ desc 'Run silently'
27
+ switch [:s,'silent']
28
+
29
+ desc "Test (Dry run, don't update files)"
30
+ long_desc "Run all commands and show results on the command line, but don't overwrite/update any files"
31
+ default_value false
32
+ switch [:t,'test']
33
+
34
+ def console_log(msg="", options={})
35
+ return if @silent
36
+ err = options[:err] || false
37
+ unless err
38
+ $stdout.puts msg
39
+ else
40
+ $stderr.puts msg
41
+ end
42
+ end
43
+
44
+ desc 'Update and notify user of configuration files location'
45
+ command :config do |c|
46
+ c.desc 'Reset all configuration files to default values'
47
+ c.switch [:r,'reset']
48
+
49
+ c.skips_pre
50
+ c.action do |global_options,options,args|
51
+ if options[:r]
52
+ print "Are you sure you want to reset all config files? y/N: "
53
+ response = STDIN.gets.strip
54
+ if response =~ /^y/i
55
+ write_config(true)
56
+ console_log "Config files reset"
57
+ end
58
+ else
59
+ write_config(false)
60
+ end
61
+ end
62
+ end
63
+
64
+ def write_config(atomic=false)
65
+ gem_root = Gem.loaded_specs['jtag'].full_gem_path
66
+ gem_lib = File.join(gem_root, 'lib')
67
+ config_source = File.join(gem_lib,'/jtag/config_files')
68
+
69
+
70
+
71
+ unless File.directory?(@config_target) || atomic
72
+ FileUtils.cp_r(config_source,@config_target)
73
+ console_log "Configuration files are located in the folder: " + @config_target
74
+ console_log %Q{Make sure that "tags_location" in config.yml is set to your tags.json url.}
75
+ console_log "Configuration files written to #{@config_target}"
76
+ end
77
+
78
+ @config_files.each do |file|
79
+ unless File.exists?(File.join(@config_target,file))
80
+ source_file = File.join(config_source,file)
81
+ target_file = File.join(@config_target,file)
82
+ FileUtils.cp(source_file, target_file)
83
+ console_log "Config file #{file} added."
84
+ end
85
+ end
86
+ console_log
87
+ console_log "Configuration files are located in the folder: " + @config_target
88
+ console_log %Q{Make sure that "tags_location" in the config.yml file is set to your tags json file.}
89
+ end
90
+
91
+ desc 'List tags, optionally filter for keywords/regular expressions (OR)'
92
+ long_desc 'This command can be used to find the exact format for a given tag to keep spaces, underscores, capitalization and pluralization consistent'
93
+ arg_name 'keyword', :multiple
94
+ command :search do |c|
95
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
96
+ c.arg_name 'output_format'
97
+ c.default_value 'yaml'
98
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/i, :type => String
99
+
100
+ c.desc 'Include tag counts'
101
+ c.arg_name 'counts'
102
+ c.default_value false
103
+ c.switch [:c,:counts]
104
+
105
+ c.action do |global_options,options,args|
106
+ tags = @jt.get_tags({:counts => true})
107
+ if args.length > 0
108
+ tags.delete_if {|tag|
109
+ re = args.join("|")
110
+ if tag && tag['name'] =~ /(#{re})/i
111
+ false
112
+ else
113
+ true
114
+ end
115
+ }
116
+ if options[:c]
117
+ tags.map! {|tag| "#{tag['name']} (#{tag['count']})" }
118
+ else
119
+ tags.map! {|tag| tag['name'] }
120
+ end
121
+ output_tags(tags,options[:format])
122
+ else
123
+ tags.delete_if {|tag| !tag }
124
+ if options[:c]
125
+ tags.map! {|tag| "#{tag['name']} (#{tag['count']})" }
126
+ else
127
+ tags.map! {|tag| tag['name'] }
128
+ end
129
+ output_tags(tags,options[:format])
130
+ end
131
+ end
132
+ end
133
+
134
+ desc 'Show the current tags for posts'
135
+ arg_name 'filenames', :multiple
136
+ command :tags do |c|
137
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
138
+ c.arg_name 'output_format'
139
+ c.default_value 'yaml'
140
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
141
+
142
+ c.action do |global_options,options,args|
143
+ args.each{|file|
144
+ tags = @jt.post_tags(file)
145
+ if args.length > 1
146
+ console_log
147
+ console_log File.basename(file) + ":"
148
+ end
149
+ if tags.empty? || tags.nil?
150
+ console_log "No tags in post", {:err => true}
151
+ else
152
+ output_tags(tags,options[:format])
153
+ end
154
+ }
155
+ end
156
+ end
157
+
158
+
159
+ desc 'Sort the existing tags for posts'
160
+ arg_name 'filenames', :multiple
161
+ command :sort do |c|
162
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
163
+ c.arg_name 'output_format'
164
+ c.default_value 'yaml'
165
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
166
+
167
+ c.action do |global_options,options,args|
168
+ args.each{|file|
169
+ tags = @jt.post_tags(file)
170
+ tags.uniq!
171
+ tags.sort!
172
+ unless global_options[:t]
173
+ @jt.update_file_tags(file, tags)
174
+ end
175
+ if args.length > 1
176
+ console_log
177
+ console_log File.basename(file) + ":"
178
+ end
179
+ if tags.empty? || tags.nil?
180
+ console_log "No tags in post", {:err => true}
181
+ else
182
+ output_tags(tags,options[:format])
183
+ end
184
+ }
185
+ end
186
+ end
187
+
188
+ desc 'Merge multiple tags into one'
189
+ long_desc 'Scans the specified posts for any of the tags, merging any found into the last one in the list'
190
+ arg_name 'tags to merge merge_tag'
191
+ command :merge do |c|
192
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
193
+ c.arg_name 'output_format'
194
+ c.default_value 'yaml'
195
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
196
+
197
+ c.action do |global_options, options, args|
198
+ files = []
199
+ tags = []
200
+ args.length.times do
201
+ arg = args.pop
202
+ if File.exists?(arg)
203
+ files.push(arg)
204
+ else
205
+ exit_now! "No valid filename in arguments" if files.empty?
206
+ tags.push(arg)
207
+ end
208
+ end
209
+
210
+ exit_now! "Needs at least two tag inputs, one or more to merge, one to merge to" if tags.length < 2
211
+ tags.reverse!
212
+ merge_tag = tags.pop
213
+ console_log %Q{Merging #{tags.join(", ")} to #{merge_tag}}
214
+ files.each {|file|
215
+ new_tags = @jt.merge_tags(tags,merge_tag,file)
216
+ next unless new_tags
217
+ unless global_options[:t]
218
+ @jt.update_file_tags(file, new_tags)
219
+ console_log
220
+ console_log "Updated tags for #{file}"
221
+ end
222
+ console_log
223
+ console_log File.basename(file) + ":"
224
+ output_tags(new_tags,options[:format])
225
+ }
226
+ end
227
+ end
228
+
229
+
230
+ desc 'Blacklist a specific tag'
231
+ arg_name 'tag [tag2...]'
232
+ command :blacklist do |c|
233
+ c.desc "Remove (unblacklist) the arguments"
234
+ c.switch [:r,'remove']
235
+
236
+ c.action do |global_options,options,args|
237
+ if options[:r]
238
+ @jt.unblacklist(args)
239
+ console_log "Removed #{args.join(", ")} from blacklist."
240
+ else
241
+ @jt.blacklist(args)
242
+ console_log "Blacklisted #{args.join(", ")}."
243
+ end
244
+ end
245
+ end
246
+
247
+ desc 'Add tags to post(s)'
248
+ arg_name 'tags', :multiple
249
+ command :add do |c|
250
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
251
+ c.arg_name 'output_format'
252
+ c.default_value 'yaml'
253
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
254
+
255
+
256
+ c.action do |global_options,options,args|
257
+ files = []
258
+ new_tags = []
259
+ args.length.times do
260
+ arg = args.pop
261
+ if File.exists?(arg)
262
+ files.push(arg)
263
+ else
264
+ exit_now! "No valid filename in arguments" if files.empty?
265
+ new_tags.push(arg)
266
+ end
267
+ end
268
+
269
+ exit_now! "No tags found in arguments" if new_tags.empty?
270
+
271
+ files.each {|file|
272
+ tags = @jt.post_tags(file)
273
+ tags.concat(new_tags)
274
+ tags.uniq!
275
+ tags.sort!
276
+ unless global_options[:t]
277
+ @jt.update_file_tags(file,tags)
278
+ console_log "Updated tags for #{file}"
279
+ end
280
+
281
+ console_log
282
+ console_log File.basename(file) + ":"
283
+ output_tags(tags,options[:format])
284
+ }
285
+ end
286
+ end
287
+
288
+ desc 'Remove tags from post(s)'
289
+ arg_name 'tags', :multiple
290
+ command :remove do |c|
291
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
292
+ c.arg_name 'output_format'
293
+ c.default_value 'yaml'
294
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
295
+
296
+ c.action do |global_options,options,args|
297
+ files = []
298
+ remove_tags = []
299
+ args.length.times do
300
+ arg = args.pop
301
+ if File.exists?(arg)
302
+ files.push(arg)
303
+ else
304
+ exit_now! "No valid filename in arguments" if files.empty?
305
+ remove_tags.push(arg)
306
+ end
307
+ end
308
+
309
+ exit_now! "No tags found in arguments" if remove_tags.empty?
310
+
311
+ files.each {|file|
312
+ tags = @jt.post_tags(file)
313
+ remove_tags.each { |d|
314
+ tags.delete_if { |tag| tag == d }
315
+ }
316
+ unless global_options[:t]
317
+ @jt.update_file_tags(file,tags)
318
+ console_log "Updated tags for #{file}"
319
+ end
320
+
321
+ console_log
322
+ console_log File.basename(file) + ":"
323
+ output_tags(tags,options[:format])
324
+ }
325
+ end
326
+ end
327
+
328
+ desc 'Generate a list of recommended tags, optionally updating the file'
329
+ arg_name 'filename', :multiple
330
+ command :tag do |c|
331
+ c.desc 'Format to use when outputting tags to console: list, json, csv or yaml. Defaults to yaml.'
332
+ c.arg_name 'output_format'
333
+ c.default_value 'yaml'
334
+ c.flag [:f,:format], :must_match => /^(csv|list|yaml|json)$/, :type => String
335
+
336
+ c.action do |global_options,options,args|
337
+
338
+ args.each {|file|
339
+ if File.exists?(File.expand_path(file))
340
+ input = IO.read(File.expand_path(file))
341
+ suggestions = @jt.suggest(input)
342
+ unless global_options[:t]
343
+ if @jt.update_file_tags(file, suggestions)
344
+ console_log
345
+ console_log "Updated file #{file} with:"
346
+ else
347
+ console_log
348
+ console_log "Failed to update #{file} with:"
349
+ end
350
+ end
351
+ if !global_options[:s] || global_options[:t]
352
+ if args.length > 1
353
+ console_log
354
+ console_log File.basename(file) + ":", {:err => true}
355
+ end
356
+ output_tags(suggestions,options[:format])
357
+ end
358
+ suggestions = nil
359
+ else
360
+ raise "No such file: #{file}"
361
+ end
362
+ }
363
+ end
364
+ end
365
+
366
+ def output_tags(tags,format='yaml')
367
+ case format
368
+ when 'list'
369
+ console_log tags.join("\n")
370
+ when 'csv'
371
+ console_log tags.to_csv
372
+ when 'json'
373
+ out = {}
374
+ out['tags'] = tags
375
+ console_log out.to_json
376
+ else
377
+ out = {}
378
+ out['tags'] = tags
379
+ console_log out.to_yaml
380
+ end
381
+ end
382
+
383
+ ## TODO: Add a feature for converting YAML tags to OpenMeta or Mavericks tags
384
+ # desc 'Describe omtag here'
385
+ # arg_name 'Describe arguments to omtag here'
386
+ # command :omtag do |c|
387
+ # c.action do |global_options,options,args|
388
+ ## system %Q{openmeta -a "#{@auto_tags.uniq.join('" "')}" -p "#{@update_file}"}
389
+ # puts "omtag command ran"
390
+ # end
391
+ # end
392
+
393
+ pre do |global,command,options,args|
394
+ # Pre logic here
395
+ # Return true to proceed; false to abort and not call the
396
+ # chosen command
397
+ # Use skips_pre before a command to skip this block
398
+ # on that command only
399
+ @silent = global[:silent]
400
+
401
+ unless config_files_complete?
402
+ write_config
403
+ console_log "Missing config files written to #{@config_target}. Please check your configuration."
404
+ return false
405
+ end
406
+
407
+ configfile = File.expand_path("~/.jtag/config.yml")
408
+
409
+ global[:config] = YAML::load(File.open(configfile,"r"))
410
+ global[:support] = File.expand_path("~/.jtag")
411
+ @jt = JTag.new(global[:support], global[:config])
412
+
413
+ true
414
+ end
415
+
416
+ post do |global,command,options,args|
417
+ # Post logic here
418
+ # Use skips_post before a command to skip this
419
+ # block on that command only
420
+ end
421
+
422
+ on_error do |exception|
423
+ # Error logic here
424
+ # return false to skip default error handling
425
+ true
426
+ end
427
+
428
+ exit run(ARGV)
@@ -0,0 +1,159 @@
1
+ == jtag - Autotagging for Jekyll
2
+
3
+ v0.1.2
4
+
5
+ === Global Options
6
+ === -d|--debug debug_level
7
+
8
+ Debug level
9
+
10
+ [Default Value] 0
11
+
12
+
13
+ === --help
14
+ Show this message
15
+
16
+
17
+
18
+ === -s|--[no-]silent
19
+ Run silently
20
+
21
+
22
+
23
+ === -t|--[no-]test
24
+ Test (Dry run, don't update files)
25
+
26
+ Run all commands and show results on the command line, but don't overwrite/update any files
27
+
28
+ === --version
29
+ Display the program version
30
+
31
+
32
+
33
+ === Commands
34
+ ==== Command: <tt>add tags to add from_post_file_or_pattern</tt>
35
+ Add tags to post
36
+
37
+
38
+ ===== Options
39
+ ===== -f|--format output_format
40
+
41
+ Format to use when outputting tags to console: list, csv or yaml
42
+
43
+ [Default Value] yaml
44
+ [Must Match] (?-mix:^(csv|list|yaml)$)
45
+
46
+
47
+ ==== Command: <tt>blacklist tag [tag2...]</tt>
48
+ Blacklist a specific tag
49
+
50
+
51
+ ===== Options
52
+ ===== -r|--[no-]remove
53
+ Remove (unblacklist) the arguments
54
+
55
+
56
+
57
+ ==== Command: <tt>config </tt>
58
+ Update and notify user of configuration files location
59
+
60
+
61
+ ===== Options
62
+ ===== -r|--[no-]reset
63
+ Reset all configuration files to default values
64
+
65
+
66
+
67
+ ==== Command: <tt>find keyword[, keyword]*</tt>
68
+ List tags, optionally filter for keywords/regular expressions (OR)
69
+
70
+ This command can be used to find the exact format for a given tag to keep spaces, underscores, capitalization and pluralization consistent
71
+ ===== Options
72
+ ===== -f|--format output_format
73
+
74
+ Format to use when outputting tags to console: list, csv or yaml. Defaults to yaml.
75
+
76
+ [Default Value] yaml
77
+ [Must Match] (?-mix:^(csv|list|yaml)$)
78
+
79
+
80
+ ===== -c|--[no-]counts
81
+ Include tag counts
82
+
83
+
84
+
85
+ ==== Command: <tt>help command</tt>
86
+ Shows a list of commands or help for one command
87
+
88
+ Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function
89
+ ===== Options
90
+ ===== -c
91
+ List commands one per line, to assist with shell completion
92
+
93
+
94
+
95
+ ==== Command: <tt>merge tags to merge merge_tag</tt>
96
+ Merge multiple tags into one
97
+
98
+ Scans the specified posts for any of the tags, merging any found into the last one in the list
99
+ ===== Options
100
+ ===== -f|--format output_format
101
+
102
+ Format to use when outputting tags to console: list, csv or yaml
103
+
104
+ [Default Value] yaml
105
+ [Must Match] (?-mix:^(csv|list|yaml)$)
106
+
107
+
108
+ ==== Command: <tt>remove tags to remove from_post_file_or_pattern</tt>
109
+ Remove tags from a post
110
+
111
+
112
+ ===== Options
113
+ ===== -f|--format output_format
114
+
115
+ Format to use when outputting tags to console: list, csv or yaml
116
+
117
+ [Default Value] yaml
118
+ [Must Match] (?-mix:^(csv|list|yaml)$)
119
+
120
+
121
+ ==== Command: <tt>sort filenames[, filenames]*</tt>
122
+ Sort the existing tags for posts
123
+
124
+
125
+ ===== Options
126
+ ===== -f|--format output_format
127
+
128
+ Format to use when outputting tags to console: list, csv or yaml
129
+
130
+ [Default Value] yaml
131
+ [Must Match] (?-mix:^(csv|list|yaml)$)
132
+
133
+
134
+ ==== Command: <tt>tag filename[, filename]*</tt>
135
+ Generate a list of recommended tags, optionally updating the file
136
+
137
+
138
+ ===== Options
139
+ ===== -f|--format output_format
140
+
141
+ Format to use when outputting tags to console: list, csv or yaml
142
+
143
+ [Default Value] yaml
144
+ [Must Match] (?-mix:^(csv|list|yaml)$)
145
+
146
+
147
+ ==== Command: <tt>tags filenames[, filenames]*</tt>
148
+ Show the current tags for posts
149
+
150
+
151
+ ===== Options
152
+ ===== -f|--format output_format
153
+
154
+ Format to use when outputting tags to console: list, csv or yaml
155
+
156
+ [Default Value] yaml
157
+ [Must Match] (?-mix:^(csv|list|yaml)$)
158
+
159
+