7digital 0.0.1

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 (85) hide show
  1. data/README.rdoc +7 -0
  2. data/lib/peachy_patchy.rb +14 -0
  3. data/lib/sevendigital.rb +53 -0
  4. data/lib/sevendigital/api_operator.rb +31 -0
  5. data/lib/sevendigital/api_operator_cached.rb +23 -0
  6. data/lib/sevendigital/api_request.rb +24 -0
  7. data/lib/sevendigital/client.rb +124 -0
  8. data/lib/sevendigital/default_configuration.yml +2 -0
  9. data/lib/sevendigital/digestion_tract/api_response_digestor.rb +52 -0
  10. data/lib/sevendigital/digestion_tract/artist_digestor.rb +31 -0
  11. data/lib/sevendigital/digestion_tract/chart_item_digestor.rb +27 -0
  12. data/lib/sevendigital/digestion_tract/digestor.rb +66 -0
  13. data/lib/sevendigital/digestion_tract/format_digestor.rb +21 -0
  14. data/lib/sevendigital/digestion_tract/label_digestor.rb +20 -0
  15. data/lib/sevendigital/digestion_tract/pager_digestor.rb +23 -0
  16. data/lib/sevendigital/digestion_tract/price_digestor.rb +25 -0
  17. data/lib/sevendigital/digestion_tract/release_digestor.rb +52 -0
  18. data/lib/sevendigital/digestion_tract/track_digestor.rb +37 -0
  19. data/lib/sevendigital/management/artist_manager.rb +39 -0
  20. data/lib/sevendigital/management/manager.rb +11 -0
  21. data/lib/sevendigital/management/release_manager.rb +51 -0
  22. data/lib/sevendigital/management/track_manager.rb +18 -0
  23. data/lib/sevendigital/model/api_response.rb +13 -0
  24. data/lib/sevendigital/model/artist.rb +37 -0
  25. data/lib/sevendigital/model/chart_item.rb +9 -0
  26. data/lib/sevendigital/model/format.rb +9 -0
  27. data/lib/sevendigital/model/label.rb +9 -0
  28. data/lib/sevendigital/model/price.rb +11 -0
  29. data/lib/sevendigital/model/release.rb +34 -0
  30. data/lib/sevendigital/model/sevendigital_error.rb +3 -0
  31. data/lib/sevendigital/model/sevendigital_object.rb +51 -0
  32. data/lib/sevendigital/model/track.rb +8 -0
  33. data/lib/sevendigital/pager.rb +17 -0
  34. data/lib/sevendigital/proxy_police.rb +24 -0
  35. data/lib/sevendigital/version.rb +3 -0
  36. data/spec/api_operator_cached_spec.rb +47 -0
  37. data/spec/api_operator_spec.rb +108 -0
  38. data/spec/api_request_spec.rb +27 -0
  39. data/spec/client_spec.rb +46 -0
  40. data/spec/data/config/sevendigital.yml +5 -0
  41. data/spec/data/configuration_env_override.yml +5 -0
  42. data/spec/data/configuration_override.yml +1 -0
  43. data/spec/digestion_tract/api_response_digestor_spec.rb +74 -0
  44. data/spec/digestion_tract/artist_digestor_spec.rb +83 -0
  45. data/spec/digestion_tract/chart_digestor_spec.rb +54 -0
  46. data/spec/digestion_tract/format_digestor_spec.rb +40 -0
  47. data/spec/digestion_tract/label_digestor_spec.rb +35 -0
  48. data/spec/digestion_tract/pager_digestor_spec.rb +52 -0
  49. data/spec/digestion_tract/price_digestor_spec.rb +57 -0
  50. data/spec/digestion_tract/release_digestor_spec.rb +102 -0
  51. data/spec/digestion_tract/track_digestor_spec.rb +107 -0
  52. data/spec/management/artist_manager_spec.rb +108 -0
  53. data/spec/management/release_manager_spec.rb +178 -0
  54. data/spec/management/track_manager_spec.rb +52 -0
  55. data/spec/model/api_response_spec.rb +32 -0
  56. data/spec/model/artist_spec.rb +122 -0
  57. data/spec/model/release_spec.rb +111 -0
  58. data/spec/pager_spec.rb +8 -0
  59. data/spec/proxy_police_spec.rb +49 -0
  60. data/spec/spec_helper.rb +48 -0
  61. data/spec/test-xml/methods/artist/byTag/top.xml +51 -0
  62. data/spec/test-xml/methods/artist/details.xml +10 -0
  63. data/spec/test-xml/methods/artist/releases.xml +545 -0
  64. data/spec/test-xml/methods/artist/similar.xml +40 -0
  65. data/spec/test-xml/methods/artist/toptracks.xml +280 -0
  66. data/spec/test-xml/methods/release/bydate.xml +174 -0
  67. data/spec/test-xml/methods/release/bytag/top.xml +151 -0
  68. data/spec/test-xml/methods/release/chart.xml +182 -0
  69. data/spec/test-xml/methods/release/details.xml +49 -0
  70. data/spec/test-xml/methods/release/recommend.xml +90 -0
  71. data/spec/test-xml/methods/release/tracks.xml +29 -0
  72. data/spec/test-xml/methods/track/chart.xml +150 -0
  73. data/spec/test-xml/methods/track/details.xml +31 -0
  74. data/spec/test-xml/objects/artist.xml +7 -0
  75. data/spec/test-xml/objects/artist_chart_item.xml +8 -0
  76. data/spec/test-xml/objects/artist_list.xml +23 -0
  77. data/spec/test-xml/objects/artist_list_empty.xml +5 -0
  78. data/spec/test-xml/objects/price.xml +8 -0
  79. data/spec/test-xml/objects/release.xml +40 -0
  80. data/spec/test-xml/objects/release_chart_item.xml +34 -0
  81. data/spec/test-xml/objects/release_list.xml +19 -0
  82. data/spec/test-xml/objects/release_list_empty.xml +5 -0
  83. data/spec/test-xml/objects/track.xml +34 -0
  84. data/spec/test-xml/objects/track_chart_item.xml +28 -0
  85. metadata +151 -0
