popcap 0.7.2 → 0.8.0

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +23 -32
  4. data/README.md +27 -24
  5. data/lib/pop_cap/audio_file.rb +13 -11
  6. data/lib/pop_cap/class_support.rb +73 -0
  7. data/lib/pop_cap/{commander.rb → ffmpeg/commander.rb} +8 -2
  8. data/lib/pop_cap/ffmpeg/converter.rb +57 -0
  9. data/lib/pop_cap/ffmpeg/ffmpeg.rb +45 -0
  10. data/lib/pop_cap/ffmpeg/tag_reader.rb +45 -0
  11. data/lib/pop_cap/ffmpeg/tag_writer.rb +44 -0
  12. data/lib/pop_cap/formatter.rb +62 -0
  13. data/lib/pop_cap/formatters/bit_rate.rb +5 -7
  14. data/lib/pop_cap/formatters/date.rb +12 -10
  15. data/lib/pop_cap/formatters/duration.rb +10 -12
  16. data/lib/pop_cap/formatters/filesize.rb +6 -8
  17. data/lib/pop_cap/tag/formatted_tag.rb +31 -0
  18. data/lib/pop_cap/tag/tag_hash.rb +55 -0
  19. data/lib/pop_cap/{tag_struct.rb → tag/tag_struct.rb} +2 -0
  20. data/lib/pop_cap/taggable.rb +50 -30
  21. data/lib/pop_cap/version.rb +1 -1
  22. data/popcap.gemspec +1 -2
  23. data/spec/integration/convert_audio_file_spec.rb +2 -2
  24. data/spec/integration/read_metatags_spec.rb +1 -1
  25. data/spec/integration/update_metatags_spec.rb +1 -1
  26. data/spec/lib/pop_cap/audio_file_spec.rb +3 -9
  27. data/spec/lib/pop_cap/class_support_spec.rb +59 -0
  28. data/spec/lib/pop_cap/{commander_spec.rb → ffmpeg/commander_spec.rb} +1 -1
  29. data/spec/lib/pop_cap/ffmpeg/converter_spec.rb +92 -0
  30. data/spec/lib/pop_cap/ffmpeg/ffmpeg_spec.rb +37 -0
  31. data/spec/lib/pop_cap/ffmpeg/tag_reader_spec.rb +67 -0
  32. data/spec/lib/pop_cap/ffmpeg/tag_writer_spec.rb +60 -0
  33. data/spec/lib/pop_cap/fileable_spec.rb +5 -5
  34. data/spec/lib/pop_cap/formatter_spec.rb +71 -0
  35. data/spec/lib/pop_cap/formatters/bit_rate_spec.rb +8 -0
  36. data/spec/lib/pop_cap/formatters/date_spec.rb +8 -0
  37. data/spec/lib/pop_cap/formatters/duration_spec.rb +8 -0
  38. data/spec/lib/pop_cap/formatters/filesize_spec.rb +8 -0
  39. data/spec/lib/pop_cap/tag/formatted_tag_spec.rb +29 -0
  40. data/spec/lib/pop_cap/tag/tag_hash_spec.rb +35 -0
  41. data/spec/lib/pop_cap/{tag_struct_spec.rb → tag/tag_struct_spec.rb} +5 -1
  42. data/spec/lib/pop_cap/taggable_spec.rb +24 -10
  43. data/spec/spec_helper.rb +0 -3
  44. data/spec/support/popcap_spec_helper.rb +50 -33
  45. metadata +40 -56
  46. data/lib/pop_cap/converter.rb +0 -40
  47. data/lib/pop_cap/ffmpeg.rb +0 -130
  48. data/lib/pop_cap/formatters.rb +0 -33
  49. data/lib/pop_cap/helper.rb +0 -53
  50. data/lib/pop_cap/tag_key.rb +0 -32
  51. data/lib/pop_cap/tag_line.rb +0 -36
  52. data/spec/lib/pop_cap/converter_spec.rb +0 -67
  53. data/spec/lib/pop_cap/ffmpeg_spec.rb +0 -96
  54. data/spec/lib/pop_cap/formatters_spec.rb +0 -36
  55. data/spec/lib/pop_cap/helper_spec.rb +0 -42
  56. data/spec/lib/pop_cap/tag_key_spec.rb +0 -48
  57. data/spec/lib/pop_cap/tag_line_spec.rb +0 -26
  58. /data/spec/{support → fixtures}/sample.flac +0 -0
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/ffmpeg/tag_writer'
3
+
4
+ module PopCap
5
+ describe TagWriter do
6
+ let(:commander) { double('commander') }
7
+ let(:instance) { double('commander_instance') }
8
+ let(:file) { 'path/to/file.flac' }
9
+ let(:tags) { {artist: 'UPDATE'} }
10
+ let(:options) { {artist: 'UPDATE', commander: commander} }
11
+ let(:writer) { TagWriter.new(file, options) }
12
+
13
+ let(:command) do
14
+ %W{ffmpeg -i #{file} -metadata artist=UPDATE /tmp/file.flac}
15
+ end
16
+
17
+ describe '.write' do
18
+ it 'has a class method for #write' do
19
+ TagWriter.should_receive(:new).with(file, options) { instance }
20
+ instance.should_receive(:write)
21
+ TagWriter.write(file, options)
22
+ end
23
+ end
24
+
25
+ describe '#write' do
26
+ it 'updates tags to a temp file' do
27
+ commander.should_receive(:new).with(*command) { instance }
28
+ instance.stub_chain(:execute, :success?) { true }
29
+ FileUtils.stub(:move)
30
+ writer.write
31
+ end
32
+
33
+ it 'moves temp file to original' do
34
+ commander.should_receive(:new).with(*command) { instance }
35
+ instance.stub_chain(:execute, :success?) { true }
36
+ FileUtils.should_receive(:move).with('/tmp/file.flac', file)
37
+ writer.write
38
+ end
39
+
40
+ context 'errors' do
41
+ it 'raises FFmpegError if could not update tags' do
42
+ expect do
43
+ commander.should_receive(:new).with(*command) { instance }
44
+ instance.stub_chain(:execute, :success?) { false }
45
+ writer.write
46
+ end.to raise_error(FFmpegError, "Error writing #{file}.")
47
+ end
48
+
49
+ it 'does not move temp file' do
50
+ expect do
51
+ commander.should_receive(:new).with(*command) { instance }
52
+ instance.stub_chain(:execute, :success?) { false }
53
+ FileUtils.should_not_receive(:move).with('/tmp/file.flac', file)
54
+ writer.write
55
+ end.to raise_error(FFmpegError, "Error writing #{file}.")
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -13,7 +13,7 @@ module PopCap
13
13
  end
14
14
  end
15
15
 
16
- let(:filepath) { 'spec/support/sample.flac' }
16
+ let(:filepath) { PopCapSpecHelper::SAMPLE_FILE }
17
17
  let(:fc) { FileClass.new(filepath) }
18
18
 
19
19
  context '#filename' do
@@ -81,16 +81,16 @@ module PopCap
81
81
 
82
82
  context '#rename' do
83
83
  it 'removes a file with specified name' do
84
- new_name = 'spec/support/example.file'
84
+ new_name = 'spec/fixtures/example.file'
85
85
  FileUtils.should_receive(:mv).with(filepath, new_name)
86
86
  fc.rename('example.file')
87
87
  end
88
88
 
89
89
  it 'updates filepath' do
90
- new_name = 'spec/support/example.file'
90
+ new_name = 'spec/fixtures/example.file'
91
91
  FileUtils.should_receive(:mv).with(filepath, new_name)
92
92
  fc.rename('example.file')
93
- expect(fc.filepath).to eq('spec/support/example.file')
93
+ expect(fc.filepath).to eq('spec/fixtures/example.file')
94
94
  end
95
95
  end
96
96
 
@@ -111,7 +111,7 @@ module PopCap
111
111
 
112
112
  context '#directory' do
113
113
  it 'returns the directory path for the file' do
114
- expect(fc.directory).to eq 'spec/support'
114
+ expect(fc.directory).to eq 'spec/fixtures'
115
115
  end
116
116
  end
117
117
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/formatter'
3
+
4
+ module PopCap
5
+ module Formatters
6
+ describe Formatter do
7
+ class TagClass < Formatter
8
+ def format; 'baz'; end
9
+ end
10
+
11
+ describe '.format' do
12
+ let(:taggy) { Formatters::TagClass.new('foo', {option: 'bar'}) }
13
+
14
+ it 'wraps #format in a class method' do
15
+ klass = Formatters::TagClass.format('foo', {option: 'bar'})
16
+ instance = taggy.format
17
+ expect(klass).to eq instance
18
+ end
19
+ end
20
+
21
+ describe '.subclasses' do
22
+ it 'returns a list of all subclasses in the ObjectSpace' do
23
+ expect(Formatter.subclasses).to include(PopCap::Formatters::TagClass)
24
+ end
25
+
26
+ it 'includes the subclass only once' do
27
+ unique = Formatter.subclasses.select do |cls|
28
+ cls == PopCap::Formatters::TagClass
29
+ end
30
+ expect(unique.size).to eq 1
31
+ end
32
+
33
+ it 'contains no nils' do
34
+ expect(Formatter.subclasses).not_to include(nil)
35
+ end
36
+
37
+ it 'requires all Formatters files' do
38
+ Formatter.subclasses
39
+ formatters_path = '../../../../lib/pop_cap/formatters/*.rb'
40
+ Dir[File.expand_path(formatters_path, __FILE__)].each do |file|
41
+ expect($LOADED_FEATURES).to include(file)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '.subclasses_demodulized' do
47
+ it 'returns of the subclasses with their names demodulized' do
48
+ expect(Formatter.subclasses_demodulized).to include('TagClass')
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#format' do
54
+ let(:taggy) { Formatters::TagClass.new('foo', {option: 'bar'}) }
55
+
56
+ it { expect(taggy).to respond_to(:format) }
57
+ end
58
+
59
+ describe '#new' do
60
+ let(:taggy) { Formatters::TagClass.new('foo', {option: 'bar'}) }
61
+
62
+ it 'has a getter for value' do
63
+ expect(taggy.value).to eq 'foo'
64
+ end
65
+
66
+ it 'has a getter for options' do
67
+ expect(taggy.options).to eq({option: 'bar'})
68
+ end
69
+ end
70
+ end
71
+ end
@@ -4,6 +4,14 @@ require 'spec_helper'
4
4
  module PopCap
5
5
  module Formatters
6
6
  describe BitRate do
7
+ describe '.format' do
8
+ it 'wraps #format in a class method' do
9
+ klass = BitRate.format(128456)
10
+ instance = BitRate.new(128456).format
11
+ expect(klass).to eq(instance)
12
+ end
13
+ end
14
+
7
15
  it 'makes bitrate human readable' do
8
16
  br = BitRate.new(128456)
9
17
  expect(br.format).to eq('128 kb/s')
@@ -4,6 +4,14 @@ require 'spec_helper'
4
4
  module PopCap
5
5
  module Formatters
6
6
  describe Date do
7
+ describe '.format' do
8
+ it 'wraps #format in a class method' do
9
+ klass = Date.format('1975')
10
+ instance = Date.new('1975').format
11
+ expect(klass).to eq(instance)
12
+ end
13
+ end
14
+
7
15
  it 'handles dates like "October 5, 1975"' do
8
16
  dm = Date.new('October 5, 1975')
9
17
  expect(dm.format).to eq 1975
@@ -4,6 +4,14 @@ require 'spec_helper'
4
4
  module PopCap
5
5
  module Formatters
6
6
  describe Duration do
7
+ describe '.format' do
8
+ it 'wraps #format in a class method' do
9
+ klass = Duration.format('128456')
10
+ instance = Duration.new('128456').format
11
+ expect(klass).to eq(instance)
12
+ end
13
+ end
14
+
7
15
  it 'formats duration as HH:MM:SS' do
8
16
  dur = Duration.new('40003')
9
17
  expect(dur.format).to eq('11:06:43')
@@ -4,6 +4,14 @@ require 'pop_cap/formatters/filesize'
4
4
  module PopCap
5
5
  module Formatters
6
6
  describe Filesize do
7
+ describe '.format' do
8
+ it 'wraps #format in a class method' do
9
+ klass = Filesize.format('1975')
10
+ instance = Filesize.new('1975').format
11
+ expect(klass).to eq(instance)
12
+ end
13
+ end
14
+
7
15
  it 'uses binary conversion' do
8
16
  fs = Filesize.new('1024')
9
17
  expect(fs.format).to eq '1K'
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'pop_cap/tag/formatted_tag'
3
+
4
+ module PopCap
5
+ describe FormattedTag do
6
+ class FakeFormatter
7
+ def self.format(value)
8
+ value.to_i
9
+ end
10
+ end
11
+
12
+ describe '#format' do
13
+ it 'formats a value with the formatter' do
14
+ tag = FormattedTag.new(FakeFormatter, '123')
15
+ expect(tag.format).to eq 123
16
+ end
17
+ end
18
+
19
+ describe '.format' do
20
+ it 'wraps #new & #format' do
21
+ instance = double('FormattedTagInstance')
22
+ FormattedTag.should_receive(:new).with(FakeFormatter, '123').
23
+ and_return(instance)
24
+ instance.should_receive(:format)
25
+ FormattedTag.format(FakeFormatter, '123')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'support/popcap_spec_helper'
3
+ require 'pop_cap/tag/tag_hash'
4
+
5
+ module PopCap
6
+ describe TagHash do
7
+ describe '#hash' do
8
+ let(:tag_hash) { TagHash.new(PopCapSpecHelper.raw_tags) }
9
+
10
+ it 'symbolizes all keys' do
11
+ tag_hash.hash.each_key do |key|
12
+ expect(key).to be_a Symbol
13
+ end
14
+ end
15
+
16
+ it 'renames filesize to size' do
17
+ expect(tag_hash.hash[:size]).to be_nil
18
+ expect(tag_hash.hash[:filesize]).not_to be_nil
19
+ end
20
+
21
+ it 'formats json output as a hash' do
22
+ expect(tag_hash.hash).to eq(PopCapSpecHelper.unformatted_hash)
23
+ end
24
+ end
25
+
26
+ describe '.hash' do
27
+ it 'wraps #new & #hash' do
28
+ instance = double('TagHash')
29
+ TagHash.should_receive(:new).with('foo') { instance }
30
+ instance.should_receive(:hash)
31
+ TagHash.hash('foo')
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,11 +1,15 @@
1
1
  require 'spec_helper'
2
- require 'pop_cap/tag_struct'
2
+ require 'pop_cap/tag/tag_struct'
3
3
 
4
4
  module PopCap
5
5
  describe TagStruct do
6
6
  let(:hash) { {artist: 'Artist', date: 1984} }
7
7
  let(:ts) { TagStruct.new(hash) }
8
8
 
9
+ it 'includes Enumerable' do
10
+ expect(TagStruct.included_modules).to include(Enumerable)
11
+ end
12
+
9
13
  describe '#new' do
10
14
  it 'raises error if not a hash' do
11
15
  expect do
@@ -17,20 +17,33 @@ module PopCap
17
17
  expect(sc).to respond_to(:raw_tags)
18
18
  end
19
19
 
20
- context '#to_hash' do
21
- it 'builds a sanitized hash from FFmpeg raw_tags' do
22
- expect(sc.to_hash).to eq PopCapSpecHelper.to_hash
20
+ describe '#unformatted' do
21
+ it 'returns TagHash#hash' do
22
+ TagHash.should_receive(:hash).with(sc.raw_tags)
23
+ sc.unformatted
23
24
  end
24
25
 
25
26
  it 'is memoized' do
26
- sc.to_hash
27
- expect(sc.instance_variable_get('@hash')).
28
- to eq PopCapSpecHelper.to_hash
27
+ sc.unformatted
28
+ TagHash.should_not_receive(:new)
29
+ sc.unformatted
29
30
  end
30
31
  end
31
32
 
32
- context '#tags' do
33
- it 'builds a tag structure of from a hash' do
33
+ describe '#formatted' do
34
+ it 'builds a formatted hash' do
35
+ expect(sc.formatted).to include(PopCapSpecHelper.formatted_hash)
36
+ end
37
+
38
+ it 'is memoized' do
39
+ sc.formatted
40
+ FormattedTag.should_not_receive(:new)
41
+ sc.formatted
42
+ end
43
+ end
44
+
45
+ describe '#tags' do
46
+ it 'builds a tag structure from #formatted_hash' do
34
47
  expect(sc.tags.album).to eq PopCapSpecHelper.tags.album
35
48
  expect(sc.tags.artist).to eq PopCapSpecHelper.tags.artist
36
49
  expect(sc.tags.bit_rate).to eq PopCapSpecHelper.tags.bit_rate
@@ -46,13 +59,14 @@ module PopCap
46
59
 
47
60
  it 'is memoized' do
48
61
  sc.tags
49
- expect(sc.instance_variable_get('@tags')).to be_true
62
+ TagStruct.should_not_receive(:new)
63
+ sc.tags
50
64
  end
51
65
  end
52
66
 
53
67
  context '#reload!' do
54
68
  it 'sets instance variables to nil' do
55
- %w(@lined @hash @tags).each do |instance|
69
+ %w(@formatted_hash @unformatted_hash @tag_struct).each do |instance|
56
70
  sc.reload!
57
71
  expect(sc.instance_variable_get(instance)).to be_nil
58
72
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,4 @@
1
1
  require 'reek/spec'
2
- require 'simplecov'
3
-
4
- SimpleCov.start
5
2
 
6
3
  RSpec.configure do
7
4
  include Reek::Spec
@@ -1,36 +1,43 @@
1
1
  require 'fileutils'
2
2
  require 'ostruct'
3
+ require 'json'
3
4
 
4
5
  module PopCapSpecHelper
6
+ SAMPLE_FILE = 'spec/fixtures/sample.flac'
7
+
5
8
  class << self
6
9
  def raw_tags
7
- <<-EOF.gsub(/^\s+/,'')
8
- [FORMAT]
9
- filename=#{File.realpath('spec/support/sample.flac')}
10
- nb_streams=1
11
- format_name=flac
12
- format_long_name=raw FLAC
13
- start_time=N/A
14
- duration=1.000000
15
- size=18291
16
- bit_rate=146328
17
- TAG:GENRE=Sample Genre
18
- TAG:track=01
19
- TAG:ALBUM=Sample Album
20
- TAG:DATE=2012
21
- TAG:TITLE=Sample Title
22
- TAG:ARTIST=Sample Artist
23
- [/FORMAT]
24
- EOF
10
+ '{"format":{"filename":"/home/marsalis/.apps/popcap/spec/fixtures/'\
11
+ 'sample.flac","nb_streams":1,"format_name":"flac","format_long_name"'\
12
+ ':"raw FLAC","duration":"1.000000","size":"18291","bit_rate":"146328"'\
13
+ ',"tags":{"GENRE":"Sample Genre","track":"01","ALBUM":"Sample Album",'\
14
+ '"DATE":"2012","TITLE":"Sample Title","ARTIST":"Sample Artist"}}}'
25
15
  end
26
16
 
27
- def to_hash
17
+ def unformatted_hash
28
18
  {
29
- filename: File.realpath('spec/support/sample.flac'),
30
- nb_streams: '1',
19
+ filename: File.realpath(SAMPLE_FILE),
20
+ nb_streams: 1,
21
+ format_name: 'flac',
22
+ format_long_name: 'raw FLAC',
23
+ duration: '1.000000',
24
+ filesize: '18291',
25
+ bit_rate: '146328',
26
+ genre: 'Sample Genre',
27
+ track: '01',
28
+ album: 'Sample Album',
29
+ date: '2012',
30
+ title: 'Sample Title',
31
+ artist: 'Sample Artist'
32
+ }
33
+ end
34
+
35
+ def formatted_hash
36
+ {
37
+ filename: File.realpath(SAMPLE_FILE),
38
+ nb_streams: 1,
31
39
  format_name: 'flac',
32
40
  format_long_name: 'raw FLAC',
33
- start_time: 'N/A',
34
41
  duration: '1',
35
42
  filesize: '17.9K',
36
43
  bit_rate: '146 kb/s',
@@ -46,7 +53,7 @@ module PopCapSpecHelper
46
53
  def tags
47
54
  OpenStruct.new(
48
55
  {
49
- filename: File.realpath('spec/support/sample.flac'),
56
+ filename: File.realpath(SAMPLE_FILE),
50
57
  nb_streams: '1',
51
58
  format_name: 'flac',
52
59
  format_long_name: 'raw FLAC',
@@ -63,24 +70,34 @@ module PopCapSpecHelper
63
70
  end
64
71
 
65
72
  def remove_converted
66
- FileUtils.rm_f('spec/support/sample.mp3')
73
+ FileUtils.rm_f('spec/fixtures/sample.mp3')
67
74
  end
68
75
 
69
76
  def setup
70
- FileUtils.cp('spec/support/sample.flac', 'spec/support/backup.flac')
77
+ FileUtils.cp(SAMPLE_FILE, 'spec/fixtures/backup.flac')
71
78
  end
72
79
 
73
80
  def teardown
74
- FileUtils.mv('spec/support/backup.flac', 'spec/support/sample.flac')
75
- FileUtils.rm_f('spec/support/sample.mp3')
81
+ FileUtils.mv('spec/fixtures/backup.flac', SAMPLE_FILE)
82
+ FileUtils.rm_f('spec/fixtures/sample.mp3')
83
+ end
84
+
85
+ def object_names(const)
86
+ ObjectSpace.each_object(const).map { |cls| cls.name }
87
+ end
88
+
89
+ def all_names
90
+ ObjectSpace.each_object(Object).map do |obj|
91
+ obj.name if(obj.is_a?(Class) || obj.is_a?(Module))
92
+ end.compact
93
+ end
94
+
95
+ def classes
96
+ ObjectSpace.each_object(Class).map { |cls| cls }
76
97
  end
77
98
 
78
- def benchmark(&block)
79
- start = Time.now
80
- raise(ArgumentError, 'Provide a block.') unless block_given?
81
- yield
82
- finish = Time.now
83
- puts "Time elapsed: #{((finish - stop)*1000).round(3)}ms"
99
+ def modules
100
+ ObjectSpace.each_object(Module).map { |cls| cls }
84
101
  end
85
102
  end
86
103
  end