kagu 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8cfd404d70114c5ab0432216ae8d4b1e978357c
4
+ data.tar.gz: c07c6fa355ab50c7bd1033de65cd4495b5fb11db
5
+ SHA512:
6
+ metadata.gz: 23c65772592525da40e5ebc004f6a98f812bc91da10d88a1301de5b925bbee511e88491615a20555e39a0538390a545c06ca2b34dc71242d7dd5757c10e683f5
7
+ data.tar.gz: d0c149fb4d705457b6b4a4760c0b9519b18ad77b8743f6597a389633dd00b5f2c3eaba33ec5279d94efc73e25143e7eaa0be3fe1de5341229575674a18b4bc2a
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ .bundle
3
+ .ruby-version
4
+ Gemfile.lock
5
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --order random
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Alexis Toulotte
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,28 @@
1
+ # Kagu
2
+
3
+ Ruby API to manage iTunes tracks and playlists.
4
+
5
+ ## Installation
6
+
7
+ Just add this into your `Gemfile`:
8
+
9
+ gem 'kagu'
10
+
11
+ Then, just run `bundle install`.
12
+
13
+ ## Example
14
+
15
+ library = Kagu::Library.new
16
+
17
+ library.tracks.each do |track|
18
+ puts track.artist
19
+ end
20
+
21
+ library.playlists.each do |playlist|
22
+ puts "#{playlist.name}: #{playlist.tracks.count}"
23
+ end
24
+
25
+ ## Executing test suite
26
+
27
+ This project is fully tested with [Rspec 3](http://github.com/rspec/rspec).
28
+ Just run `bundle exec rake` (after a `bundle install`).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Default: runs specs.'
7
+ task default: :spec
8
+
9
+ desc 'Run all specs in spec directory.'
10
+ RSpec::Core::RakeTask.new(:spec)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/kagu.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'kagu'
3
+ s.version = File.read("#{File.dirname(__FILE__)}/VERSION").strip
4
+ s.platform = Gem::Platform::RUBY
5
+ s.author = 'Alexis Toulotte'
6
+ s.email = 'al@alweb.org'
7
+ s.homepage = 'https://github.com/alexistoulotte/kagu'
8
+ s.summary = 'API for iTunes'
9
+ s.description = 'API to manage iTunes tracks and playlists'
10
+ s.license = 'MIT'
11
+
12
+ s.rubyforge_project = 'kagu'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ s.add_dependency 'activesupport', '>= 4.1.0', '< 4.2.0'
20
+ s.add_dependency 'htmlentities', '>= 4.3.0', '< 4.4.0'
21
+
22
+ s.add_development_dependency 'byebug', '>= 3.2.0', '< 3.3.0'
23
+ s.add_development_dependency 'rake', '>= 10.3.0', '< 10.4.0'
24
+ s.add_development_dependency 'rspec', '>= 3.0.0', '< 3.1.0'
25
+ end
@@ -0,0 +1,12 @@
1
+ module AttributesInitializer
2
+
3
+ def initialize(attributes = {})
4
+ attributes.each do |name, value|
5
+ send("#{name}=", value) if respond_to?("#{name}=", true)
6
+ end
7
+ self.class.const_get(:MANDATORY_ATTRIBUTES).each do |attribute|
8
+ raise("#{self.class}##{attribute} is mandatory") if send(attribute).nil?
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,30 @@
1
+ module Kagu
2
+
3
+ class Library
4
+
5
+ PATH = "#{ENV['HOME']}/Music/iTunes/iTunes Music Library.xml"
6
+
7
+ attr_reader :path
8
+
9
+ def initialize(path = PATH)
10
+ self.path = path
11
+ end
12
+
13
+ def playlists
14
+ Playlists.new(self)
15
+ end
16
+
17
+ def tracks
18
+ Tracks.new(self)
19
+ end
20
+
21
+ private
22
+
23
+ def path=(path)
24
+ raise IOError.new("No such file: #{path.inspect}") unless File.file?(path)
25
+ @path = path
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,35 @@
1
+ module Kagu
2
+
3
+ class Playlist
4
+
5
+ MANDATORY_ATTRIBUTES = %w(name)
6
+
7
+ include AttributesInitializer
8
+ include Enumerable
9
+
10
+ attr_reader :name, :tracks
11
+
12
+ delegate :each, to: :tracks
13
+
14
+ def to_s
15
+ name
16
+ end
17
+
18
+ private
19
+
20
+ def itunes_name=(value)
21
+ @@html_entities ||= HTMLEntities.new
22
+ self.name = @@html_entities.decode(value)
23
+ end
24
+
25
+ def name=(value)
26
+ @name = value.to_s.squish.presence
27
+ end
28
+
29
+ def tracks=(values)
30
+ @tracks = [values].flatten.select { |value| value.is_a?(Track) }
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,49 @@
1
+ module Kagu
2
+
3
+ class Playlists
4
+
5
+ include Enumerable
6
+
7
+ attr_reader :library
8
+
9
+ def initialize(library)
10
+ @library = library.is_a?(Library) ? library : raise("#{self.class}#library must be a library, #{library.inspect} given")
11
+ end
12
+
13
+ def each(&block)
14
+ return unless block_given?
15
+ tracks = {}.tap do |tracks|
16
+ library.tracks.each { |track| tracks[track.id] = track }
17
+ end
18
+ File.open(library.path, 'r') do |file|
19
+ begin
20
+ line = file.readline.strip
21
+ end while !line.starts_with?('<key>Playlists</key>')
22
+ playlist_name = nil
23
+ playlist_tracks = []
24
+ while !file.eof? && (line = file.readline.strip)
25
+ if line == '<key>Master</key><true/>'
26
+ playlist_name = nil
27
+ next
28
+ elsif line == '</array>'
29
+ yield(Playlist.new(itunes_name: playlist_name, tracks: playlist_tracks)) if playlist_name.present?
30
+ playlist_name = nil
31
+ playlist_tracks = []
32
+ next
33
+ end
34
+ match = line.match(/<key>(.+)<\/key><(\w+)>(.*)<\/\2>/)
35
+ next unless match
36
+ name = match[1]
37
+ value = match[3]
38
+ if name == 'Name'
39
+ playlist_name = value
40
+ elsif name == 'Track ID'
41
+ playlist_tracks << tracks[value.to_i]
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
data/lib/kagu/track.rb ADDED
@@ -0,0 +1,111 @@
1
+ module Kagu
2
+
3
+ class Track
4
+
5
+ include AttributesInitializer
6
+ include Comparable
7
+
8
+ MANDATORY_ATTRIBUTES = %w(added_at id length path)
9
+
10
+ attr_reader :added_at, :album, :artist, :genre, :id, :length, :path, :title
11
+
12
+ def initialize(attributes = {})
13
+ super
14
+ raise("No such file: #{path.inspect}") unless File.file?(path)
15
+ end
16
+
17
+ def <=>(other)
18
+ return nil unless other.is_a?(self.class)
19
+ added_at <=> other.added_at
20
+ end
21
+
22
+ def ==(other)
23
+ other.is_a?(self.class) && artist == other.artist && title == other.title && (length.to_i - other.length.to_i).abs < 3
24
+ end
25
+
26
+ def eql?(other)
27
+ super || self == other
28
+ end
29
+
30
+ def relative_path(directory)
31
+ directory.present? && directory.starts_with?(directory) ? path.gsub(/\A#{Regexp.escape(directory)}\//, '') : path
32
+ end
33
+
34
+ def to_s
35
+ "#{artist} - #{title}"
36
+ end
37
+
38
+ private
39
+
40
+ def added_at=(value)
41
+ @added_at = value.is_a?(Time) ? value.utc : nil
42
+ end
43
+
44
+ def album=(value)
45
+ @album = value.to_s.squish.presence
46
+ end
47
+
48
+ def artist=(value)
49
+ @artist = value.to_s.squish.presence
50
+ end
51
+
52
+ def genre=(value)
53
+ @genre = value.to_s.squish.presence
54
+ end
55
+
56
+ def html_entities_decode(value)
57
+ @@html_entities ||= HTMLEntities.new
58
+ @@html_entities.decode(value.to_s)
59
+ end
60
+
61
+ def id=(value)
62
+ @id = value.to_s =~ /\A[0-9]+\z/ ? value.to_i : nil
63
+ end
64
+
65
+ def itunes_album=(value)
66
+ self.album = html_entities_decode(value)
67
+ end
68
+
69
+ def itunes_artist=(value)
70
+ self.artist = html_entities_decode(value)
71
+ end
72
+
73
+ def itunes_date_added=(value)
74
+ self.added_at = value.present? ? Time.parse(value.to_s) : nil
75
+ end
76
+
77
+ def itunes_genre=(value)
78
+ self.genre = html_entities_decode(value)
79
+ end
80
+
81
+ def itunes_location=(value)
82
+ self.path = CGI.unescape(html_entities_decode(value).gsub('+', '%2B')).gsub(/\Afile:\/\/localhost/, '')
83
+ end
84
+
85
+ def itunes_name=(value)
86
+ self.title = html_entities_decode(value)
87
+ end
88
+
89
+ def itunes_total_time=(value)
90
+ self.length = value.to_s =~ /\A[0-9]+\z/ ? (value.to_i / 1000.0).round : nil
91
+ end
92
+
93
+ def itunes_track_id=(value)
94
+ self.id = value
95
+ end
96
+
97
+ def length=(value)
98
+ @length = value.to_s =~ /\A[0-9]+\z/ ? value.to_i : nil
99
+ end
100
+
101
+ def path=(value)
102
+ @path = value.to_s.presence
103
+ end
104
+
105
+ def title=(value)
106
+ @title = value.to_s.squish.presence
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,32 @@
1
+ module Kagu
2
+
3
+ class Tracks
4
+
5
+ include Enumerable
6
+
7
+ attr_reader :library
8
+
9
+ def initialize(library)
10
+ @library = library.is_a?(Library) ? library : raise("#{self.class}#library must be a library, #{library.inspect} given")
11
+ end
12
+
13
+ def each(&block)
14
+ return unless block_given?
15
+ File.open(library.path, 'r') do |file|
16
+ while !file.eof? && (line = file.readline.strip)
17
+ next unless line.starts_with?('<key>Track ID</key>')
18
+ attributes = {}
19
+ begin
20
+ match = line.match(/<key>(.+)<\/key><(\w+)>(.*)<\/\2>/)
21
+ name = "itunes_#{match[1].downcase.gsub(' ', '_')}"
22
+ value = match[3]
23
+ attributes[name] = value
24
+ end while (line = file.readline.strip) != '</dict>'
25
+ yield(Track.new(attributes)) if attributes['itunes_track_type'] == 'File' && attributes['itunes_podcast'].blank?
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
data/lib/kagu.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'active_support/core_ext'
2
+ require 'byebug' if ENV['DEBUGGER']
3
+ require 'fileutils'
4
+ require 'htmlentities'
5
+
6
+ lib_path = "#{__dir__}/kagu"
7
+
8
+ require "#{lib_path}/attributes_initializer"
9
+ require "#{lib_path}/library"
10
+ require "#{lib_path}/playlist"
11
+ require "#{lib_path}/playlists"
12
+ require "#{lib_path}/track"
13
+ require "#{lib_path}/tracks"
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kagu::Library do
4
+
5
+ let(:library) { Kagu::Library.new }
6
+
7
+ describe '#initialize' do
8
+
9
+ it 'is correct by default' do
10
+ expect(library.path).to eq("#{ENV['HOME']}/Music/iTunes/iTunes Music Library.xml")
11
+ end
12
+
13
+ it 'raise an error if directory' do
14
+ expect {
15
+ Kagu::Library.new('/tmp')
16
+ }.to raise_error(IOError, 'No such file: "/tmp"')
17
+ end
18
+
19
+ it "raise an error if file can't be found" do
20
+ expect {
21
+ Kagu::Library.new('/tmp/bar.foo.baz')
22
+ }.to raise_error(IOError, 'No such file: "/tmp/bar.foo.baz"')
23
+ end
24
+
25
+ end
26
+
27
+ describe '#playlists' do
28
+
29
+ it 'returns a Playlists object' do
30
+ expect(library.playlists).to be_a(Kagu::Playlists)
31
+ expect(library.playlists.library).to be(library)
32
+ end
33
+
34
+ end
35
+
36
+ describe '#tracks' do
37
+
38
+ it 'returns a Tracks object' do
39
+ expect(library.tracks).to be_a(Kagu::Tracks)
40
+ expect(library.tracks.library).to be(library)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kagu::Playlist do
4
+
5
+ let(:library) { Kagu::Library.new }
6
+ let(:playlist) { Kagu::Playlist.new(name: 'Best tracks', tracks: tracks) }
7
+ let(:tracks) { library.tracks.take(15) }
8
+
9
+ describe '#each' do
10
+
11
+ it 'is delegated to tracks' do
12
+ track = nil
13
+ playlist.each do |t|
14
+ track = t
15
+ break
16
+ end
17
+ expect(track).to be_a(Kagu::Track)
18
+ end
19
+
20
+ end
21
+
22
+ describe '#itunes_name=' do
23
+
24
+ it 'set name and convert entities' do
25
+ expect {
26
+ playlist.send(:itunes_name=, 'Hello &amp; World')
27
+ }.to change { playlist.name }.from('Best tracks').to('Hello & World')
28
+ end
29
+
30
+ end
31
+
32
+ describe '#name' do
33
+
34
+ it 'is set at initialization' do
35
+ expect(Kagu::Playlist.new(name: 'Best tracks').name).to eq('Best tracks')
36
+ end
37
+
38
+ it 'is squished' do
39
+ expect(Kagu::Playlist.new(name: "Best \t tracks\n").name).to eq('Best tracks')
40
+ end
41
+
42
+ it 'is mandatory' do
43
+ expect {
44
+ Kagu::Playlist.new(name: ' ')
45
+ }.to raise_error('Kagu::Playlist#name is mandatory')
46
+ end
47
+
48
+ end
49
+
50
+ describe '#to_s' do
51
+
52
+ it 'is name' do
53
+ expect(Kagu::Playlist.new(name: 'Best tracks').to_s).to eq('Best tracks')
54
+ end
55
+
56
+ end
57
+
58
+ describe '#tracks' do
59
+
60
+ it 'is tracks given at initialization' do
61
+ expect(Kagu::Playlist.new(name: 'Test', tracks: tracks).tracks).to eq(tracks)
62
+ end
63
+
64
+ it 'removes invalid tracks' do
65
+ expect(Kagu::Playlist.new(name: 'Test', tracks: ['bar', [tracks.first], 'foo']).tracks).to eq([tracks.first])
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kagu::Playlists do
4
+
5
+ let(:library) { Kagu::Library.new }
6
+ let(:playlists) { Kagu::Playlists.new(library) }
7
+
8
+ describe '#each' do
9
+
10
+ it 'playlists must contains at least 1 song and a name' do
11
+ expect(playlists.count).to be > 5
12
+ playlists.each do |playlist|
13
+ expect(playlist.name).not_to eq('Bibliothèque')
14
+ expect(playlist.name).not_to match(/\&#\d+;/)
15
+ expect(playlist.name).to be_a(String)
16
+ expect(playlist.name).to be_present
17
+ expect(playlist.tracks.size).to be > 0
18
+ end
19
+ end
20
+
21
+ it 'does not fails if block is not given' do
22
+ expect {
23
+ expect(playlists.each).to be_nil
24
+ }.not_to raise_error
25
+ end
26
+
27
+ it 'returns nil' do
28
+ expect(playlists.each { break }).to be_nil
29
+ end
30
+
31
+ end
32
+
33
+ describe '#initialize' do
34
+
35
+ it 'raise an error if library is nil' do
36
+ expect {
37
+ Kagu::Playlists.new(nil)
38
+ }.to raise_error('Kagu::Playlists#library must be a library, nil given')
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,231 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kagu::Track do
4
+
5
+ let(:attributes) { { added_at: track.added_at, album: track.album, artist: track.artist, id: track.id, length: track.length, path: track.path, title: track.title } }
6
+ let(:library) { Kagu::Library.new }
7
+ let(:track) { library.tracks.first }
8
+
9
+ describe '#==' do
10
+
11
+ let(:other) { track.dup }
12
+
13
+ it 'is false if not a track' do
14
+ expect(track == 'foo').to be(false)
15
+ end
16
+
17
+ it 'is false if artist differs' do
18
+ expect {
19
+ allow(other).to receive(:artist).and_return('Foo')
20
+ }.to change { track == other }.from(true).to(false)
21
+ end
22
+
23
+ it 'is false if title differs' do
24
+ expect {
25
+ allow(other).to receive(:title).and_return('Bar')
26
+ }.to change { track == other }.from(true).to(false)
27
+ end
28
+
29
+ it 'is false if length differs' do
30
+ expect {
31
+ allow(other).to receive(:length).and_return(track.length - 5)
32
+ }.to change { track == other }.from(true).to(false)
33
+ end
34
+
35
+ it 'is true if length differs just a little' do
36
+ expect {
37
+ allow(other).to receive(:length).and_return(track.length - 2)
38
+ }.not_to change { track == other }
39
+ end
40
+
41
+ it 'is true if album differs' do
42
+ expect {
43
+ allow(other).to receive(:album).and_return('Baz')
44
+ }.not_to change { track == other }
45
+ end
46
+
47
+ end
48
+
49
+ describe '#<=>' do
50
+
51
+ let(:other) { track.dup }
52
+
53
+ it 'compares tracks lengths' do
54
+ expect {
55
+ allow(other).to receive(:added_at).and_return(1.day.from_now)
56
+ }.to change { track <=> other }.from(0).to(-1)
57
+ end
58
+
59
+ it 'is nil if added_at is nil' do
60
+ allow(track).to receive(:added_at).and_return(nil)
61
+ expect(track <=> other).to be_nil
62
+ end
63
+
64
+ it 'is nil if other track added_at is nil' do
65
+ allow(other).to receive(:added_at).and_return(nil)
66
+ expect(track <=> other).to be_nil
67
+ end
68
+
69
+ it 'is nil if other is not a track' do
70
+ expect(track <=> '').to be_nil
71
+ end
72
+
73
+ end
74
+
75
+ describe '#added_at' do
76
+
77
+ it 'returns a time' do
78
+ expect(track.added_at).to be_a(Time)
79
+ end
80
+
81
+ it 'return a time in utc' do
82
+ expect(track.added_at.zone).to eq('UTC')
83
+ end
84
+
85
+ it 'raise an error if not specified' do
86
+ expect {
87
+ Kagu::Track.new(attributes.except(:added_at))
88
+ }.to raise_error('Kagu::Track#added_at is mandatory')
89
+ end
90
+
91
+ end
92
+
93
+ describe '#album' do
94
+
95
+ it 'is is squished' do
96
+ track.send(:album=, " Life Is \r Peachy \n")
97
+ expect(track.album).to eq('Life Is Peachy')
98
+ end
99
+
100
+ end
101
+
102
+ describe '#artist' do
103
+
104
+ it 'is is squished' do
105
+ track.send(:artist=, " Benny \r Page \n")
106
+ expect(track.artist).to eq('Benny Page')
107
+ end
108
+
109
+ end
110
+
111
+ describe '#eql?' do
112
+
113
+ it 'is true for same object' do
114
+ expect(track.eql?(track)).to be(true)
115
+ end
116
+
117
+ it 'is true if == returns true' do
118
+ other = track.dup
119
+ expect {
120
+ allow(track).to receive(:==).and_return(false)
121
+ }.to change { track.eql?(other) }.from(true).to(false)
122
+ end
123
+
124
+ end
125
+
126
+ describe '#genre' do
127
+
128
+ it 'is correct' do
129
+ expect(track.genre).to be_a(String)
130
+ expect(track.genre).to be_present
131
+ end
132
+
133
+ end
134
+
135
+ describe '#id' do
136
+
137
+ it 'is correct' do
138
+ expect(track.id).to be_an(Integer)
139
+ expect(track.id).to be > 0
140
+ end
141
+
142
+ it 'raise an error if not specified' do
143
+ expect {
144
+ Kagu::Track.new(attributes.except(:id))
145
+ }.to raise_error('Kagu::Track#id is mandatory')
146
+ end
147
+
148
+ end
149
+
150
+ describe '#itunes_name=' do
151
+
152
+ it 'sets title with entities decoded' do
153
+ expect {
154
+ track.send(:itunes_name=, 'Racing &amp; Green')
155
+ }.to change { track.title }.to('Racing & Green')
156
+ end
157
+
158
+ end
159
+
160
+ describe '#length' do
161
+
162
+ it 'is correct' do
163
+ expect(track.length).to be_an(Integer)
164
+ expect(track.length).to be > 0
165
+ end
166
+
167
+ it 'raise an error if not specified' do
168
+ expect {
169
+ Kagu::Track.new(attributes.except(:length))
170
+ }.to raise_error('Kagu::Track#length is mandatory')
171
+ end
172
+
173
+ end
174
+
175
+ describe '#path' do
176
+
177
+ it 'is correct' do
178
+ expect(track.path).to be_a(String)
179
+ expect(File.file?(track.path)).to be(true)
180
+ end
181
+
182
+ it 'raise an error if not specified' do
183
+ expect {
184
+ Kagu::Track.new(attributes.except(:path))
185
+ }.to raise_error('Kagu::Track#path is mandatory')
186
+ end
187
+
188
+ it 'raise an error if not found' do
189
+ expect {
190
+ Kagu::Track.new(attributes.merge(path: '/tmp/bar.mp3'))
191
+ }.to raise_error('No such file: "/tmp/bar.mp3"')
192
+ end
193
+
194
+ it 'raise an error if not a file' do
195
+ expect {
196
+ Kagu::Track.new(attributes.merge(path: '/tmp'))
197
+ }.to raise_error('No such file: "/tmp"')
198
+ end
199
+
200
+ end
201
+
202
+ describe '#relative_path' do
203
+
204
+ it 'is correct' do
205
+ expect(track.relative_path(ENV['HOME'])).to eq(track.path.gsub("#{ENV['HOME']}/", ''))
206
+ end
207
+
208
+ it 'is full path if not starting with given path' do
209
+ expect(track.relative_path('/Users/john')).to eq(track.path)
210
+ end
211
+
212
+ end
213
+
214
+ describe '#title' do
215
+
216
+ it 'is is squished' do
217
+ track.send(:title=, " Racing \r Green \n")
218
+ expect(track.title).to eq('Racing Green')
219
+ end
220
+
221
+ end
222
+
223
+ describe '#to_s' do
224
+
225
+ it 'is "artist - title"' do
226
+ expect(track.to_s).to eq("#{track.artist} - #{track.title}")
227
+ end
228
+
229
+ end
230
+
231
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kagu::Tracks do
4
+
5
+ let(:library) { Kagu::Library.new }
6
+ let(:tracks) { Kagu::Tracks.new(library) }
7
+
8
+ describe '#each' do
9
+
10
+ it 'most of tracks must be correct path must be a file' do
11
+ tracks.take(100).each do |track|
12
+ expect(File.file?(track.path)).to be(true)
13
+ expect(track).to be_a(Kagu::Track)
14
+ expect(track.added_at).to be_a(Time)
15
+ expect(track.album).to be_a(String)
16
+ expect(track.album).to be_present
17
+ expect(track.artist).to be_a(String)
18
+ expect(track.artist).to be_present
19
+ expect(track.genre).to be_a(String)
20
+ expect(track.genre).to be_present
21
+ expect(track.id).to be_an(Integer)
22
+ expect(track.length).to be_an(Integer)
23
+ expect(track.path).not_to include('file://')
24
+ expect(track.path).to include('Music')
25
+ expect(track.title).to be_a(String)
26
+ expect(track.title).to be_present
27
+ end
28
+ end
29
+
30
+ it 'does not fails if block is not given' do
31
+ expect {
32
+ expect(tracks.each).to be_nil
33
+ }.not_to raise_error
34
+ end
35
+
36
+ it 'returns nil' do
37
+ expect(tracks.each { break }).to be_nil
38
+ end
39
+
40
+ end
41
+
42
+ describe '#initialize' do
43
+
44
+ it 'raise an error if library is nil' do
45
+ expect {
46
+ Kagu::Tracks.new(nil)
47
+ }.to raise_error('Kagu::Tracks#library must be a library, nil given')
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,6 @@
1
+ require File.expand_path("#{__dir__}/../lib/kagu")
2
+ require 'byebug'
3
+
4
+ RSpec.configure do |config|
5
+ config.raise_errors_for_deprecations!
6
+ end
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kagu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexis Toulotte
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 4.1.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 4.2.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: htmlentities
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 4.3.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: 4.4.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 4.3.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: 4.4.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: byebug
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 3.2.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: 3.3.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.2.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: 3.3.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: rake
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 10.3.0
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: 10.4.0
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 10.3.0
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: 10.4.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 3.0.0
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: 3.1.0
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 3.0.0
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: 3.1.0
113
+ description: API to manage iTunes tracks and playlists
114
+ email: al@alweb.org
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - ".rspec"
121
+ - Gemfile
122
+ - MIT-LICENSE
123
+ - README.mdown
124
+ - Rakefile
125
+ - VERSION
126
+ - kagu.gemspec
127
+ - lib/kagu.rb
128
+ - lib/kagu/attributes_initializer.rb
129
+ - lib/kagu/library.rb
130
+ - lib/kagu/playlist.rb
131
+ - lib/kagu/playlists.rb
132
+ - lib/kagu/track.rb
133
+ - lib/kagu/tracks.rb
134
+ - spec/kagu/library_spec.rb
135
+ - spec/kagu/playlist_spec.rb
136
+ - spec/kagu/playlists_spec.rb
137
+ - spec/kagu/track_spec.rb
138
+ - spec/kagu/tracks_spec.rb
139
+ - spec/spec_helper.rb
140
+ homepage: https://github.com/alexistoulotte/kagu
141
+ licenses:
142
+ - MIT
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project: kagu
160
+ rubygems_version: 2.2.2
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: API for iTunes
164
+ test_files:
165
+ - spec/kagu/library_spec.rb
166
+ - spec/kagu/playlist_spec.rb
167
+ - spec/kagu/playlists_spec.rb
168
+ - spec/kagu/track_spec.rb
169
+ - spec/kagu/tracks_spec.rb
170
+ - spec/spec_helper.rb