@@ -0,0 +1,7 @@
1
+ Ruby wrapper for 7digital API
2
+
3
+ Example usage:
4
+ sevendigital_client = Sevendigital::Client.new(:oauth_consumer_key => "YOUR_KEY_HERE", :country => "GB")
5
+ artist = sevendigital_client.artist.get_details(1)
6
+ puts artist.name
7
+
@@ -0,0 +1,14 @@
1
+ module Peachy
2
+ class Proxy
3
+
4
+ # hide_public_methods ['kind_of?', 'send']
5
+
6
+ def no_matching_xml method_name
7
+ return nil
8
+ end
9
+
10
+ def ==(other_proxy)
11
+ (to_s == other_proxy.to_s)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+
2
+ module Sevendigital
3
+
4
+ # :stopdoc:
5
+ VERSION = '0.0.1'
6
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
+ # :startdoc:
9
+
10
+ # Returns the version string for the library.
11
+ #
12
+ def self.version
13
+ VERSION
14
+ end
15
+
16
+ # Returns the library path for the module. If any arguments are given,
17
+ # they will be joined to the end of the libray path using
18
+ # <tt>File.join</tt>.
19
+ #
20
+ def self.libpath( *args )
21
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
22
+ end
23
+
24
+ # Returns the lpath for the module. If any arguments are given,
25
+ # they will be joined to the end of the path using
26
+ # <tt>File.join</tt>.
27
+ #
28
+ def self.path( *args )
29
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
30
+ end
31
+
32
+ # Utility method used to require all files ending in .rb that lie in the
33
+ # directory below this file that has the same name as the filename passed
34
+ # in. Optionally, a specific _directory_ name can be passed in such that
35
+ # the _filename_ does not have to be equivalent to the directory.
36
+ #
37
+ def self.require_all_libs_relative_to( fname, dir = nil )
38
+ dir ||= ::File.basename(fname, '.*')
39
+ search_me = ::File.expand_path(
40
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
41
+
42
+ Dir.glob(search_me).sort.each {|rb| require rb }
43
+ end
44
+
45
+ end # module 7digital
46
+
47
+ require 'peachy'
48
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'peachy_patchy')
49
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'sevendigital', 'management', 'manager')
50
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'sevendigital', 'digestion_tract', 'digestor')
51
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'sevendigital', 'model', 'sevendigital_object')
52
+ Sevendigital.require_all_libs_relative_to(__FILE__)
53
+
@@ -0,0 +1,31 @@
1
+ module Sevendigital
2
+
3
+ require 'net/http'
4
+
5
+ class ApiOperator
6
+
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def call_api(api_request)
12
+ make_http_request_and_digest(create_request_uri(api_request))
13
+ end
14
+
15
+ def make_http_request_and_digest(uri)
16
+ http_response = Net::HTTP.get_response(uri)
17
+ api_response = @client.api_response_digestor.from_http_response(http_response)
18
+ raise Sevendigital::SevendigitalError, "#{api_response.error_code} - #{api_response.error_message}" if !api_response.ok?
19
+ api_response
20
+ end
21
+
22
+ def create_request_uri(api_request)
23
+ api_request.ensure_country_is_set(@client.country)
24
+ params = api_request.parameters.collect{ |a,v| "&#{a}=#{v}" }.join
25
+ URI.parse("http://#{@client.configuration.api_url}/#{@client.configuration.api_version}/#{api_request.api_method}"+
26
+ "?oauth_consumer_key=#{@client.configuration.oauth_consumer_key}#{params}")
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,23 @@
1
+ module Sevendigital
2
+
3
+ class ApiOperatorCached < ApiOperator
4
+
5
+ def initialize(client, cache)
6
+ @cache = cache
7
+ super(client)
8
+ end
9
+
10
+ def call_api(api_request)
11
+ uri = create_request_uri(api_request)
12
+ api_response = @cache.get(uri.to_s)
13
+ #puts "got from cache #{uri}" if api_response
14
+ if (!api_response) then
15
+ #puts "calling #{uri}"
16
+ api_response = make_http_request_and_digest(uri)
17
+ @cache.set(uri.to_s, api_response)
18
+ end
19
+ api_response
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,24 @@
1
+ module Sevendigital
2
+
3
+ class ApiRequest
4
+
5
+ attr_reader :api_method, :parameters
6
+
7
+ def initialize(api_method, parameters, options = {})
8
+ @api_method = api_method
9
+ @parameters = comb_parameters(options.merge(parameters))
10
+ end
11
+
12
+ def comb_parameters(parameters)
13
+ page_size = parameters[:page_size] || parameters[:per_page]
14
+ parameters[:pageSize] ||= page_size if page_size
15
+ return parameters
16
+ end
17
+
18
+ def ensure_country_is_set(country)
19
+ @parameters[:country] ||= country
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,124 @@
1
+ require 'ostruct'
2
+ require 'yaml'
3
+
4
+
5
+ module Sevendigital
6
+
7
+ DEFAULT_CONFIGURATION = {
8
+ :api_url => "api.7digital.com",
9
+ :api_version => "1.2"
10
+ }
11
+
12
+ class Client
13
+
14
+ def load_configuration_from_yml(file_name, environment=nil)
15
+ plain_settings = YAML.load_file(file_name)
16
+ if (plain_settings["common"] || (environment && plain_settings[environment])) then
17
+ environment_settings = plain_settings["common"] || {}
18
+ environment_settings.update(plain_settings[environment]) if environment
19
+ environment_settings
20
+ else
21
+ plain_settings
22
+ end
23
+ end
24
+
25
+ def load_configurations(configuration)
26
+
27
+ default_settings = Sevendigital::DEFAULT_CONFIGURATION
28
+
29
+ if (configuration.kind_of? String) then
30
+ yml_configuration_file = configuration
31
+ else
32
+ yml_configuration_file ="#{RAILS_ROOT}/config/sevendigital.yml" if defined?(RAILS_ROOT)
33
+ explicit_settings = configuration if configuration.kind_of? Hash
34
+ explicit_settings = configuration.marshal_dump if configuration.kind_of? OpenStruct
35
+ end
36
+
37
+ environment = defined?(RAILS_ENV) ? RAILS_ENV : nil
38
+ yml_settings = load_configuration_from_yml(yml_configuration_file, environment) if yml_configuration_file
39
+
40
+ settings = default_settings
41
+ settings.update(yml_settings) if yml_settings
42
+ settings.update(explicit_settings) if explicit_settings
43
+
44
+ return OpenStruct.new(settings)
45
+ end
46
+
47
+ #Code here
48
+
49
+ def initialize(configuration=nil, api_operator=nil)
50
+ @configuration = load_configurations(configuration)
51
+ @api_operator = api_operator || hire_api_operator
52
+ end
53
+
54
+ def hire_api_operator
55
+ @configuration.cache ? ApiOperatorCached.new(self, @configuration.cache) : ApiOperator.new(self)
56
+ end
57
+
58
+ def artist
59
+ @artist_manager ||= ArtistManager.new(self)
60
+ end
61
+
62
+ def artist_digestor
63
+ @artist_digestor ||= ArtistDigestor.new(self)
64
+ end
65
+
66
+ def release
67
+ @release_manager ||= ReleaseManager.new(self)
68
+ end
69
+
70
+ def release_digestor
71
+ @release_digestor ||= ReleaseDigestor.new(self)
72
+ end
73
+
74
+ def label_digestor
75
+ @label_digestor ||= LabelDigestor.new(self)
76
+ end
77
+
78
+ def format_digestor
79
+ @format_digestor ||= FormatDigestor.new(self)
80
+ end
81
+
82
+ def price_digestor
83
+ @price_digestor ||= PriceDigestor.new(self)
84
+ end
85
+
86
+ def pager_digestor
87
+ @pager_digestor ||= PagerDigestor.new(self)
88
+ end
89
+
90
+ def track
91
+ @track_manager ||= TrackManager.new(self)
92
+ end
93
+
94
+ def track_digestor
95
+ @track_digestor ||= TrackDigestor.new(self)
96
+ end
97
+
98
+ def api_response_digestor
99
+ @api_response_digestor ||= ApiResponseDigestor.new(self)
100
+ end
101
+
102
+ def chart_item_digestor
103
+ @chart_item_digestor ||= ChartItemDigestor.new(self)
104
+ end
105
+
106
+ def configuration
107
+ return @configuration
108
+ end
109
+
110
+ def operator
111
+ @api_operator
112
+ end
113
+
114
+ def country
115
+ @country || @configuration.country
116
+ end
117
+
118
+ def country=(country_code)
119
+ @country = country_code
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,2 @@
1
+ api_url: api.7digital.com
2
+ api_version: 1.2
@@ -0,0 +1,52 @@
1
+ module Sevendigital
2
+
3
+ class ApiResponseDigestor < Digestor
4
+
5
+ def from_xml(xml_or_proxy, element_name = :response)
6
+ return from_proxy(ProxyPolice.ensure_is_proxy(xml_or_proxy, element_name))
7
+ end
8
+
9
+ def from_proxy(proxy)
10
+ if proxy && proxy.status then
11
+ return from_ok_response(proxy) if proxy.status == 'ok'
12
+ return from_error_response(proxy.error) if proxy.status == 'error' && proxy.error
13
+ end
14
+ return from_invalid_xml
15
+ end
16
+
17
+ def from_invalid_xml
18
+ response = ApiResponse.new
19
+ response.error_code = 10000
20
+ response.error_message = 'Invalid 7digital API response'
21
+ return response
22
+ end
23
+
24
+ def from_error_response(error)
25
+ response = ApiResponse.new
26
+ response.error_code = error.code.to_i
27
+ response.error_message = error.error_message.value.to_s
28
+ return response
29
+ end
30
+
31
+ def from_ok_response(response_content)
32
+ response = ApiResponse.new
33
+ response.error_code = 0
34
+ response.content = response_content
35
+ return response
36
+ end
37
+
38
+ def from_http_response(http_response)
39
+ return from_xml(http_response.body.to_s) if http_response.is_a?(Net::HTTPSuccess)
40
+ from_invalid_http_response(http_response)
41
+ end
42
+
43
+ def from_invalid_http_response(http_response)
44
+ response = ApiResponse.new
45
+ response.error_code = Integer(http_response.code)
46
+ response.error_message= http_response.body.to_s
47
+ return response
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,31 @@
1
+ module Sevendigital
2
+
3
+ class ArtistDigestor < Digestor
4
+
5
+ def default_element_name; :artist end
6
+ def default_list_element_name; :artists end
7
+
8
+ def from_proxy(artist_proxy)
9
+ make_sure_not_eating_nil(artist_proxy)
10
+ artist = Artist.new(@api_client)
11
+ populate_required_properties(artist, artist_proxy)
12
+ populate_optional_properties(artist, artist_proxy)
13
+ return artist
14
+ end
15
+
16
+ private
17
+
18
+ def populate_required_properties(artist, artist_proxy)
19
+ artist.id = artist_proxy.id.to_i
20
+ artist.name = artist_proxy.name.value.to_s
21
+ end
22
+
23
+ def populate_optional_properties(artist, artist_proxy)
24
+ artist.sort_name = artist_proxy.sort_name.value.to_s if artist_proxy.sort_name
25
+ artist.appears_as = artist_proxy.appears_as.value.to_s if artist_proxy.appears_as
26
+ artist.image = artist_proxy.image.value.to_s if artist_proxy.image
27
+ artist.url = artist_proxy.url.value.to_s if artist_proxy.url
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module Sevendigital
2
+
3
+ class ChartItemDigestor < Digestor
4
+
5
+ def default_element_name; :chart_item end
6
+ def default_list_element_name; :chart end
7
+
8
+ def from_proxy(chart_item_proxy)
9
+
10
+ make_sure_not_eating_nil(chart_item_proxy)
11
+
12
+ chart_item = ChartItem.new(@client)
13
+ chart_item.position = chart_item_proxy.position.value.to_i
14
+ chart_item.change = chart_item_proxy.change.value.to_s.downcase.to_sym
15
+ if chart_item_proxy.release then
16
+ chart_item.item = @api_client.release_digestor.from_proxy(chart_item_proxy.release)
17
+ elsif chart_item_proxy.track
18
+ chart_item.item = @api_client.track_digestor.from_proxy(chart_item_proxy.track)
19
+ else
20
+ chart_item.item = @api_client.artist_digestor.from_proxy(chart_item_proxy.artist)
21
+ end
22
+ return chart_item
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,66 @@
1
+ module Sevendigital
2
+
3
+ class Digestor
4
+
5
+ #TODO TEST THIS CRAP
6
+
7
+ def initialize(api_client)
8
+ @api_client = api_client
9
+ end
10
+
11
+ def from_xml(xml_or_proxy, element_name = default_element_name)
12
+ return from_proxy(ProxyPolice.ensure_is_proxy(xml_or_proxy, element_name))
13
+ end
14
+
15
+ def list_from_xml(xml_or_proxy, list_element_name = default_list_element_name)
16
+ list_from_proxy(ProxyPolice.ensure_is_proxy(xml_or_proxy, list_element_name))
17
+ end
18
+
19
+ def list_from_proxy(object_list_proxy)
20
+ make_sure_not_eating_nil(object_list_proxy)
21
+ list = []
22
+ if object_list_proxy.send(default_element_name) then
23
+ object_list_proxy.send(default_element_name).each { |object_proxy| list << from_proxy(object_proxy) }
24
+ end
25
+ return paginate_results(object_list_proxy, list)
26
+ end
27
+
28
+ #nested parsing for api methods that return standard object inside containers with no additional (useful) information
29
+ #e.g. tagged_item.artist, recommendation.release, search_result.track, etc
30
+
31
+ def nested_list_from_xml(xml_or_proxy, container_element_name, list_element_name = default_list_element_name)
32
+ nested_list_from_proxy(ProxyPolice.ensure_is_proxy(xml_or_proxy, list_element_name), container_element_name)
33
+ end
34
+
35
+ def nested_list_from_proxy(object_list_proxy, container_element_name)
36
+ make_sure_not_eating_nil(object_list_proxy)
37
+ list = []
38
+ if object_list_proxy.send(container_element_name) then
39
+ object_list_proxy.send(container_element_name).each do
40
+ |object_proxy| list << from_proxy(object_proxy.send(default_element_name))
41
+ end
42
+ end
43
+ return paginate_results(object_list_proxy, list)
44
+ end
45
+
46
+ def paginate_results(xml_results, list)
47
+ @api_client.pager_digestor.from_xml(xml_results).paginate_list(list)
48
+ end
49
+
50
+ def make_sure_not_eating_nil(proxy)
51
+ raise DigestiveProblem, "There's nothing i can digest" unless proxy
52
+ end
53
+
54
+ def value_present?(proxy_node)
55
+ !proxy_node.nil? && !proxy_node.value.nil? && !proxy_node.value.empty?
56
+ end
57
+
58
+ def content_present?(proxy_node)
59
+ !proxy_node.nil?
60
+ end
61
+
62
+ end
63
+
64
+ class DigestiveProblem < StandardError; end
65
+
66
+ end