stickyflag 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +4 -0
- data/.simplecov +9 -0
- data/.travis.yml +13 -0
- data/Gemfile +2 -0
- data/LICENSE.md +7 -0
- data/README.md +49 -0
- data/Rakefile +19 -0
- data/TODO.md +3 -0
- data/bin/stickyflag +6 -0
- data/features/clear.feature +14 -0
- data/features/clear_quietly.feature +23 -0
- data/features/configuration.feature +14 -0
- data/features/get.feature +14 -0
- data/features/get_quietly.feature +23 -0
- data/features/set.feature +14 -0
- data/features/set_quietly.feature +22 -0
- data/features/step_definitions/configuration_steps.rb +31 -0
- data/features/step_definitions/database_steps.rb +41 -0
- data/features/step_definitions/pending_steps.rb +5 -0
- data/features/step_definitions/tag_steps.rb +62 -0
- data/features/support/cukegem.rb +82 -0
- data/features/support/env.rb +37 -0
- data/features/tags.feature +18 -0
- data/features/unset.feature +14 -0
- data/features/unset_quietly.feature +23 -0
- data/lib/stickyflag/configuration.rb +66 -0
- data/lib/stickyflag/database.rb +162 -0
- data/lib/stickyflag/external_cmds.rb +64 -0
- data/lib/stickyflag/patches/tempfile_encoding.rb +22 -0
- data/lib/stickyflag/patches/tmpnam.rb +38 -0
- data/lib/stickyflag/paths.rb +47 -0
- data/lib/stickyflag/tag_factory.rb +108 -0
- data/lib/stickyflag/tags/c.rb +25 -0
- data/lib/stickyflag/tags/mmd.rb +168 -0
- data/lib/stickyflag/tags/pdf.rb +119 -0
- data/lib/stickyflag/tags/png.rb +61 -0
- data/lib/stickyflag/tags/source_code.rb +99 -0
- data/lib/stickyflag/tags/tex.rb +25 -0
- data/lib/stickyflag/version.rb +12 -0
- data/lib/stickyflag.rb +253 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/stickyflag/configuration_spec.rb +132 -0
- data/spec/stickyflag/database_spec.rb +331 -0
- data/spec/stickyflag/external_cmds_spec.rb +175 -0
- data/spec/stickyflag/patches/tempfile_encoding_spec.rb +26 -0
- data/spec/stickyflag/patches/tmpnam_spec.rb +35 -0
- data/spec/stickyflag/paths_spec.rb +29 -0
- data/spec/stickyflag/tag_factory_spec.rb +185 -0
- data/spec/stickyflag/tags/c_spec.rb +14 -0
- data/spec/stickyflag/tags/mmd_spec.rb +40 -0
- data/spec/stickyflag/tags/pdf_spec.rb +39 -0
- data/spec/stickyflag/tags/png_spec.rb +6 -0
- data/spec/stickyflag/tags/tex_spec.rb +6 -0
- data/spec/stickyflag_spec.rb +482 -0
- data/spec/support/examples/c_all_comments.c +3 -0
- data/spec/support/examples/c_no_tags.c +5 -0
- data/spec/support/examples/c_with_tag.c +6 -0
- data/spec/support/examples/mmd_all_meta.mmd +6 -0
- data/spec/support/examples/mmd_crazy_keys.mmd +8 -0
- data/spec/support/examples/mmd_crazy_tags.mmd +9 -0
- data/spec/support/examples/mmd_no_tags.mmd +1 -0
- data/spec/support/examples/mmd_with_tag.mmd +3 -0
- data/spec/support/examples/pdf_no_tags.pdf +0 -0
- data/spec/support/examples/pdf_with_tag.pdf +0 -0
- data/spec/support/examples/png_no_tags.png +0 -0
- data/spec/support/examples/png_with_tag.png +0 -0
- data/spec/support/examples/tex_no_tags.tex +10 -0
- data/spec/support/examples/tex_with_tag.tex +11 -0
- data/spec/support/examples/untaggable.txt +0 -0
- data/spec/support/examples.rb +32 -0
- data/spec/support/run_with_args.rb +36 -0
- data/spec/support/silence_stream.rb +12 -0
- data/spec/support/tag_handler_behavior.rb +125 -0
- data/stickyflag.gemspec +48 -0
- metadata +399 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'thor'
|
3
|
+
require 'open3'
|
4
|
+
require 'stickyflag/patches/tempfile_encoding'
|
5
|
+
|
6
|
+
module StickyFlag
|
7
|
+
module Tags
|
8
|
+
module PDF
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def get(file_name, pdftk_path = 'pdftk')
|
12
|
+
stdout_str = ''
|
13
|
+
stderr_str = ''
|
14
|
+
|
15
|
+
begin
|
16
|
+
Open3.popen3(pdftk_path, file_name.to_s, 'dump_data_utf8') do |i, o, e, t|
|
17
|
+
out_reader = Thread.new { o.read }
|
18
|
+
err_reader = Thread.new { e.read }
|
19
|
+
i.close
|
20
|
+
stdout_str = out_reader.value
|
21
|
+
stderr_str = err_reader.value
|
22
|
+
|
23
|
+
stdout_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
|
24
|
+
stderr_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
|
25
|
+
end
|
26
|
+
rescue Exception
|
27
|
+
raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; pdftk call failed")
|
28
|
+
end
|
29
|
+
if stderr_str.start_with?("Error: ") || stderr_str.include?("Errno::ENOENT")
|
30
|
+
raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; pdftk call failed")
|
31
|
+
end
|
32
|
+
|
33
|
+
# More than one of these shouldn't be possible, but try to recover if
|
34
|
+
# it somehow happens.
|
35
|
+
matches = stdout_str.scan(/InfoKey: X-StickyFlag-Flags\nInfoValue: (.*?)\n/)
|
36
|
+
return [] if matches.empty?
|
37
|
+
|
38
|
+
tags = []
|
39
|
+
|
40
|
+
matches.each do |m|
|
41
|
+
match_string = m[0]
|
42
|
+
match_tags = match_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
43
|
+
next if match_tags.empty?
|
44
|
+
|
45
|
+
tags.concat(match_tags)
|
46
|
+
end
|
47
|
+
|
48
|
+
tags
|
49
|
+
end
|
50
|
+
|
51
|
+
def set(file_name, tag, pdftk_path = 'pdftk')
|
52
|
+
tags = get(file_name, pdftk_path)
|
53
|
+
return if tags.include? tag
|
54
|
+
|
55
|
+
tags << tag
|
56
|
+
|
57
|
+
info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
|
58
|
+
begin
|
59
|
+
info.write("InfoKey: X-StickyFlag-Flags\n")
|
60
|
+
info.write("InfoValue: #{tags.join(', ')}\n")
|
61
|
+
info.close
|
62
|
+
|
63
|
+
outpath = File.tmpnam('.pdf')
|
64
|
+
ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
|
65
|
+
unless ret == true
|
66
|
+
raise Thor::Error.new("ERROR: Failed to set tag for #{file_name}; pdftk call failed")
|
67
|
+
end
|
68
|
+
|
69
|
+
FileUtils.mv outpath, file_name
|
70
|
+
ensure
|
71
|
+
info.unlink
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def unset(file_name, tag, pdftk_path = 'pdftk')
|
76
|
+
tags = get(file_name, pdftk_path)
|
77
|
+
return unless tags.include? tag
|
78
|
+
|
79
|
+
tags.delete(tag)
|
80
|
+
|
81
|
+
info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
|
82
|
+
begin
|
83
|
+
info.write("InfoKey: X-StickyFlag-Flags\n")
|
84
|
+
info.write("InfoValue: #{tags.join(', ')}\n")
|
85
|
+
info.close
|
86
|
+
|
87
|
+
outpath = File.tmpnam('.pdf')
|
88
|
+
ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
|
89
|
+
unless ret == true
|
90
|
+
raise Thor::Error.new("ERROR: Failed to unset tag for #{file_name}; pdftk call failed")
|
91
|
+
end
|
92
|
+
|
93
|
+
FileUtils.mv outpath, file_name
|
94
|
+
ensure
|
95
|
+
info.unlink
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def clear(file_name, pdftk_path = 'pdftk')
|
100
|
+
info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
|
101
|
+
begin
|
102
|
+
info.write("InfoKey: X-StickyFlag-Flags\n")
|
103
|
+
info.write("InfoValue: \n")
|
104
|
+
info.close
|
105
|
+
|
106
|
+
outpath = File.tmpnam('.pdf')
|
107
|
+
ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
|
108
|
+
unless ret == true
|
109
|
+
raise Thor::Error.new("ERROR: Failed to clear tags for #{file_name}; pdftk call failed")
|
110
|
+
end
|
111
|
+
|
112
|
+
FileUtils.mv outpath, file_name
|
113
|
+
ensure
|
114
|
+
info.unlink
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'thor'
|
3
|
+
begin
|
4
|
+
require 'oily_png'
|
5
|
+
rescue LoadError
|
6
|
+
require 'chunky_png'
|
7
|
+
end
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
module StickyFlag
|
11
|
+
module Tags
|
12
|
+
module PNG
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def get(file_name)
|
16
|
+
image = ChunkyPNG::Image.from_file(file_name)
|
17
|
+
tag_string = image.metadata['X-StickyFlag-Flags']
|
18
|
+
return [] if tag_string.nil?
|
19
|
+
tag_string.force_encoding('UTF-8') if RUBY_VERSION >= "1.9.0"
|
20
|
+
tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
21
|
+
end
|
22
|
+
|
23
|
+
def set(file_name, tag)
|
24
|
+
tags = get(file_name)
|
25
|
+
return if tags.include? tag
|
26
|
+
|
27
|
+
tags << tag
|
28
|
+
|
29
|
+
image = ChunkyPNG::Image.from_file(file_name)
|
30
|
+
image.metadata['X-StickyFlag-Flags'] = tags.join(', ')
|
31
|
+
|
32
|
+
outpath = File.tmpnam('.png')
|
33
|
+
image.save(outpath)
|
34
|
+
FileUtils.mv(outpath, file_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def unset(file_name, tag)
|
38
|
+
tags = get(file_name)
|
39
|
+
return unless tags.include? tag
|
40
|
+
|
41
|
+
tags.delete(tag)
|
42
|
+
|
43
|
+
image = ChunkyPNG::Image.from_file(file_name)
|
44
|
+
image.metadata['X-StickyFlag-Flags'] = tags.join(', ')
|
45
|
+
|
46
|
+
outpath = File.tmpnam('.png')
|
47
|
+
image.save(outpath)
|
48
|
+
FileUtils.mv(outpath, file_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear(file_name)
|
52
|
+
image = ChunkyPNG::Image.from_file(file_name)
|
53
|
+
image.metadata.delete('X-StickyFlag-Flags')
|
54
|
+
|
55
|
+
outpath = File.tmpnam('.png')
|
56
|
+
image.save(outpath)
|
57
|
+
FileUtils.mv(outpath, file_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module StickyFlag
|
4
|
+
module Tags
|
5
|
+
module SourceCode
|
6
|
+
module_function
|
7
|
+
|
8
|
+
#
|
9
|
+
# Specify:
|
10
|
+
# comment_line_regex: matches any line of comments at the start of the source code (when this doesn't match, we'll stop looking)
|
11
|
+
#
|
12
|
+
# tag_line_regex: matches the specific line that contains the tags, the one group in this regex should be the comma-separated list of tags
|
13
|
+
#
|
14
|
+
# tag_line_for(tags): convert the tag string (already a string!) into the output line we should print
|
15
|
+
#
|
16
|
+
|
17
|
+
def get(file_name)
|
18
|
+
File.open(file_name, 'r:UTF-8').each_line do |line|
|
19
|
+
return [] unless line =~ comment_line_regex
|
20
|
+
|
21
|
+
m = line.match(tag_line_regex)
|
22
|
+
if m
|
23
|
+
tag_string = m[1]
|
24
|
+
return [] if tag_string.nil? || tag_string.empty?
|
25
|
+
return tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def set(file_name, tag)
|
33
|
+
tags = get(file_name)
|
34
|
+
return if tags.include? tag
|
35
|
+
|
36
|
+
tags << tag
|
37
|
+
set_tags = false
|
38
|
+
|
39
|
+
outpath = File.tmpnam
|
40
|
+
File.open(outpath, 'w:UTF-8') do |outfile|
|
41
|
+
File.open(file_name, 'r:UTF-8').each_line do |line|
|
42
|
+
if line !~ comment_line_regex
|
43
|
+
if set_tags == false
|
44
|
+
# We haven't set the tags yet, but the current line does *not*
|
45
|
+
# match the comment line regex. Add the tags line as a new line
|
46
|
+
# at the end of the comment block.
|
47
|
+
outfile.puts tag_line_for(tags.join(', '))
|
48
|
+
set_tags = true
|
49
|
+
else
|
50
|
+
outfile.puts line
|
51
|
+
end
|
52
|
+
else
|
53
|
+
if line =~ tag_line_regex
|
54
|
+
# Replace the old tag line with the new tag line
|
55
|
+
outfile.puts tag_line_for(tags.join(', '))
|
56
|
+
else
|
57
|
+
outfile.puts line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
FileUtils.mv(outpath, file_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unset(file_name, tag)
|
67
|
+
tags = get(file_name)
|
68
|
+
return unless tags.include? tag
|
69
|
+
|
70
|
+
tags.delete(tag)
|
71
|
+
|
72
|
+
outpath = File.tmpnam
|
73
|
+
File.open(outpath, 'w:UTF-8') do |outfile|
|
74
|
+
File.open(file_name, 'r:UTF-8').each_line do |line|
|
75
|
+
if line =~ tag_line_regex
|
76
|
+
outfile.puts tag_line_for(tags.join(', '))
|
77
|
+
else
|
78
|
+
outfile.puts line
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
FileUtils.mv(outpath, file_name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def clear(file_name)
|
87
|
+
outpath = File.tmpnam
|
88
|
+
File.open(outpath, 'w:UTF-8') do |outfile|
|
89
|
+
File.open(file_name, 'r:UTF-8').each_line do |line|
|
90
|
+
next if line =~ tag_line_regex
|
91
|
+
outfile.puts line
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
FileUtils.mv(outpath, file_name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'stickyflag/tags/source_code'
|
3
|
+
|
4
|
+
module StickyFlag
|
5
|
+
module Tags
|
6
|
+
module TeX
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def comment_line_regex
|
10
|
+
/\A% .*/
|
11
|
+
end
|
12
|
+
|
13
|
+
def tag_line_regex
|
14
|
+
/\A% SF_TAGS = (.*)/
|
15
|
+
end
|
16
|
+
|
17
|
+
def tag_line_for(str)
|
18
|
+
"% SF_TAGS = #{str}"
|
19
|
+
end
|
20
|
+
|
21
|
+
include SourceCode
|
22
|
+
module_function :get, :set, :unset, :clear
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/stickyflag.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thor'
|
5
|
+
require 'backports'
|
6
|
+
|
7
|
+
require 'stickyflag/version'
|
8
|
+
|
9
|
+
require 'stickyflag/patches/tempfile_encoding'
|
10
|
+
require 'stickyflag/patches/tmpnam.rb'
|
11
|
+
|
12
|
+
require 'stickyflag/tags/source_code'
|
13
|
+
require 'stickyflag/tags/c'
|
14
|
+
require 'stickyflag/tags/mmd'
|
15
|
+
require 'stickyflag/tags/pdf'
|
16
|
+
require 'stickyflag/tags/png'
|
17
|
+
require 'stickyflag/tags/tex'
|
18
|
+
|
19
|
+
require 'stickyflag/configuration'
|
20
|
+
require 'stickyflag/paths'
|
21
|
+
require 'stickyflag/external_cmds'
|
22
|
+
require 'stickyflag/database'
|
23
|
+
require 'stickyflag/tag_factory'
|
24
|
+
|
25
|
+
module StickyFlag
|
26
|
+
class ThorApp < Thor
|
27
|
+
include Thor::Actions
|
28
|
+
class_option :color, :required => false, :default => true,
|
29
|
+
:desc => 'if true, print our status messages in color', :type => :boolean
|
30
|
+
class_option :force, :requires => false, :default => false, :aliases => '-f',
|
31
|
+
:desc => 'do not print diagnostic messages for missing files or empty tags on multiple-file operations'
|
32
|
+
class_option :quiet, :required => false, :default => false, :aliases => '-q',
|
33
|
+
:desc => 'if true, print only essential output to the console (skip empty tags, diagnostics)'
|
34
|
+
|
35
|
+
include Paths
|
36
|
+
include Configuration
|
37
|
+
include ExternalCmds
|
38
|
+
include TagFactory
|
39
|
+
include Database
|
40
|
+
|
41
|
+
def initialize(*args)
|
42
|
+
super
|
43
|
+
|
44
|
+
if options.color?
|
45
|
+
self.shell = Thor::Shell::Color.new
|
46
|
+
end
|
47
|
+
|
48
|
+
load_config!
|
49
|
+
find_external_cmds
|
50
|
+
load_database
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'config', "display and set configuration parameters"
|
54
|
+
long_desc <<-LONGDESC
|
55
|
+
`stickyflag config` allows you to set persistent configuration parameters
|
56
|
+
for StickyFlag.
|
57
|
+
|
58
|
+
With only --key <key> specified, display the current value for the given key.
|
59
|
+
|
60
|
+
With --key <key> <value>, set the value for the key.
|
61
|
+
|
62
|
+
With the --list option, list all available configuration parameters and
|
63
|
+
their current values.
|
64
|
+
LONGDESC
|
65
|
+
method_option :key, :aliases => '-k', :required => false,
|
66
|
+
:desc => 'the configuration key to set', :type => :string
|
67
|
+
method_option :list, :aliases => '-l', :default => false, :required => false,
|
68
|
+
:desc => 'list all available configuration options', :type => :boolean
|
69
|
+
method_option :reset, :default => false, :required => false,
|
70
|
+
:desc => 'reset *all* configuration settings to defaults', :type => :boolean
|
71
|
+
def config(value = nil)
|
72
|
+
if options.reset?
|
73
|
+
reset_config!
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
if options.list? || (options[:key].nil? && value.nil?)
|
78
|
+
dump_config
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
if options[:key].nil?
|
83
|
+
raise Thor::Error.new("ERROR: Cannot set a value without a key specified")
|
84
|
+
end
|
85
|
+
|
86
|
+
if value.nil?
|
87
|
+
value = get_config options[:key]
|
88
|
+
say "#{options[:key]}: '#{value}'"
|
89
|
+
return
|
90
|
+
end
|
91
|
+
|
92
|
+
set_config options[:key], value
|
93
|
+
say "'#{options[:key]}' set to '#{value}'" unless options.quiet?
|
94
|
+
|
95
|
+
save_config!
|
96
|
+
end
|
97
|
+
|
98
|
+
desc 'get [FILES]', "print the tags set for a set of files"
|
99
|
+
long_desc <<-LONGDESC
|
100
|
+
`stickyflag get` lets you look at the tags that have been applied to
|
101
|
+
a file or set of files.
|
102
|
+
LONGDESC
|
103
|
+
def get(*files)
|
104
|
+
if files.empty?
|
105
|
+
raise Thor::Error.new("stickyflag get requires at least 1 argument: \"stickyflag get [FILES]\"")
|
106
|
+
end
|
107
|
+
|
108
|
+
files.each do |file_name|
|
109
|
+
unless File.exist? file_name
|
110
|
+
say_status :error, "File #{file_name} does not exist", :red unless options.force? || options.quiet?
|
111
|
+
next
|
112
|
+
end
|
113
|
+
|
114
|
+
tags = get_tags_for file_name
|
115
|
+
if tags.empty?
|
116
|
+
say "#{file_name}: no tags" unless options.force? || options.quiet?
|
117
|
+
next
|
118
|
+
else
|
119
|
+
say "#{file_name}: #{tags.join(', ')}"
|
120
|
+
next
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
desc 'set [FILE] [TAG]', "set a tag for a file"
|
127
|
+
long_desc <<-LONGDESC
|
128
|
+
`stickyflag set` lets you add one particular tag to the tags present in a
|
129
|
+
given file. Specify the file you want to modify, and the tag you want to
|
130
|
+
add.
|
131
|
+
LONGDESC
|
132
|
+
def set(file_name, tag)
|
133
|
+
check_tag tag
|
134
|
+
|
135
|
+
unless File.exist? file_name
|
136
|
+
say_status :error, "File #{file_name} does not exist", :red unless options.quiet?
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
set_tag_for file_name, tag
|
141
|
+
|
142
|
+
tags = get_tags_for file_name
|
143
|
+
say_status :success, "New tags for #{file_name}: #{tags.join(', ')}", :green unless options.quiet?
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
desc 'unset [FILE] [TAG]', "remove a tag from a file"
|
148
|
+
long_desc <<-LONGDESC
|
149
|
+
`stickyflag unset` lets you delete one tag from the tags present in a file.
|
150
|
+
Specify the file you want to modify, and the tag you want to remove. This
|
151
|
+
action will fail if the tag is not set in the requested file.
|
152
|
+
LONGDESC
|
153
|
+
def unset(file_name, tag)
|
154
|
+
check_tag tag
|
155
|
+
|
156
|
+
unless File.exist? file_name
|
157
|
+
say_status :error, "File #{file_name} does not exist", :red unless options.quiet?
|
158
|
+
return
|
159
|
+
end
|
160
|
+
|
161
|
+
unset_tag_for file_name, tag
|
162
|
+
|
163
|
+
# Unsetting a tag might leave us with no tags at all, which makes this more
|
164
|
+
# complicated than the #set behavior
|
165
|
+
unless options.quiet?
|
166
|
+
tags = get_tags_for file_name
|
167
|
+
if tags.empty?
|
168
|
+
say_status :success, "New tags for #{file_name}: no tags", :green
|
169
|
+
else
|
170
|
+
say_status :success, "New tags for #{file_name}: #{tags.join(', ')}", :green
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
desc 'clear [FILES]', "remove all tags from a set of files"
|
177
|
+
long_desc <<-LONGDESC
|
178
|
+
`stickyflag clear` removes all tags that have been applied to the given
|
179
|
+
list of files.
|
180
|
+
LONGDESC
|
181
|
+
def clear(*files)
|
182
|
+
if files.empty?
|
183
|
+
raise Thor::Error.new("stickyflag clear requires at least 1 argument: \"stickyflag clear [FILES]\"")
|
184
|
+
end
|
185
|
+
|
186
|
+
files.each do |file_name|
|
187
|
+
unless File.exist? file_name
|
188
|
+
say_status :error, "File #{file_name} does not exist", :red unless options.force? || options.quiet?
|
189
|
+
return
|
190
|
+
end
|
191
|
+
|
192
|
+
clear_tags_for file_name
|
193
|
+
say_status :success, "Tags cleared for #{file_name}", :green unless options.quiet?
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
desc 'update', "update the tag database from the files on disk"
|
198
|
+
long_desc <<-LONGDESC
|
199
|
+
`stickyflag update` will read all files under the current directory (or
|
200
|
+
using the `root` configuration key) and refresh the database based on their
|
201
|
+
contents.
|
202
|
+
LONGDESC
|
203
|
+
def update
|
204
|
+
root = get_config(:root).strip
|
205
|
+
root = '.' if root.empty? || root.nil?
|
206
|
+
|
207
|
+
update_database_from_files root
|
208
|
+
end
|
209
|
+
|
210
|
+
desc 'tags', "show the list of currently used tags"
|
211
|
+
long_desc <<-LONGDESC
|
212
|
+
`stickyflag tags` shows the list of currently used tags as present in the
|
213
|
+
database. Note that this may be out of date with respect to the contents
|
214
|
+
of the disk, and can be refreshed using `stickyflag update`.
|
215
|
+
LONGDESC
|
216
|
+
def tags
|
217
|
+
say "Tags currently in use:" unless options.quiet?
|
218
|
+
padding = ''
|
219
|
+
padding = ' ' unless options.quiet?
|
220
|
+
|
221
|
+
tag_list.each do |t|
|
222
|
+
say "#{padding}#{t}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
desc 'find [TAG] [...]', "show all files that are tagged with the given tags"
|
227
|
+
long_desc <<-LONGDESC
|
228
|
+
`stickyflag find` locates all files that have a given tag or set of tags.
|
229
|
+
If multiple tags are provided, returned files must have all those tags.
|
230
|
+
LONGDESC
|
231
|
+
def find(*tags)
|
232
|
+
if tags.empty?
|
233
|
+
raise Thor::Error.new("stickyflag find requires at least 1 argument: \"stickyflag find [TAG] [...]\"")
|
234
|
+
end
|
235
|
+
tags.each { |t| check_tag t }
|
236
|
+
|
237
|
+
files_for_tags(tags).each do |file|
|
238
|
+
say file
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def check_tag(tag)
|
245
|
+
if tag.include? ','
|
246
|
+
raise Thor::Error.new("ERROR: Tag names cannot include a comma.")
|
247
|
+
end
|
248
|
+
if tag.empty?
|
249
|
+
raise Thor::Error.new("ERROR: Cannot set an empty tag.")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
$KCODE = 'U' if RUBY_VERSION < "1.9.0"
|
3
|
+
ENV['RSPEC_TESTING'] = 'true'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'simplecov' unless ENV["CI"] == 'true'
|
8
|
+
|
9
|
+
require 'backports'
|
10
|
+
require 'rspec/autorun'
|
11
|
+
require 'stickyflag'
|
12
|
+
|
13
|
+
Dir['./spec/support/**/*.rb'].map { |f| require f }
|
14
|
+
|
15
|
+
RSpec.configure do |c|
|
16
|
+
c.before(:each) do
|
17
|
+
# Don't write anything to the console
|
18
|
+
Thor::Shell::Basic.any_instance.stub(:say) { }
|
19
|
+
Thor::Shell::Basic.any_instance.stub(:say_status) { }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|