jtag 0.1.19 → 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 +374 -252
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dce6f0470ef8fd405ee64a347ff3248c12080d76b7d260f2767ea0df6a99754e
|
4
|
+
data.tar.gz: e6bbedc5133afafb194165e07a830feacab40d6409a11d2f76dc974e42df26f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ae0e91ccbc3af0622c323fff774f0624927f16d9573a4170ba9f37ea08ebde9cf2cb92092f498bd4fbbd22179fa37e63d1ba01ad802995d8638b32567a95ab2
|
7
|
+
data.tar.gz: 355dd96ef61e1b14e1bd17e03ac39b6d39afb118d1733eb6fd7b0ca48fd1ed170b9668d009e8c8157e3fcd020be7d184cab04d1760914349722f6726a044d006
|
data/.irbrc
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
### 0.1.19
|
2
|
+
|
3
|
+
2024-09-16 13:15
|
4
|
+
|
5
|
+
#### NEW
|
6
|
+
|
7
|
+
- Allows piping of file lists. For actions to which it applies, you can pipe a file list, either from a previous jtag command or from another source (newline separated) to act on only those files, e.g. `jtag posts_tagged keyboard | jtag tags`
|
8
|
+
|
9
|
+
#### FIXED
|
10
|
+
|
11
|
+
- File.exists? => File.exist? for Ruby 3
|
12
|
+
|
13
|
+
### 1.0
|
14
|
+
|
15
|
+
Initial release
|
data/Gemfile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Author: Brett Terpstra
|
3
|
+
# Page generator for /data/tags.json, a list of all the tags on your blog and a list of the top 50 tags.
|
4
|
+
# Part of the AutoTag tool for Jekyll, but usable in other applications
|
5
|
+
#
|
6
|
+
# Looks for a tags_json.html template in your _layouts folder, see the example provided.
|
7
|
+
# Provides page.json_tags and page.json_tags_count as arrays for looping
|
8
|
+
#
|
9
|
+
# Config options for _config.yml
|
10
|
+
# tag_excludes: array of tags to exclude from all listings
|
11
|
+
# tags_json_dir: alternate of folder for the data.json file. Set to blank for root, defaults to 'data'
|
12
|
+
#
|
13
|
+
# If using tags.json with a javascript application, be sure you have the proper headers defined in .htaccess:
|
14
|
+
# AddType application/json json
|
15
|
+
# ExpiresByType application/json "access plus 0 seconds"
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
|
19
|
+
module Jekyll
|
20
|
+
|
21
|
+
class AutoTag < Page
|
22
|
+
def initialize(site, base, dir)
|
23
|
+
@site = site
|
24
|
+
@base = base
|
25
|
+
@dir = dir
|
26
|
+
@name = 'tags.json'
|
27
|
+
self.process(@name)
|
28
|
+
self.read_yaml(File.join(base, '_layouts'), 'tags_json.html')
|
29
|
+
tags_with_counts = site.tags.delete_if {|k,v|
|
30
|
+
site.config['tag_excludes'].include?(k) unless site.config['tag_excludes'].nil?
|
31
|
+
}.sort_by { |k,v| v.count }.reverse
|
32
|
+
|
33
|
+
self.data['json_tags'] = tags_with_counts.sort {|a,b| a[0].downcase <=> b[0].downcase }.map {|k,v| k }
|
34
|
+
self.data['json_tags_count'] = []
|
35
|
+
tags_with_counts.each { |k,v|
|
36
|
+
self.data['json_tags_count'] << {
|
37
|
+
"name" => k,
|
38
|
+
"count" => v.count
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class AutoTagGenerator < Generator
|
44
|
+
safe true
|
45
|
+
def generate(site)
|
46
|
+
if site.layouts.key? 'tags_json'
|
47
|
+
dir = site.config['tag_json_dir'] || 'data'
|
48
|
+
write_tag_json(site, dir)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
def write_tag_json(site, dir)
|
52
|
+
index = AutoTag.new(site, site.source, dir)
|
53
|
+
index.render(site.layouts, site.site_payload)
|
54
|
+
index.write(site.dest)
|
55
|
+
site.pages << index
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# jTag
|
2
|
+
|
3
|
+
*Because you can't be too careful these days.*
|
4
|
+
|
5
|
+
**jTag** is a 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 tag set (requires a plugin/template in your Jekyll install).
|
6
|
+
|
7
|
+
Configuration includes a persistent blacklist and synonym definitions. Tags manually added to posts are automatically whitelisted for the auto-tagger. The auto-tagger can update your posts directly, or be used to provide suggestions for a working post that you can manually insert in the post.
|
8
|
+
|
9
|
+
**NOTE:** jTag works by modifying the YAML headers of your posts directly. There's no undo. Back up your posts (which is a good idea anyway). The best way to this is with git.
|
10
|
+
|
11
|
+
If you're not already using git to manage your blog (i.e. not deploying via git), just add the non-public parts of the directory (the source files) to a repo and make a commit when you finish post. A local repo would be fine in most cases, but a remote repo offers a little extra peace of mind. Mine is set up so that a gen_deploy Rake task automatically creates a snapshot every time I publish, in addition to my own commits.
|
12
|
+
|
13
|
+
|
14
|
+
The jTag tool is at 0.1.6 at the time of this writing. It's been tested, but never used by anyone but me until today. I'll be adding features and updating as I go, so please [let me know about bugs][support].
|
15
|
+
|
16
|
+
First, though, let's cover the plugin side, which hasn't needed to change since I first wrote it. Set it up and forget it.
|
17
|
+
|
18
|
+
## Generating a tag index
|
19
|
+
|
20
|
+
To use jtag, we need to generate an index of all the tags on a Jekyll/OctoPress blog in JSON format. I chose JSON because it could be used in JavaScript apps easily and would make the data useful for more than just jTag. The code for both the template and the plugin can be found in the [Jekyll folder of the GitHub repository](https://github.com/ttscoff/jtag/tree/master/Jekyll).
|
21
|
+
|
22
|
+
### Plugin: autotag_gen.rb
|
23
|
+
|
24
|
+
This plugin is a Page generator which creates `/data/tags.json`, which is a list of all the tags on your blog, as well as a list of the top 50 tags with a count of how many times they appear across your blog. You can [download the plugin here](https://github.com/ttscoff/jtag/blob/master/Jekyll/plugins/autotag_gen.rb).
|
25
|
+
|
26
|
+
The plugin looks for the `tags_json.html` template in your _layouts folder. See the example provided. It provides the following, which can be used in the liquid template:
|
27
|
+
|
28
|
+
- `page.json_tags`
|
29
|
+
- `page.json_tags_count`
|
30
|
+
|
31
|
+
In your blog's `_config.yml`, you can optionally add rules for excluding tags or using an alternate destination:
|
32
|
+
|
33
|
+
- `tag_excludes:` array of tags to exclude from all listings
|
34
|
+
- `tags_json_dir:` alternate folder for the `data.json` file. Set to blank for root, defaults to 'data'
|
35
|
+
|
36
|
+
Side note: if you're also using tags.json with JavaScript on your site, be sure you have the proper headers defined in `.htaccess`:
|
37
|
+
|
38
|
+
|
39
|
+
AddType application/json json
|
40
|
+
ExpiresByType application/json "access plus 0 seconds"
|
41
|
+
|
42
|
+
### Template: tags_json.html
|
43
|
+
|
44
|
+
Place [the template](https://github.com/ttscoff/jtag/blob/master/Jekyll/source/_layouts/tags_json.html) in `source/_layouts/tags_json.html`.
|
45
|
+
|
46
|
+
Now you can do a full build of your blog and the `data/tags.json` file will be ready for deploy.
|
47
|
+
|
48
|
+
## jTag
|
49
|
+
|
50
|
+
jTag is currently at version 0.1.6 It will be a work in progress for a while as I make it more efficient and figure out the best ways to integrate it into my blogging workflow.It grabs all of the tags on your blog (the `tags.json` file) and compares them to the text you give it. It handles matching plurals and different conjugations of words so that you get tag results even if they're not an exact match.
|
51
|
+
|
52
|
+
### Installation/Configuration
|
53
|
+
|
54
|
+
The gem is hosted on [rubygems.org](https://rubygems.org/gems/jtag). To install, either add the jtag dependency to your Gemfile in your Jekyll folder (`gem "jtag", "~> 0.1.6"`) and run `bundle`, or run `gem install jtag`. If you're not using a ruby version manager, you may need to use `sudo gem install jtag` to install it in your system Ruby configuration.
|
55
|
+
|
56
|
+
Run `jtag config` to ensure it's loaded and create the configuration folder and files in `~/.config/jtag`.
|
57
|
+
|
58
|
+
The only configuration option required is the location (url without protocol) of your `tags.json` file. It's set in `~/.config/jtag/config.yml`:
|
59
|
+
|
60
|
+
tags_location: brettterpstra.com/data/tags.json
|
61
|
+
|
62
|
+
There are three other files that are generated in `~/.config/jtag/`, and they can be edited as needed:
|
63
|
+
|
64
|
+
- **blacklist.txt**
|
65
|
+
|
66
|
+
This is a blacklist. Tags in this list will never show up in automatic results. If it already exists in the post it's merged in, but it won't be created. There are some tags that are relatively generic in terminology and would be triggered on almost every post. They're usually tags that I'd enter myself anyway. For example, I have a tag for Gabe Weatherhead's show, [Generational]([70decibels]): "generational." Because jTag stems the word before scanning for it, anything based off the root "generi" will trigger that tag. Thus, it's blacklisted and added manually on the infrequent occasions that I'm on the show.
|
67
|
+
|
68
|
+
The easiest way to add a tag to the blacklist is to run `jtag blacklist TAGNAME`. You can blacklist multiple tags at once, just separate them with a space. The blacklist is stored in `~/.config/jtag/blacklist.txt` and can be edited manually if needed.
|
69
|
+
- **stopwords.txt**
|
70
|
+
|
71
|
+
This is a predefined list of common words that will most likely never be tags. They're "stemmed" down to their root, so that "thing," "things," and "thingy" are all blocked by one line. If you find your results are missing a particular tag, scan the `~/.config/jtag/stopwords.txt` file for the culprit.
|
72
|
+
- **synonyms.yml**
|
73
|
+
|
74
|
+
The `synonyms.yml` file is a YAML file that defines alternate spellings, punctuations or related topics which determine when a tag is used. For example, my tag for Mountain Lion is "mountainlion" because I like single-word, lowercase tags. That isn't going to be found in the text of my posts, though, so I add synonyms like this:
|
75
|
+
|
76
|
+
|
77
|
+
mountainlion:
|
78
|
+
- Mountain Lion
|
79
|
+
- 10.8
|
80
|
+
- OS X 10.8
|
81
|
+
|
82
|
+
|
83
|
+
You can add as many as you like. When an attached term is found in the text, it will include the parent tag.
|
84
|
+
|
85
|
+
## Usage
|
86
|
+
|
87
|
+
**Note:** jTag requires that YAML headers exist in the post being processed. I may add the option to pass just content text and get back an appropriate auto-tag response, but for now you at least need a yaml `---` header and terminator.
|
88
|
+
|
89
|
+
jTag is built using subcommands, each with it's own options and parameters. You can get help for jTag with `jtag help`, and documentation for each command with `jtag help command`.
|
90
|
+
|
91
|
+
### Commands
|
92
|
+
|
93
|
+
jtag [-t] command [options] [arguments] filename(s)
|
94
|
+
|
95
|
+
help - Shows a list of commands or help for one command
|
96
|
+
|
97
|
+
add - Add tags to post(s)
|
98
|
+
remove - Remove tags from post(s)
|
99
|
+
merge - Merge multiple tags into one
|
100
|
+
|
101
|
+
config - Update and notify user of configuration files location
|
102
|
+
blacklist - Blacklist a specific tag
|
103
|
+
|
104
|
+
search - List all tags, optionally filter for keywords/regular expressions (boolean OR search)
|
105
|
+
sort - Sort the existing tags for posts
|
106
|
+
tag - Generate a list of recommended tags, optionally updating the file
|
107
|
+
tags - Show the current tags for posts
|
108
|
+
|
109
|
+
Use the global `-t` option with jTag to run it in "test" mode. No files will be overwritten if the command starts with `jtag -t (command)`. All results will be written to STDOUT for inspection. If you run it with `-t` and pass `-f fmt` to relevant commands with a single file argument, it will output just the tag results in your selected format. This makes it possible to script jTag into other applications.
|
110
|
+
|
111
|
+
`config`
|
112
|
+
: Use **config** to install configuration files, replace missing ones, or reset to defaults (--reset).
|
113
|
+
|
114
|
+
`blacklist`
|
115
|
+
: Use **blacklist** to quickly add or remove (run with `-r`) a tag from the blacklist.
|
116
|
+
|
117
|
+
`add`
|
118
|
+
: Use **add** on a single file, a glob pattern or the results of a grep, ack or find command (using xargs) to add one or more tags to the specified posts. With this, you can quickly add tags to an individual post, or add a tag to any posts that contain certain words in its content or title.
|
119
|
+
|
120
|
+
find source/_posts -name "*marky*" | xargs jtag add markdownifier
|
121
|
+
|
122
|
+
`remove`
|
123
|
+
: Use **remove** to remove tags on select files, or run it on an entire folder to purge tags from your system.
|
124
|
+
|
125
|
+
`merge`
|
126
|
+
: You can use **merge** to prune your taxonomy. Have two tags that only differ by capitalization or pluralization? Run something like `jtag merge Markdown markdown _posts/*.md` and all instances of "Markdown" will be converted to "markdown," avoiding duplicates should they arise. You can merge as many tags as you want, the last one in the list will be the one that replaces them all.
|
127
|
+
|
128
|
+
`search`
|
129
|
+
: Use **search** to list all of the tags on your blog, optionally filtering the list with a keyword as the argument. Multiple keywords will be combined in an boolean OR fashion, so any tags that match any of the keywords will be returned. Keywords can also be quoted regular expressions (e.g. "^mark" to find only tags that _start_ with "mark").
|
130
|
+
|
131
|
+
Add the **-c** option to get tags back with a number representing how many posts they appear in. This command is a tool to help you find the proper punctuation, capitalization and pluralization of existing tags to ensure consistency.
|
132
|
+
|
133
|
+
$ jtag search -f list markdown
|
134
|
+
markdown
|
135
|
+
multimarkdown
|
136
|
+
markdownservices
|
137
|
+
markdownifier
|
138
|
+
markdownediting
|
139
|
+
markdownrules
|
140
|
+
|
141
|
+
`tag`
|
142
|
+
: Use **tag** to suggest tags for your post based on its content. Suggested tags will be mixed with any existing tags (which are automatically whitelisted) and it will return a YAML array block to add to your post. By default, it will write the tags to the file automatically. If you use the -t global switch (`jtag -t tag post.md`) it will just give you the output (in YAML by default) and let you copy/paste it.
|
143
|
+
|
144
|
+
`tags`
|
145
|
+
: Use **tags** to see the current tags on a post or posts. They'll be listed with the filename as a header and the tags below. This is just handy for quickly confirming operations.
|
146
|
+
|
147
|
+
`sort`
|
148
|
+
: The **sort** command is a convenience method to alphabetize the tags in your post. It can safely be run on your entire `_posts` folder just to tidy things up.
|
149
|
+
|
150
|
+
Any command that outputs a list of tags has the option (`-f, --format`)to change the format to one of `csv`, `list` (plain text), `json` or `yaml` (default). Hopefully this will provide some scripting and integration options in the future.
|
151
|
+
|
152
|
+
### Bash completion
|
153
|
+
|
154
|
+
If you're interested, there's also a quick script you can drop into `.bash_profile` and get [jtag tab-completion in Bash](https://github.com/ttscoff/jtag/blob/master/jtag.completion.bash).
|
155
|
+
|
156
|
+
[support]: https://github.com/ttscoff/jtag/issues/
|
157
|
+
|
158
|
+
|
159
|
+

|
data/Rakefile
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'cucumber'
|
6
|
+
require 'cucumber/rake/task'
|
7
|
+
Rake::RDocTask.new do |rd|
|
8
|
+
rd.main = "README.rdoc"
|
9
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
10
|
+
rd.title = 'jTag'
|
11
|
+
end
|
12
|
+
|
13
|
+
spec = eval(File.read('jtag.gemspec'))
|
14
|
+
|
15
|
+
Gem::PackageTask.new(spec) do |pkg|
|
16
|
+
end
|
17
|
+
CUKE_RESULTS = 'results.html'
|
18
|
+
CLEAN << CUKE_RESULTS
|
19
|
+
desc 'Run features'
|
20
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
21
|
+
opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
|
22
|
+
opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
|
23
|
+
t.cucumber_opts = opts
|
24
|
+
t.fork = false
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run features tagged as work-in-progress (@wip)'
|
28
|
+
Cucumber::Rake::Task.new('features:wip') do |t|
|
29
|
+
tag_opts = ' --tags ~@pending'
|
30
|
+
tag_opts = ' --tags @wip'
|
31
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
|
32
|
+
t.fork = false
|
33
|
+
end
|
34
|
+
|
35
|
+
task :cucumber => :features
|
36
|
+
task 'cucumber:wip' => 'features:wip'
|
37
|
+
task :wip => 'features:wip'
|
38
|
+
require 'rake/testtask'
|
39
|
+
Rake::TestTask.new do |t|
|
40
|
+
t.libs << "test"
|
41
|
+
t.test_files = FileList['test/*_test.rb']
|
42
|
+
end
|
43
|
+
desc 'Install the gem in the current ruby'
|
44
|
+
task :install, :all do |t, args|
|
45
|
+
args.with_defaults(:all => false)
|
46
|
+
if args[:all]
|
47
|
+
sh "~/scripts/ruby-all gem install pkg/*.gem"
|
48
|
+
# sh "sudo gem install pkg/*.gem"
|
49
|
+
else
|
50
|
+
sh "gem install pkg/*.gem"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'Install the gem in the all rubies'
|
55
|
+
task :installall do |t, args|
|
56
|
+
Rake::Task[:install].invoke(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Development version check'
|
60
|
+
task :ver do |t|
|
61
|
+
system "grep VERSION lib/#{spec.name}/version.rb"
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Bump incremental version number'
|
65
|
+
task :bump, :type do |t, args|
|
66
|
+
args.with_defaults(:type => "inc")
|
67
|
+
version_file = "lib/#{spec.name}/version.rb"
|
68
|
+
raise "File not found" unless File.exist? version_file
|
69
|
+
content = IO.read(version_file)
|
70
|
+
content.sub!(/VERSION = '(\d+)\.(\d+)\.(\d+)(\.\S+)?'/) {|m|
|
71
|
+
major = $1.to_i
|
72
|
+
minor = $2.to_i
|
73
|
+
inc = $3.to_i
|
74
|
+
pre = $4
|
75
|
+
|
76
|
+
case args[:type]
|
77
|
+
when /^maj/
|
78
|
+
major += 1
|
79
|
+
minor = 0
|
80
|
+
inc = 0
|
81
|
+
when /^min/
|
82
|
+
minor += 1
|
83
|
+
inc = 0
|
84
|
+
else
|
85
|
+
inc += 1
|
86
|
+
end
|
87
|
+
|
88
|
+
$stdout.puts "At version #{major}.#{minor}.#{inc}#{pre}"
|
89
|
+
"VERSION = '#{major}.#{minor}.#{inc}#{pre}'"
|
90
|
+
}
|
91
|
+
File.open(version_file, 'w+') {|f|
|
92
|
+
f.puts content
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def alias_task(tasks)
|
97
|
+
tasks.each do |new_name, old_name|
|
98
|
+
task new_name, [*Rake.application[old_name].arg_names] => [old_name]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
alias_task [
|
103
|
+
[:i, :install],
|
104
|
+
[:ia, :installall],
|
105
|
+
[:p, :package],
|
106
|
+
[:c, :clobber]
|
107
|
+
]
|
108
|
+
|
109
|
+
|
110
|
+
task :build => [:package]
|
111
|
+
task :default => [:clobber,:rdoc,:package]
|
112
|
+
task :gobig => [:default,:installall]
|
113
|
+
|