stickyflag 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  language: ruby
2
2
  before_install:
3
3
  - sudo apt-get update -qq
4
- - sudo apt-get install -qq pdftk
4
+ - sudo apt-get install -qq pdftk mkvtoolnix
5
5
  script: "bundle exec rake"
6
6
 
7
7
  env:
data/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
 
4
4
  *file tagging for everyone*
5
5
 
6
+ [![Build Status](https://secure.travis-ci.org/cpence/stickyflag.png)](http://travis-ci.org/cpence/stickyflag) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/cpence/stickyflag)
7
+
6
8
  ## What does it do?
7
9
 
8
10
  Filesystems are great. Most of our file organization tasks can be performed by arranging things in hierarchical directories. But every once in a while, you have an organization problem that can really only be resolved with categories that *cut across* these hierarchical structures. What if you want everything related to Project X, including its code, meeting notes about it, photos you took of it, and so on? The code is in your development directory, meeting notes in a meetings directory, and photos in an images directory.
@@ -22,11 +24,13 @@ StickyFlag can currently tag:
22
24
  * MultiMarkdown files
23
25
  * PDF files (with `pdftk` installed; see below)
24
26
  * PNG files
27
+ * Matroska video files
25
28
  * Source code (C/C++, TeX)
26
29
 
27
30
  ## What else does it need?
28
31
 
29
- If you're going to tag PDF files, you need to install [pdftk,](http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) which is available for Windows, Mac OS X, and Linux (licensed under the GPL).
32
+ * If you're going to tag PDF files, you need to install [pdftk,](http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) which is available for all platforms (licensed under the GPL).
33
+ * If you're going to tag MKV files, you need to install [mkvtoolnix,](http://www.bunkus.org/videotools/mkvtoolnix/) which is available for all platforms (licensed under the GPL).
30
34
 
31
35
  ## How do I get it?
32
36
 
@@ -8,6 +8,11 @@ module StickyFlag
8
8
  DEFAULT_CONFIG = {
9
9
  :have_pdftk => false,
10
10
  :pdftk_path => '',
11
+ :have_mkvextract => false,
12
+ :mkvextract_path => '',
13
+ :have_mkvpropedit => false,
14
+ :mkvpropedit_path => '',
15
+
11
16
  :root => ''
12
17
  }
13
18
 
@@ -15,7 +20,7 @@ module StickyFlag
15
20
  @configuration ||= DEFAULT_CONFIG.clone
16
21
 
17
22
  unless @configuration.keys.include?(key.to_sym)
18
- raise Thor::Error.new('ERROR: Invalid configuration key')
23
+ raise Thor::Error.new("ERROR: Invalid configuration key (#{key.to_s})")
19
24
  end
20
25
 
21
26
  @configuration[key.to_sym]
@@ -25,7 +30,7 @@ module StickyFlag
25
30
  @configuration ||= DEFAULT_CONFIG.clone
26
31
 
27
32
  unless @configuration.keys.include?(key.to_sym)
28
- raise Thor::Error.new('ERROR: invalid configuration key')
33
+ raise Thor::Error.new("ERROR: invalid configuration key (#{key.to_s})")
29
34
  end
30
35
 
31
36
  @configuration[key.to_sym] = value
@@ -51,6 +56,9 @@ module StickyFlag
51
56
  file_name = config_path
52
57
  if File.file? file_name
53
58
  @configuration = YAML::load(File.open(file_name, 'r:UTF-8'))
59
+
60
+ # Merge with the default to pick up new keys
61
+ @configuration = DEFAULT_CONFIG.merge(@configuration)
54
62
  end
55
63
  end
56
64
 
@@ -4,7 +4,9 @@ module StickyFlag
4
4
  module ExternalCmds
5
5
 
6
6
  EXTERNAL_CMDS = {
7
- 'pdftk' => 'read and write PDF tags'
7
+ 'pdftk' => 'read and write PDF tags',
8
+ 'mkvextract' => 'read MKV tags',
9
+ 'mkvpropedit' => 'write MKV tags'
8
10
  }
9
11
 
10
12
  def find_external_cmds
@@ -3,7 +3,6 @@ require 'thor'
3
3
  require 'pathname'
4
4
  require 'backports'
5
5
  require 'stickyflag/tags/pdf'
6
- require 'stickyflag/tags/pdf'
7
6
  require 'stickyflag/tags/png'
8
7
  require 'stickyflag/tags/tex'
9
8
  require 'stickyflag/tags/c'
@@ -11,98 +10,58 @@ require 'stickyflag/tags/mmd'
11
10
 
12
11
  module StickyFlag
13
12
  module TagFactory
13
+ TAG_MODULES = StickyFlag::Tags.constants.map { |sym|
14
+ StickyFlag::Tags.const_get(sym)
15
+ }.select { |const|
16
+ const.is_a?(Module) && const.respond_to?(:extensions)
17
+ }
18
+
19
+ TAG_EXTENSIONS = TAG_MODULES.map { |mod| mod.extensions }.reduce(:|)
20
+
14
21
  def available_tagging_extensions
15
- ['.pdf', '.png', '.tex', '.c', '.cpp', '.cxx', '.c++',
16
- '.h', '.hpp', '.hxx', '.mmd']
22
+ TAG_EXTENSIONS
17
23
  end
18
-
19
- def get_tags_for(file_name)
24
+
25
+ def call_tag_method(file_name, method, *args)
20
26
  extension = File.extname file_name
21
- case extension
22
- when '.pdf' then return StickyFlag::Tags::PDF::get(file_name, get_config(:pdftk_path))
23
- when '.png' then return StickyFlag::Tags::PNG::get(file_name)
24
- when '.tex' then return StickyFlag::Tags::TeX::get(file_name)
25
- when '.c' then return StickyFlag::Tags::C::get(file_name)
26
- when '.cpp' then return StickyFlag::Tags::C::get(file_name)
27
- when '.cxx' then return StickyFlag::Tags::C::get(file_name)
28
- when '.c++' then return StickyFlag::Tags::C::get(file_name)
29
- when '.h' then return StickyFlag::Tags::C::get(file_name)
30
- when '.hpp' then return StickyFlag::Tags::C::get(file_name)
31
- when '.hxx' then return StickyFlag::Tags::C::get(file_name)
32
- when '.mmd' then return StickyFlag::Tags::MMD::get(file_name)
33
- else raise Thor::Error.new("ERROR: Don't know how to get tags for a file of extension '#{extension}'")
27
+
28
+ TAG_MODULES.each do |mod|
29
+ if mod.send(:extensions).include? extension
30
+ if mod.respond_to?(:config_values)
31
+ config_values = mod.send(:config_values)
32
+ config_values.each { |val| args << get_config(val) }
33
+ end
34
+
35
+ return mod.send(method, file_name, *args)
36
+ end
34
37
  end
38
+
39
+ raise Thor::Error.new("ERROR: Don't know how to tag a file of extension '#{extension}'")
40
+ end
41
+
42
+ def get_tags_for(file_name)
43
+ call_tag_method(file_name, :get)
35
44
  end
36
45
 
37
46
  def set_tag_for(file_name, tag)
38
47
  tags = get_tags_for file_name
39
- if tags.include? tag
40
- # Already set
41
- return
42
- end
48
+ return if tags.include? tag
43
49
 
44
50
  set_database_tag file_name, tag
45
-
46
- extension = File.extname file_name
47
- case extension
48
- when '.pdf' then return StickyFlag::Tags::PDF::set(file_name, tag, get_config(:pdftk_path))
49
- when '.png' then return StickyFlag::Tags::PNG::set(file_name, tag)
50
- when '.tex' then return StickyFlag::Tags::TeX::set(file_name, tag)
51
- when '.c' then return StickyFlag::Tags::C::set(file_name, tag)
52
- when '.cpp' then return StickyFlag::Tags::C::set(file_name, tag)
53
- when '.cxx' then return StickyFlag::Tags::C::set(file_name, tag)
54
- when '.c++' then return StickyFlag::Tags::C::set(file_name, tag)
55
- when '.h' then return StickyFlag::Tags::C::set(file_name, tag)
56
- when '.hpp' then return StickyFlag::Tags::C::set(file_name, tag)
57
- when '.hxx' then return StickyFlag::Tags::C::set(file_name, tag)
58
- when '.mmd' then return StickyFlag::Tags::MMD::set(file_name, tag)
59
- else raise Thor::Error.new("ERROR: Don't know how to set tags for a file of extension '#{extension}'")
60
- end
51
+ call_tag_method(file_name, :set, tag)
61
52
  end
62
53
 
63
54
  def unset_tag_for(file_name, tag)
64
55
  tags = get_tags_for file_name
65
- unless tags.include? tag
66
- raise Thor::Error.new("ERROR: Cannot unset tag #{tag} from file, not set")
67
- end
56
+ raise Thor::Error.new("ERROR: Cannot unset tag #{tag} from file, not set") unless tags.include? tag
68
57
 
69
58
  unset_database_tag file_name, tag
70
-
71
- extension = File.extname file_name
72
- case extension
73
- when '.pdf' then return StickyFlag::Tags::PDF::unset(file_name, tag, get_config(:pdftk_path))
74
- when '.png' then return StickyFlag::Tags::PNG::unset(file_name, tag)
75
- when '.tex' then return StickyFlag::Tags::TeX::unset(file_name, tag)
76
- when '.c' then return StickyFlag::Tags::C::unset(file_name, tag)
77
- when '.cpp' then return StickyFlag::Tags::C::unset(file_name, tag)
78
- when '.cxx' then return StickyFlag::Tags::C::unset(file_name, tag)
79
- when '.c++' then return StickyFlag::Tags::C::unset(file_name, tag)
80
- when '.h' then return StickyFlag::Tags::C::unset(file_name, tag)
81
- when '.hpp' then return StickyFlag::Tags::C::unset(file_name, tag)
82
- when '.hxx' then return StickyFlag::Tags::C::unset(file_name, tag)
83
- when '.mmd' then return StickyFlag::Tags::MMD::unset(file_name, tag)
84
- else raise Thor::Error.new("ERROR: Don't know how to unset tags for a file of extension '#{extension}'")
85
- end
59
+ call_tag_method(file_name, :unset, tag)
86
60
  end
87
61
 
88
62
  def clear_tags_for(file_name)
89
63
  clear_database_tags file_name
90
-
91
- extension = File.extname file_name
92
- case extension
93
- when '.pdf' then return StickyFlag::Tags::PDF::clear(file_name, get_config(:pdftk_path))
94
- when '.png' then return StickyFlag::Tags::PNG::clear(file_name)
95
- when '.tex' then return StickyFlag::Tags::TeX::clear(file_name)
96
- when '.c' then return StickyFlag::Tags::C::clear(file_name)
97
- when '.cpp' then return StickyFlag::Tags::C::clear(file_name)
98
- when '.cxx' then return StickyFlag::Tags::C::clear(file_name)
99
- when '.c++' then return StickyFlag::Tags::C::clear(file_name)
100
- when '.h' then return StickyFlag::Tags::C::clear(file_name)
101
- when '.hpp' then return StickyFlag::Tags::C::clear(file_name)
102
- when '.hxx' then return StickyFlag::Tags::C::clear(file_name)
103
- when '.mmd' then return StickyFlag::Tags::MMD::clear(file_name)
104
- else raise Thor::Error.new("ERROR: Don't know how to clear all tags for a file of extension '#{extension}'")
105
- end
64
+ call_tag_method(file_name, :clear)
106
65
  end
107
66
  end
108
67
  end
@@ -5,6 +5,10 @@ module StickyFlag
5
5
  module Tags
6
6
  module C
7
7
  module_function
8
+
9
+ def extensions
10
+ [ '.c', '.cpp', '.cxx', '.c++', '.h', '.hpp', '.hxx' ]
11
+ end
8
12
 
9
13
  def comment_line_regex
10
14
  /\A\/\/ .*/
@@ -0,0 +1,204 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'thor'
3
+ require 'open3'
4
+ require 'nokogiri'
5
+ require 'stickyflag/patches/tempfile_encoding'
6
+
7
+ module StickyFlag
8
+ module Tags
9
+ module MKV
10
+ module_function
11
+
12
+ def extensions
13
+ [ '.mkv' ]
14
+ end
15
+ def config_values
16
+ [ :mkvextract_path, :mkvpropedit_path ]
17
+ end
18
+
19
+ def get_tag_xml(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
20
+ stdout_str = ''
21
+ stderr_str = ''
22
+
23
+ begin
24
+ Open3.popen3("#{mkvextract_path} tags #{file_name} -q") do |i, o, e, t|
25
+ out_reader = Thread.new { o.read }
26
+ err_reader = Thread.new { e.read }
27
+ i.close
28
+ stdout_str = out_reader.value
29
+ stderr_str = err_reader.value
30
+
31
+ stdout_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
32
+ stderr_str.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
33
+ end
34
+ rescue Exception
35
+ raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; mkvextract call failed")
36
+ end
37
+ if stderr_str.start_with?("Error: ") || stderr_str.include?("Errno::ENOENT") || stdout_str.start_with?("Error: ")
38
+ raise Thor::Error.new("ERROR: Failed to get tags for #{file_name}; mkvextract call failed")
39
+ end
40
+
41
+ if stdout_str == ''
42
+ # This is what happens when a file has no tags whatsoever, we need
43
+ # to build a skeleton document
44
+ stdout_str = <<-XML
45
+ <?xml version="1.0" encoding="UTF-8"?>
46
+
47
+ <!DOCTYPE Tags SYSTEM "matroskatags.dtd">
48
+
49
+ <Tags>
50
+ <Tag>
51
+ <Targets>
52
+ </Targets>
53
+ </Tag>
54
+ </Tags>
55
+ XML
56
+ end
57
+
58
+ # Strip off newlines and BOM, these wreak havoc on the Java XML parser,
59
+ # which considers them content before the prolog
60
+ bom = "\xEF\xBB\xBF"
61
+ bom.force_encoding("UTF-8") if RUBY_VERSION >= "1.9.0"
62
+ stdout_str.strip!.gsub!(bom, '')
63
+
64
+ Nokogiri::XML(stdout_str)
65
+ end
66
+
67
+ def get_stickyflag_root(xml_doc)
68
+ xml_doc.xpath("/Tags/Tag").each do |tag|
69
+ targets = tag.at_xpath("Targets")
70
+ next if targets.nil? # This shouldn't ever happen
71
+
72
+ # If there's no UID elements under this tag, then we're good (note
73
+ # that sometimes mkvpropedit will *force* in a TargetTypeValue or
74
+ # a TargetType, and we don't want to block on those)
75
+ good = true
76
+ targets.children.each do |child|
77
+ if child.name.end_with? 'UID'
78
+ good = false
79
+ break
80
+ end
81
+ end
82
+
83
+ return tag if good
84
+ end
85
+
86
+ # Make one, then
87
+ tag_tag = Nokogiri::XML::Node.new 'Tag', xml_doc
88
+ targets_tag = Nokogiri::XML::Node.new 'Targets', xml_doc
89
+ tag_tag.add_child targets_tag
90
+
91
+ root_elem = xml_doc.at_xpath("/Tags")
92
+ root_elem.add_child tag_tag
93
+
94
+ tag_tag
95
+ end
96
+
97
+ def get_stickyflag_tag(xml_doc)
98
+ tag_tag = get_stickyflag_root(xml_doc)
99
+ if tag_tag.nil?
100
+ # Should never happen!
101
+ raise Thor::Error.new("INTERNAL ERROR: Failed to find the StickyFlag tag root in MKV XML")
102
+ end
103
+
104
+ tag_tag.at_xpath("Simple[Name = 'X_STICKYFLAG_FLAGS']/String")
105
+ end
106
+
107
+ def set_tag_xml(xml_doc, file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
108
+ # Write out this XML file and attach it to the MKV
109
+ outfile = Tempfile.new_with_encoding ['sfmkvtag', '.xml']
110
+ begin
111
+ outfile.write(xml_doc.to_xml)
112
+ outfile.close
113
+
114
+ ret = system(mkvpropedit_path, file_name, '--tags', "all:#{outfile.path}", "-q")
115
+ unless ret == true
116
+ raise Thor::Error.new("ERROR: Failed to update tag for #{file_name}; mkvpropedit call failed")
117
+ end
118
+ ensure
119
+ outfile.unlink
120
+ end
121
+ end
122
+
123
+ def get(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
124
+ xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
125
+ stickyflag_tag = get_stickyflag_tag(xml_doc)
126
+ return [] if stickyflag_tag.nil?
127
+
128
+ tag_string = stickyflag_tag.content
129
+ return [] if tag_string.nil? || tag_string.empty?
130
+
131
+ tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
132
+ end
133
+
134
+ def set(file_name, tag, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
135
+ xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
136
+ stickyflag_tag = get_stickyflag_tag(xml_doc)
137
+
138
+ unless stickyflag_tag.nil?
139
+ # We already have the right tag, check it
140
+ tag_string = stickyflag_tag.content
141
+ unless tag_string.nil? || tag_string.empty?
142
+ tags = tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
143
+ return if tags.include? tag
144
+ tags << tag
145
+
146
+ # Set the new string in the XML file
147
+ new_tag_string = tags.join(', ')
148
+ stickyflag_tag.content = new_tag_string
149
+ else
150
+ # Somehow the tag has no content, write it in
151
+ stickyflag_tag.content = tag
152
+ end
153
+ else
154
+ # No tag, add it
155
+ root = get_stickyflag_root(xml_doc)
156
+ simple_tag = Nokogiri::XML::Node.new 'Simple', xml_doc
157
+
158
+ name_tag = Nokogiri::XML::Node.new 'Name', xml_doc
159
+ name_tag.content = 'X_STICKYFLAG_FLAGS'
160
+ string_tag = Nokogiri::XML::Node.new 'String', xml_doc
161
+ string_tag.content = tag
162
+
163
+ simple_tag.add_child(name_tag)
164
+ simple_tag.add_child(string_tag)
165
+
166
+ root.add_child(simple_tag)
167
+ end
168
+
169
+ set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
170
+ end
171
+
172
+ def unset(file_name, tag, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
173
+ xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
174
+ stickyflag_tag = get_stickyflag_tag(xml_doc)
175
+
176
+ unless stickyflag_tag.nil?
177
+ # We already have the right tag, check it
178
+ tag_string = stickyflag_tag.content
179
+ unless tag_string.nil? || tag_string.empty?
180
+ tags = tag_string.split(',').map { |t| t.empty? ? nil : t.strip }.compact
181
+ return unless tags.include? tag
182
+ tags.delete(tag)
183
+
184
+ # Set the new string in the XML file
185
+ new_tag_string = tags.join(', ')
186
+ stickyflag_tag.content = new_tag_string
187
+
188
+ set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
189
+ end
190
+ end
191
+ end
192
+
193
+ def clear(file_name, mkvextract_path = 'mkvextract', mkvpropedit_path = 'mkvpropedit')
194
+ xml_doc = get_tag_xml(file_name, mkvextract_path, mkvpropedit_path)
195
+ stickyflag_tag = get_stickyflag_tag(xml_doc)
196
+
197
+ unless stickyflag_tag.nil?
198
+ stickyflag_tag.remove
199
+ set_tag_xml(xml_doc, file_name, mkvextract_path, mkvpropedit_path)
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -5,6 +5,10 @@ module StickyFlag
5
5
  module Tags
6
6
  module MMD
7
7
  module_function
8
+
9
+ def extensions
10
+ [ '.md', '.mmd', '.markdown', '.text' ]
11
+ end
8
12
 
9
13
  # We duplicate the source-code module here because we want to have
10
14
  # cute support for the indentation of tag contents in MMD, and because
@@ -7,6 +7,13 @@ module StickyFlag
7
7
  module Tags
8
8
  module PDF
9
9
  module_function
10
+
11
+ def extensions
12
+ [ '.pdf' ]
13
+ end
14
+ def config_values
15
+ [ :pdftk_path ]
16
+ end
10
17
 
11
18
  def get(file_name, pdftk_path = 'pdftk')
12
19
  stdout_str = ''
@@ -47,13 +54,8 @@ module StickyFlag
47
54
 
48
55
  tags
49
56
  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
 
58
+ def write_tags_to(file_name, tags, pdftk_path = 'pdftk')
57
59
  info = Tempfile.new_with_encoding ['sfpdftag', '.txt']
58
60
  begin
59
61
  info.write("InfoKey: X-StickyFlag-Flags\n")
@@ -61,9 +63,9 @@ module StickyFlag
61
63
  info.close
62
64
 
63
65
  outpath = File.tmpnam('.pdf')
64
- ret = system(pdftk_path, file_name.to_s, 'update_info', info.path, 'output', outpath)
66
+ ret = system(pdftk_path, file_name, 'update_info', info.path, 'output', outpath)
65
67
  unless ret == true
66
- raise Thor::Error.new("ERROR: Failed to set tag for #{file_name}; pdftk call failed")
68
+ raise Thor::Error.new("ERROR: Failed to write tag for #{file_name}; pdftk call failed")
67
69
  end
68
70
 
69
71
  FileUtils.mv outpath, file_name
@@ -71,48 +73,27 @@ module StickyFlag
71
73
  info.unlink
72
74
  end
73
75
  end
76
+
77
+ def set(file_name, tag, pdftk_path = 'pdftk')
78
+ tags = get(file_name, pdftk_path)
79
+ return if tags.include? tag
80
+
81
+ tags << tag
82
+
83
+ write_tags_to(file_name, tags, pdftk_path)
84
+ end
74
85
 
75
86
  def unset(file_name, tag, pdftk_path = 'pdftk')
76
87
  tags = get(file_name, pdftk_path)
77
88
  return unless tags.include? tag
78
89
 
79
90
  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
91
 
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
92
+ write_tags_to(file_name, tags, pdftk_path)
97
93
  end
98
94
 
99
95
  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
96
+ write_tags_to(file_name, [], pdftk_path)
116
97
  end
117
98
  end
118
99
  end
@@ -11,6 +11,10 @@ module StickyFlag
11
11
  module Tags
12
12
  module PNG
13
13
  module_function
14
+
15
+ def extensions
16
+ [ '.png' ]
17
+ end
14
18
 
15
19
  def get(file_name)
16
20
  image = ChunkyPNG::Image.from_file(file_name)
@@ -5,6 +5,10 @@ module StickyFlag
5
5
  module Tags
6
6
  module TeX
7
7
  module_function
8
+
9
+ def extensions
10
+ [ '.tex' ]
11
+ end
8
12
 
9
13
  def comment_line_regex
10
14
  /\A% .*/
@@ -4,7 +4,7 @@ module StickyFlag
4
4
  module Version
5
5
  NUMBERS = [
6
6
  MAJOR = 0,
7
- MINOR = 2,
7
+ MINOR = 3,
8
8
  BUILD = 0
9
9
  ]
10
10
  end
@@ -51,7 +51,7 @@ describe 'StickyFlag::ExternalCmds' do
51
51
  end
52
52
 
53
53
  it 'outputs a warning message' do
54
- @obj.should_receive(:say_status).with(:warning, kind_of(String), kind_of(Symbol))
54
+ @obj.should_receive(:say_status).with(:warning, kind_of(String), kind_of(Symbol)).at_least(:once)
55
55
  @obj.find_external_cmds
56
56
  end
57
57
  end
@@ -81,8 +81,8 @@ describe 'StickyFlag::ExternalCmds' do
81
81
  end
82
82
 
83
83
  it 'should not print any messages' do
84
- @obj.should_not_receive(:say)
85
- @obj.should_not_receive(:say_status)
84
+ @obj.should_not_receive(:say).with(/pdftk/)
85
+ @obj.should_not_receive(:say_status).with(:warning, /pdftk/, kind_of(Symbol))
86
86
  @obj.find_external_cmds
87
87
  end
88
88
  end
@@ -109,7 +109,7 @@ describe 'StickyFlag::ExternalCmds' do
109
109
 
110
110
  it 'prints out an error message' do
111
111
  @obj.stub(:say_status) {}
112
- @obj.should_receive(:say_status).with(:error, kind_of(String), kind_of(Symbol))
112
+ @obj.should_receive(:say_status).with(:error, /pdftk/, kind_of(Symbol))
113
113
  @obj.find_external_cmds
114
114
  end
115
115
  end
@@ -136,7 +136,7 @@ describe 'StickyFlag::ExternalCmds' do
136
136
 
137
137
  it 'prints out an error message' do
138
138
  @obj.stub(:say_status) {}
139
- @obj.should_receive(:say_status).with(:error, kind_of(String), kind_of(Symbol))
139
+ @obj.should_receive(:say_status).with(:error, /pdftk/, kind_of(Symbol))
140
140
  @obj.find_external_cmds
141
141
  end
142
142
  end
@@ -166,8 +166,8 @@ describe 'StickyFlag::ExternalCmds' do
166
166
  end
167
167
 
168
168
  it 'should not print any messages' do
169
- @obj.should_not_receive(:say)
170
- @obj.should_not_receive(:say_status)
169
+ @obj.should_not_receive(:say).with(/pdftk/)
170
+ @obj.should_not_receive(:say_status).with(:warning, /pdftk/, kind_of(Symbol))
171
171
  @obj.find_external_cmds
172
172
  end
173
173
  end
@@ -33,70 +33,11 @@ describe 'StickyFlag::TagFactory' do
33
33
  end
34
34
 
35
35
  describe '.available_tagging_extensions' do
36
- # Some extensions require the presence of actual, good files, not
37
- # just empty shells, so we can't test them in this way. Skip those.
38
- CANNOT_TEST = [ '.pdf', '.png' ]
39
-
40
- it 'should call get for every good extension' do
41
- (@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
42
- file = Tempfile.new_with_encoding(['ext', ext])
43
- file.puts('test')
44
- file.close
45
-
46
- expect { @obj.get_tags_for(file.path) }.to_not raise_error
47
- file.unlink
48
- end
49
- end
50
-
51
- it 'should call get for other extensions' do
52
- CANNOT_TEST.each do |ext|
53
- path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
54
- expect { @obj.clear_tags_for(path) }.to_not raise_error
55
- File.unlink(path)
56
- end
57
- end
58
-
59
- it 'should call set/unset for every good extension' do
60
- (@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
61
- file = Tempfile.new_with_encoding(['ext', ext])
62
- file.puts('test')
63
- file.close
64
-
65
- expect {
66
- @obj.set_tag_for(file.path, 'test')
67
- @obj.unset_tag_for(file.path, 'test')
68
- }.to_not raise_error
69
- file.unlink
70
- end
71
- end
72
-
73
- it 'should call set/unset for other extensions' do
74
- CANNOT_TEST.each do |ext|
75
- path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
76
- expect {
77
- @obj.set_tag_for(path, 'test2')
78
- @obj.unset_tag_for(path, 'test2')
79
- }.to_not raise_error
80
- File.unlink(path)
81
- end
82
- end
83
-
84
- it 'should call clear for every good extension' do
85
- (@obj.available_tagging_extensions - CANNOT_TEST).each do |ext|
86
- file = Tempfile.new_with_encoding(['ext', ext])
87
- file.puts('test')
88
- file.close
89
-
90
- expect { @obj.clear_tags_for(file.path) }.to_not raise_error
91
- file.unlink
92
- end
93
- end
94
-
95
- it 'should call clear for other extensions' do
96
- CANNOT_TEST.each do |ext|
97
- path = copy_example "#{ext[1..-1]}_with_tag#{ext}"
98
- expect { @obj.clear_tags_for(path) }.to_not raise_error
99
- File.unlink(path)
36
+ it 'should be able to call through to a class method for every good extension' do
37
+ @obj.available_tagging_extensions.each do |ext|
38
+ # This is a bit of a hack -- call TagClass::respond_to?, which takes
39
+ # one argument
40
+ expect { @obj.call_tag_method("test#{ext}", :respond_to?) }.to_not raise_error
100
41
  end
101
42
  end
102
43
  end
@@ -0,0 +1,54 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'stickyflag/tags/mkv'
3
+ require 'stickyflag/paths'
4
+ require 'stickyflag/configuration'
5
+ require 'stickyflag/external_cmds'
6
+
7
+ class GetConfiguration
8
+ include StickyFlag::Paths
9
+ include StickyFlag::Configuration
10
+ include StickyFlag::ExternalCmds
11
+ end
12
+
13
+ describe StickyFlag::Tags::MKV do
14
+ before(:each) do
15
+ config = GetConfiguration.new
16
+ config.stub(:load_config!) { }
17
+ config.stub(:save_config!) { }
18
+
19
+ config.find_external_cmds
20
+
21
+ @mkve = config.get_config(:mkvextract_path)
22
+ @mkvp = config.get_config(:mkvpropedit_path)
23
+ end
24
+
25
+ it_behaves_like 'a tag handler' do
26
+ let(:params) {
27
+ [ @mkve, @mkvp ]
28
+ }
29
+ end
30
+
31
+ context 'when we set our own tag' do
32
+ it "doesn't wipe out other tags set on the file" do
33
+ path = copy_example("mkv_with_tag.mkv")
34
+ StickyFlag::Tags::MKV.set(path, 'test2', @mkve, @mkvp)
35
+
36
+ # This is an internal method, but it's too useful not to use
37
+ xml_doc = StickyFlag::Tags::MKV.get_tag_xml(path, @mkve, @mkvp)
38
+
39
+ # This is one of the original tags on the file
40
+ tag = xml_doc.at_xpath("/Tags/Tag[Targets[TargetTypeValue = '50']]/Simple[Name = 'TITLE']")
41
+ tag.should_not be_nil
42
+ tag.at_xpath("String").content.should eq('Big Buck Bunny - test 1')
43
+ end
44
+ end
45
+
46
+ context 'with bad mkv utility paths' do
47
+ it 'raises errors for everything' do
48
+ expect { StickyFlag::Tags::MKV.get(example_path("mkv_with_tag.mkv"), '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
49
+ expect { StickyFlag::Tags::MKV.clear(example_path("mkv_with_tag.mkv"), '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
50
+ expect { StickyFlag::Tags::MKV.set(example_path("mkv_with_tag.mkv"), 'test2', '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
51
+ expect { StickyFlag::Tags::MKV.unset(example_path("mkv_with_tag.mkv"), 'test', '/wut/bad', '/wut/no') }.to raise_error(Thor::Error)
52
+ end
53
+ end
54
+ end
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_runtime_dependency 'thor'
20
20
  s.add_runtime_dependency 'backports'
21
21
  s.add_runtime_dependency 'xdg'
22
+ s.add_runtime_dependency 'nokogiri'
22
23
  s.add_runtime_dependency 'chunky_png'
23
24
  s.add_runtime_dependency 'sequel'
24
25
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stickyflag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-31 00:00:00.000000000 Z
12
+ date: 2012-11-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: nokogiri
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
62
78
  - !ruby/object:Gem::Dependency
63
79
  name: chunky_png
64
80
  requirement: !ruby/object:Gem::Requirement
@@ -282,6 +298,7 @@ files:
282
298
  - lib/stickyflag/paths.rb
283
299
  - lib/stickyflag/tag_factory.rb
284
300
  - lib/stickyflag/tags/c.rb
301
+ - lib/stickyflag/tags/mkv.rb
285
302
  - lib/stickyflag/tags/mmd.rb
286
303
  - lib/stickyflag/tags/pdf.rb
287
304
  - lib/stickyflag/tags/png.rb
@@ -297,6 +314,7 @@ files:
297
314
  - spec/stickyflag/paths_spec.rb
298
315
  - spec/stickyflag/tag_factory_spec.rb
299
316
  - spec/stickyflag/tags/c_spec.rb
317
+ - spec/stickyflag/tags/mkv_spec.rb
300
318
  - spec/stickyflag/tags/mmd_spec.rb
301
319
  - spec/stickyflag/tags/pdf_spec.rb
302
320
  - spec/stickyflag/tags/png_spec.rb
@@ -306,6 +324,8 @@ files:
306
324
  - spec/support/examples/c_all_comments.c
307
325
  - spec/support/examples/c_no_tags.c
308
326
  - spec/support/examples/c_with_tag.c
327
+ - spec/support/examples/mkv_no_tags.mkv
328
+ - spec/support/examples/mkv_with_tag.mkv
309
329
  - spec/support/examples/mmd_all_meta.mmd
310
330
  - spec/support/examples/mmd_crazy_keys.mmd
311
331
  - spec/support/examples/mmd_crazy_tags.mmd
@@ -373,6 +393,7 @@ test_files:
373
393
  - spec/stickyflag/paths_spec.rb
374
394
  - spec/stickyflag/tag_factory_spec.rb
375
395
  - spec/stickyflag/tags/c_spec.rb
396
+ - spec/stickyflag/tags/mkv_spec.rb
376
397
  - spec/stickyflag/tags/mmd_spec.rb
377
398
  - spec/stickyflag/tags/pdf_spec.rb
378
399
  - spec/stickyflag/tags/png_spec.rb
@@ -382,6 +403,8 @@ test_files:
382
403
  - spec/support/examples/c_all_comments.c
383
404
  - spec/support/examples/c_no_tags.c
384
405
  - spec/support/examples/c_with_tag.c
406
+ - spec/support/examples/mkv_no_tags.mkv
407
+ - spec/support/examples/mkv_with_tag.mkv
385
408
  - spec/support/examples/mmd_all_meta.mmd
386
409
  - spec/support/examples/mmd_crazy_keys.mmd
387
410
  - spec/support/examples/mmd_crazy_tags.mmd