itunes 0.3.0 → 0.4.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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ Gemfile.lock
3
+ coverage/*
4
+ doc/*
5
+ .yardoc
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format=nested
3
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md CHANGED
@@ -1,43 +1,48 @@
1
- # iTunes
1
+ iTunes
2
+ ======
3
+
2
4
  A Ruby wrapper around the iTunes API that lets you search for any sort of data that is available on the iTunes store.
3
5
 
4
- ## Example Response
6
+ Example Response
7
+ ----------------
5
8
 
6
9
  {
7
- "artistId" : 954266,
8
- "artistName" : "Green Day",
9
- "artistViewUrl" : "http://itunes.apple.com/us/artist/green-day/id954266?uo=4",
10
- "artworkUrl100" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.100x100-75.jpg",
11
- "artworkUrl30" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.30x30-50.jpg",
12
- "artworkUrl60" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.60x60-50.jpg",
13
- "collectionCensoredName" : "Dookie",
14
- "collectionExplicitness" : "explicit",
15
- "collectionId" : 5132583,
16
- "collectionName" : "Dookie",
17
- "collectionPrice" : 9.99,
18
- "collectionViewUrl" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
19
- "contentAdvisoryRating" : null,
10
+ "artist_id" : 954266,
11
+ "artist_name" : "Green Day",
12
+ "artist_view_url" : "http://itunes.apple.com/us/artist/green-day/id954266?uo=4",
13
+ "artwork_url100" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.100x100-75.jpg",
14
+ "artwork_url30" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.30x30-50.jpg",
15
+ "artwork_url60" : "http://a1.phobos.apple.com/us/r1000/049/Features/1e/17/05/dj.rpihtiig.60x60-50.jpg",
16
+ "collection_censored_name" : "Dookie",
17
+ "collection_explicitness" : "explicit",
18
+ "collection_id" : 5132583,
19
+ "collection_name" : "Dookie",
20
+ "collection_price" : 9.99,
21
+ "collection_view_url" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
22
+ "content_advisory_rating" : null,
20
23
  "country" : "USA",
21
24
  "currency" : "USD",
22
- "discCount" : 1,
23
- "discNumber" : 1,
25
+ "disc_count" : 1,
26
+ "disc_number" : 1,
24
27
  "kind" : "song",
25
- "previewUrl" : "http://a1.phobos.apple.com/us/r1000/027/Music/0e/86/7a/mzm.wchstext.aac.p.m4a",
26
- "primaryGenreName" : "Alternative",
27
- "releaseDate" : "2003-04-22 07:00:00 Etc/GMT",
28
- "trackCensoredName" : "She",
29
- "trackCount" : 15,
30
- "trackExplicitness" : "notExplicit",
31
- "trackId" : 5132563,
32
- "trackName" : "She",
33
- "trackNumber" : 8,
34
- "trackPrice" : 1.29,
35
- "trackTimeMillis" : 134293,
36
- "trackViewUrl" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
37
- "wrapperType" : "track"
28
+ "preview_url" : "http://a1.phobos.apple.com/us/r1000/027/Music/0e/86/7a/mzm.wchstext.aac.p.m4a",
29
+ "primary_genre_name" : "Alternative",
30
+ "release_date" : "2003-04-22 07:00:00 Etc/GMT",
31
+ "track_censored_name" : "She",
32
+ "track_count" : 15,
33
+ "track_explicitness" : "notExplicit",
34
+ "track_id" : 5132563,
35
+ "track_name" : "She",
36
+ "track_number" : 8,
37
+ "track_price" : 1.29,
38
+ "track_time_millis" : 134293,
39
+ "track_view_url" : "http://itunes.apple.com/us/album/she/id5132583?i=5132563&uo=4",
40
+ "wrapper_type" : "track"
38
41
  }
39
42
 
40
- ## Available Methods
43
+ Available Methods
44
+ -----------------
45
+
41
46
  - movie
42
47
  - podcast
43
48
  - music
@@ -47,31 +52,38 @@ A Ruby wrapper around the iTunes API that lets you search for any sort of data t
47
52
  - tv_show
48
53
  - all
49
54
 
50
- ## Using the iTunes gem
55
+ Using the iTunes gem
56
+ --------------------
51
57
 
52
58
  require 'itunes'
53
-
54
- >> itunes = ITunes.new
59
+
60
+ >> itunes = ITunes::Client.new
55
61
  >> songs = itunes.music('green day she')
56
- => {"result_count" => 15, "results" => [...]}
57
- >> songs['results'].each do |song|
58
- >> puts "#{song['trackName']} - #{song['artistName']} (#{song['collectionName']})"
62
+ => <#Hashie::Rash result_count=15 results=[...]>
63
+ >> songs.results.each do |song|
64
+ >> puts "#{song.track_name} - #{song.artist_name} (#{song.collection_name})"
59
65
  >> end
60
66
  => She - Green Day (Dookie)
61
67
  => She - Green Day (Dookie)
62
68
  => She - Green Day (Dookie)
63
69
  => She - Green Day (Dookie)
64
70
  => She - Green Day (Dookie)
65
- => ...
71
+
72
+ Search directly from the class
73
+
66
74
  >> iron_man = ITunes.movie('iron man 2')
67
- => {"result_count" => 1, "results" => [...]}
68
- => ...
75
+ => <#Hashie::Rash result_count=1 results=[...]>
76
+
77
+ Limit the results:
78
+
69
79
  >> foo_fighters = ITunes.music('foo fighters everlong', :limit => 1)
70
- => {"result_count" => 1, "results" => [{ "trackName" => "Everlong", ... }]}
80
+ => <#Hashie::Rash result_count=1 results=[<#Hashie::Rash ...>]>
81
+
82
+ What is Hashie::Rash?
71
83
 
72
- ## Upcoming Features
84
+ If you are familiar with Hashie, Rash is very similar to a Mash. The primary difference is that Rash gives us ruby-friendly keys so instead of artistId, Rash converts that to artist_id.
73
85
 
74
- - A better DSL.
86
+ Copyright
87
+ ---------
75
88
 
76
- ## Copyright
77
89
  Copyright © 2010 Garrett Bjerkhoel. See [MIT-LICENSE](http://github.com/dewski/itunes/blob/master/MIT-LICENSE) for details.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ namespace :doc do
10
+ require 'yard'
11
+ YARD::Rake::YardocTask.new do |task|
12
+ task.files = ['lib/**/*.rb']
13
+ task.options = [
14
+ '--protected',
15
+ '--output-dir', 'doc/yard',
16
+ '--markup', 'markdown',
17
+ ]
18
+ end
19
+ end
data/itunes.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "itunes/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "itunes"
7
+ s.version = ITunes::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Garrett Bjerkhoel"]
10
+ s.email = %q{me@garrettbjerkhoel.com}
11
+ s.homepage = %q{http://github.com/dewski/itunes}
12
+ s.summary = %q{iTunes API}
13
+
14
+ s.rubyforge_project = "itunes"
15
+
16
+ s.extra_rdoc_files = [
17
+ "MIT-LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+
22
+ s.add_development_dependency('bundler', '~> 1.0')
23
+ s.add_development_dependency('rake', '~> 0.8')
24
+ s.add_development_dependency('rspec', '~> 2.5.0')
25
+ s.add_development_dependency('yard', '~> 0.6')
26
+ s.add_development_dependency('maruku', '~> 0.6')
27
+ s.add_development_dependency('simplecov', '~> 0.3')
28
+ s.add_development_dependency('webmock', '~> 1.6')
29
+ s.add_development_dependency('vcr', '~> 1.4.0')
30
+ s.add_development_dependency('json', '~> 1.5.1')
31
+
32
+ s.add_runtime_dependency('hashie', '~> 1.0.0')
33
+ s.add_runtime_dependency('rash', '~> 0.2.0')
34
+ s.add_runtime_dependency('faraday', '~> 0.5.4')
35
+ s.add_runtime_dependency('faraday_middleware', '~> 0.3.1')
36
+ s.add_runtime_dependency('multi_json', '~> 0.0.5')
37
+
38
+ s.files = `git ls-files`.split("\n")
39
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
40
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
41
+ s.require_paths = ["lib"]
42
+ end
43
+
@@ -0,0 +1,30 @@
1
+ require 'faraday'
2
+
3
+ # @private
4
+ module Faraday
5
+ # @private
6
+ class Response::Rashify < Response::Middleware
7
+ begin
8
+ require 'hashie'
9
+ require 'rash'
10
+ rescue LoadError, NameError => error
11
+ self.load_error = error
12
+ end
13
+
14
+ def self.register_on_complete(env)
15
+ env[:response].on_complete do |response|
16
+ response_body = response[:body]
17
+ if response_body.is_a?(Hash)
18
+ response[:body] = ::Hashie::Rash.new(response_body)
19
+ elsif response_body.is_a?(Array)
20
+ response[:body] = response_body.map{|item| item.is_a?(Hash) ? ::Hashie::Rash.new(item) : item}
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize(app)
26
+ super
27
+ @parser = nil
28
+ end
29
+ end
30
+ end
data/lib/itunes.rb CHANGED
@@ -1,73 +1,26 @@
1
- require 'httparty'
1
+ require 'faraday_middleware'
2
+ require 'faraday/rashify'
3
+ require 'itunes/configuration'
4
+ require 'itunes/client'
2
5
 
3
- class ITunes
4
- include HTTParty
6
+ module ITunes
7
+ extend Configuration
5
8
 
6
- base_uri 'ax.phobos.apple.com.edgesuite.net/WebObjects/MZStoreServices.woa/wa/ws'
7
- format :json
8
-
9
- attr_accessor :limit
10
-
11
- def method_missing(name, *args)
12
- methods = %q{movie podcast music music_video audiobook short_film tv_show all}
13
-
14
- if methods.include?(name.to_s)
15
- camelcase = name.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
16
- camelcase[0] = camelcase[0].chr.downcase
17
-
18
- @limit = args[1].delete(:limit) unless args[1].nil? # will return nil if there is no limit
19
-
20
- request(args.first, camelcase)
21
- else
22
- super(name, *args)
23
- end
24
- end
25
-
26
- def initialize(limit=nil)
27
- @limit = limit
28
- end
29
-
30
- # So you don't have to create an instance if you don't need to
31
- def self.music(terms, opts=nil)
32
- self.new.music(terms, opts)
33
- end
34
-
35
- def self.podcast(terms, opts=nil)
36
- self.new.podcast(terms, opts)
37
- end
38
-
39
- def self.movie(terms, opts=nil)
40
- self.new.movie(terms, opts)
9
+ # Alias for ITunes::Client.new
10
+ #
11
+ # @return [ITunes::Client]
12
+ def self.client(options={})
13
+ ITunes::Client.new(options)
41
14
  end
42
-
43
- def self.music_video(terms, opts=nil)
44
- self.new.music_video(terms, opts)
45
- end
46
-
47
- def self.audiobook(terms, opts=nil)
48
- self.new.audiobook(terms, opts)
49
- end
50
-
51
- def self.short_film(terms, opts=nil)
52
- self.new.short_film(terms, opts)
53
- end
54
-
55
- def self.tv_show(terms, opts=nil)
56
- self.new.tv_show(terms, opts)
57
- end
58
-
59
- def self.all(terms, opts=nil)
60
- self.new.all(terms, opts)
15
+
16
+ # Delegate to ITunes::Client
17
+ def self.method_missing(method, *args, &block)
18
+ return super unless client.respond_to?(method)
19
+ client.send(method, *args, &block)
61
20
  end
62
21
 
63
- private
64
- def request(term, media='all')
65
- raise ArgumentError, 'you need to search for something, provide a term.' if term.nil?
66
-
67
- #
68
- query = { :term => term, :media => media }
69
- query.merge!({ :limit => @limit }) unless @limit.nil? or @limit == 0
70
-
71
- self.class.get('Search', { :query => query })
72
- end
22
+ # Delegate to ITunes::Client
23
+ def self.respond_to?(method)
24
+ return client.respond_to?(method) || super
25
+ end
73
26
  end
@@ -0,0 +1,26 @@
1
+ require File.expand_path('../request', __FILE__)
2
+
3
+ module ITunes
4
+ class Client
5
+
6
+ # @private
7
+ attr_accessor *Configuration::VALID_OPTIONS_KEYS
8
+
9
+ # Creates a new Client
10
+ def initialize(options={})
11
+ options = ITunes.options.merge(options)
12
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
13
+ send("#{key}=", options[key])
14
+ end
15
+ end
16
+
17
+ Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
18
+
19
+ alias :api_endpoint :endpoint
20
+
21
+ include Request
22
+ include Search
23
+ include Lookup
24
+
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module ITunes
2
+ module Lookup
3
+
4
+ ID_TYPES = { :amg_artist => 'amgArtistId', :id => 'id', :amg_album => 'amgAlbumId', :upc => 'upc' }
5
+
6
+ # Performs a lookup request based on iTunes IDs, UPCs/ EANs, and All Music Guide (AMG) IDs
7
+ # @param [String] id
8
+ # @option options [Symbol] :id_type used to specify the option type being passed, valid types are: id, upc, amg_artist, amg_album
9
+ # @raise [ArgumentError] If an invalid id_type is specified in options
10
+ def lookup(id, options={})
11
+ id = id.split(',') if id.kind_of?(String)
12
+ id_type = options.delete(:id_type) || :id
13
+
14
+ raise ArgumentError, 'invalid id_type.' unless ID_TYPES.keys.include?(id_type.to_sym)
15
+
16
+ options.merge!({ ID_TYPES[id_type.to_sym] => id.join(',') })
17
+ request('Lookup', options)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,81 @@
1
+ module ITunes
2
+ module Search
3
+
4
+ # Performs a music search
5
+ # @param [String] term The search term
6
+ # @option options [Symbol]
7
+ def music(term, options={})
8
+ search(term, 'music', options)
9
+ end
10
+
11
+ # Performs a movie search
12
+ # @param [String] term The search term
13
+ # @option options [Symbol]
14
+ def movie(term, options={})
15
+ search(term, 'movie', options)
16
+ end
17
+
18
+ # Performs a podcast search
19
+ # @param [String] term The search term
20
+ # @option options [Symbol]
21
+ def podcast(term, options={})
22
+ search(term, 'podcast', options)
23
+ end
24
+
25
+ # Performs a music video search
26
+ # @param [String] term The search term
27
+ # @option options [Symbol]
28
+ def music_video(term, options={})
29
+ search(term, 'musicVideo', options)
30
+ end
31
+
32
+ # Performs an audiobook search
33
+ # @param [String] term The search term
34
+ # @option options [Symbol]
35
+ def audiobook(term, options={})
36
+ search(term, 'audiobook', options)
37
+ end
38
+
39
+ # Performs a short film search
40
+ # @param [String] term The search term
41
+ # @option options [Symbol]
42
+ def short_film(term, options={})
43
+ search(term, 'shortFilm', options)
44
+ end
45
+
46
+ # Performs a tv show search
47
+ # @param [String] term The search term
48
+ # @option options [Symbol]
49
+ def tv_show(term, options={})
50
+ search(term, 'tvShow', options)
51
+ end
52
+
53
+ # Performs a search on all itunes content
54
+ # @param [String] term The search term
55
+ # @option options [Symbol]
56
+ def all(term, options={})
57
+ search(term, 'all', options)
58
+ end
59
+
60
+ private
61
+
62
+ def search(term, media='all', options={})
63
+ raise ArgumentError, 'you need to search for something, provide a term.' if term.nil? || term.length == 0
64
+
65
+ params = { :term => term, :media => media }
66
+ if options.has_key?(:limit)
67
+ params.merge!(:limit => options.delete(:limit))
68
+ elsif limit
69
+ params.merge!({ :limit => limit })
70
+ end
71
+ params.delete(:limit) if params[:limit] && params[:limit] == 0
72
+ params.merge!(options)
73
+
74
+ # clear empty key/value pairs
75
+ params.reject! { |key, value| value.nil? }
76
+
77
+ request('Search', params)
78
+ end
79
+
80
+ end
81
+ end