popcap 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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