itunes 0.3.0 → 0.4.0

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