metal_archives 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07ba21e5ea2f91a35d841543732f2cad9faf3943
4
+ data.tar.gz: fd48c5f0ae010184778ed9d6137004684beb1c64
5
+ SHA512:
6
+ metadata.gz: ed2595557edfcb18ff9d74822039e6d8d846e9d9348ba73ffd41dd1d4bc8d06dddd48472ab833c2afd17cd120b3cd4e63c1ddb7f081a780a95b87c8bb4279149
7
+ data.tar.gz: 87d80f9d69daa76339513028aba3c6d0179b130847c9e1314ce40c9fe7a1a8f993370dd488e424fb1734bde1dc0fff48cce0540595e5fc8d546059803b01f638
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ # Ruby version
2
+ .ruby-version
3
+ .ruby-gemset
4
+
5
+ # Temp files
6
+ .byebug_history
7
+
8
+ # RDoc
9
+ html/
10
+
11
+ Gemfile.lock
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1
4
+
5
+ Initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Metal Archives Web Service Wrapper
2
+
3
+ ## Installation
4
+
5
+ ```shell
6
+ $ gem install metal_archives
7
+ ```
8
+
9
+ or add it to your Gemfile
10
+
11
+ ```ruby
12
+ gem 'metal_archives'
13
+ ```
14
+
15
+ ```shell
16
+ $ bundle install
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ ```ruby
22
+ require 'active_support/cache'
23
+
24
+ MetalArchives.configure do |c|
25
+ # Application identity (required)
26
+ c.app_name = "My App"
27
+ c.app_version = "1.0"
28
+ c.app_contact = "support@mymusicapp.com"
29
+
30
+ # Cache config (optional)
31
+ c.enable_cache = true
32
+ c.cache_store = ActiveSupport::Cache.lookup_store(:file_store, '/tmp/metal_archives-cache')
33
+
34
+ # Request throttling (optional, overrides defaults)
35
+ c.request_rate = 1
36
+ c.request_timeout = 3
37
+
38
+ # Print debugging information
39
+ c.debug = false
40
+ end
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```ruby
46
+ require 'metal_archives'
47
+
48
+ # Search for bands
49
+ @alquimia_list = MetalArchives::Band.search('Alquimia')
50
+
51
+ # Find bands by name
52
+ @iron_maiden = MetalArchives::Band.find_by(:name => 'Iron Maiden')
53
+
54
+ # Find bands by attributes
55
+ require 'countries'
56
+
57
+ @bands_in_belgium = MetalArchives::Band.search_by :country => ISO3166::Country['BE']
58
+ @bands_formed_in_1990 = MetalArchives::Band.search_by :year => Range.new(Date.new(1990))
59
+
60
+ # Metal Archives' usual tips apply
61
+
62
+ @bands_containing_hell = MetalArchives::Band.search_by :name => '*hell*'
63
+ @non_melodic_death_bands = MetalArchives::Band.search_by :genre => 'death -melodic'
64
+ ```
65
+
66
+ Refer to the model's RDoc documentation for full documentation.
67
+
68
+ ## Debugging
69
+
70
+ Turn on `debug` in the configuration block to enable logging HTTP requests and responses.
71
+
72
+ ```
73
+ $ irb -r metal_archives
74
+ ```
75
+
76
+ ## Testing
77
+ ```
78
+ $ bundle exec rake test
79
+ ```
80
+
81
+ ## Documentation
82
+ ```
83
+ $ bundle exec rake rdoc
84
+ ```
85
+
86
+ ## Copyright
87
+
88
+ Copyright 2016 Florian Dejonckheere. See `LICENSE` for further details.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/*/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ require 'rdoc/task'
11
+ RDoc::Task.new do |rdoc|
12
+ rdoc.main = "README.md"
13
+ rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
14
+ end
@@ -0,0 +1,22 @@
1
+ require 'metal_archives/version'
2
+ require 'metal_archives/configuration'
3
+ require 'metal_archives/error'
4
+
5
+ require 'metal_archives/models/base_model'
6
+ require 'metal_archives/models/range'
7
+ require 'metal_archives/models/label'
8
+ require 'metal_archives/models/artist'
9
+ require 'metal_archives/models/band'
10
+
11
+ require 'metal_archives/parsers/parser_helper'
12
+ require 'metal_archives/parsers/label'
13
+ require 'metal_archives/parsers/artist'
14
+ require 'metal_archives/parsers/band'
15
+
16
+ require 'metal_archives/http_client'
17
+
18
+ ##
19
+ # Metal Archives Ruby API
20
+ #
21
+ module MetalArchives
22
+ end
@@ -0,0 +1,81 @@
1
+ module MetalArchives
2
+ class << self
3
+ ##
4
+ # API configuration
5
+ #
6
+ # Instance of rdoc-ref:MetalArchives::Configuration
7
+ #
8
+ attr_accessor :config
9
+
10
+ ##
11
+ # Configure API options.
12
+ #
13
+ # A block must be specified, to which a
14
+ # rdoc-ref:MetalArchives::Configuration parameter will be passed.
15
+ #
16
+ # Raises rdoc-ref:InvalidConfigurationException
17
+ #
18
+ def configure
19
+ raise MetalArchives::Errors::InvalidConfigurationError, 'No configuration block given' unless block_given?
20
+ yield MetalArchives.config ||= MetalArchives::Configuration.new
21
+
22
+ raise MetalArchives::Errors::InvalidConfigurationError, 'app_name has not been configured' unless MetalArchives.config.app_name and not MetalArchives.config.app_name.empty?
23
+ raise MetalArchives::Errors::InvalidConfigurationError, 'app_version has not been configured' unless MetalArchives.config.app_version and not MetalArchives.config.app_version.empty?
24
+ raise MetalArchives::Errors::InvalidConfigurationError, 'app_contact has not been configured' unless MetalArchives.config.app_contact and not MetalArchives.config.app_contact.empty?
25
+
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Contains configuration options
31
+ #
32
+ class Configuration
33
+ ##
34
+ # *Required.* Application name (used in request header)
35
+ #
36
+ attr_accessor :app_name
37
+
38
+ ##
39
+ # *Required.* Application version (used in request header)
40
+ #
41
+ attr_accessor :app_version
42
+
43
+ ##
44
+ # *Required.* Application contact email (used in request header)
45
+ #
46
+ attr_accessor :app_contact
47
+
48
+ ##
49
+ # Whether to enable the cache
50
+ #
51
+ attr_accessor :enable_cache
52
+
53
+ ##
54
+ # ActiveSupport::Cache compatible store
55
+ #
56
+ attr_accessor :cache_store
57
+
58
+ ##
59
+ # Request throttling rate (in seconds per request per path)
60
+ #
61
+ attr_accessor :request_rate
62
+
63
+ ##
64
+ # Request timeout (in seconds per request per path)
65
+ #
66
+ attr_accessor :request_timeout
67
+
68
+ ##
69
+ # Print debug information
70
+ #
71
+ attr_accessor :debug
72
+
73
+ ##
74
+ # Default configuration values
75
+ #
76
+ def initialize
77
+ @throttle_rate = 1
78
+ @throttle_wait = 3
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,35 @@
1
+ module MetalArchives
2
+ ##
3
+ # MetalArchives gem specific errors
4
+ #
5
+ module Errors
6
+ ##
7
+ # Generic error
8
+ #
9
+ class Error < StandardError; end
10
+
11
+ ##
12
+ # Error in data
13
+ #
14
+ class DataError < Error; end
15
+
16
+ ##
17
+ # No or invalid configuration found
18
+ #
19
+ class InvalidConfigurationError < Error; end
20
+
21
+ ##
22
+ # Error parsing value
23
+ #
24
+ class ParserError < Error; end
25
+
26
+ ##
27
+ # Functionality not implemented (yet)
28
+ class NotImplementedError < Error; end
29
+
30
+ ##
31
+ # Error in backend response
32
+ #
33
+ class APIError < Error; end
34
+ end
35
+ end
@@ -0,0 +1,75 @@
1
+ require 'faraday'
2
+ require 'faraday-http-cache'
3
+ require 'faraday_throttler'
4
+
5
+ module MetalArchives
6
+ ##
7
+ # HTTP request client
8
+ #
9
+ class HTTPClient # :nodoc:
10
+ class << self
11
+
12
+ ##
13
+ # Retrieve a HTTP resource
14
+ #
15
+ def get(*params)
16
+ response = client.get *params
17
+
18
+ raise Errors::APIError, response.status if response.status >= 400
19
+
20
+ response
21
+ rescue Faraday::Error::ClientError => e
22
+ raise Errors::APIError, e
23
+ end
24
+
25
+ private
26
+ ##
27
+ # Retrieve a HTTP client
28
+ #
29
+ def client
30
+ raise Errors::InvalidConfigurationError, 'Not configured yet' unless MetalArchives.config
31
+
32
+ @faraday ||= Faraday.new do |f|
33
+ f.request :url_encoded # form-encode POST params
34
+ f.response :logger if !!MetalArchives.config.debug # log requests to STDOUT
35
+
36
+ f.use MetalArchives::Middleware
37
+ f.use Faraday::HttpCache,
38
+ :store => MetalArchives.config.cache_store if !!MetalArchives.config.enable_cache
39
+ f.use :throttler,
40
+ :rate => MetalArchives.config.request_rate,
41
+ :wait => MetalArchives.config.request_timeout
42
+
43
+ f.adapter Faraday.default_adapter
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Faraday middleware
51
+ #
52
+ class Middleware < Faraday::Middleware # :nodoc:
53
+ def call(env)
54
+ env[:request_headers].merge!(
55
+ 'User-Agent' => user_agent_string,
56
+ 'Via' => via_string,
57
+ 'Accept' => accept_string
58
+ )
59
+ @app.call(env)
60
+ end
61
+
62
+ private
63
+ def user_agent_string
64
+ "#{MetalArchives.config.app_name}/#{MetalArchives.config.app_version} ( #{MetalArchives.config.app_contact} )"
65
+ end
66
+
67
+ def accept_string
68
+ 'application/json'
69
+ end
70
+
71
+ def via_string
72
+ "gem metal_archives/#{VERSION}"
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,216 @@
1
+ require 'date'
2
+ require 'countries'
3
+
4
+ module MetalArchives
5
+
6
+ ##
7
+ # Represents a single performer (but not a solo artist)
8
+ #
9
+ class Artist < BaseModel
10
+ ##
11
+ # :attr_reader: id
12
+ #
13
+ # Returns +Integer+
14
+ #
15
+ property :id, :type => Integer
16
+
17
+ ##
18
+ # :attr_reader: name
19
+ #
20
+ # Returns +String+
21
+ #
22
+ property :name
23
+
24
+ ##
25
+ # :attr_reader: aliases
26
+ #
27
+ # Returns +Array+ of +String+
28
+ #
29
+ property :aliases, :multiple => true
30
+
31
+ ##
32
+ # :attr_reader: country
33
+ #
34
+ # Returns +ISO3166::Country+
35
+ #
36
+ property :country, :type => ISO3166::Country
37
+
38
+ ##
39
+ # :attr_reader: location
40
+ #
41
+ # Returns +String+
42
+ #
43
+ property :location
44
+
45
+ ##
46
+ # :attr_reader: date_of_birth
47
+ #
48
+ # Returns +Date+
49
+ #
50
+ property :date_of_birth, :type => Date
51
+
52
+ ##
53
+ # :attr_reader: date_of_death
54
+ #
55
+ # Returns +Date+
56
+ #
57
+ property :date_of_death, :type => Date
58
+
59
+ ##
60
+ # :attr_reader: cause_of_death
61
+ #
62
+ # Returns +String+
63
+ #
64
+ property :cause_of_death
65
+
66
+ ##
67
+ # :attr_reader: gender
68
+ #
69
+ # Returns +Symbol+, either +:male+ or +:female+
70
+ #
71
+ enum :gender, :values => [:male, :female]
72
+
73
+ ##
74
+ # :attr_reader: biography
75
+ #
76
+ # Returns raw HTML +String+
77
+ #
78
+ property :biography
79
+
80
+ ##
81
+ # :attr_reader: trivia
82
+ #
83
+ # Returns raw HTML +String+
84
+ #
85
+ property :trivia
86
+
87
+ ##
88
+ # :attr_reader: links
89
+ #
90
+ # Returns +Array+ of +Hash+ containing the following keys
91
+ #
92
+ # [+similar+]
93
+ # - +:url+: +String+
94
+ # - +:type+: +Symbol+, either +:official+, +:unofficial+ or +:unlisted_bands+
95
+ # - +:title+: +String+
96
+ #
97
+ property :links, :multiple => true
98
+
99
+ # TODO: active bands/albums
100
+ # TODO: past bands/albums
101
+ # TODO: guest bands/albums
102
+ # TODO: misc bands/albums
103
+
104
+ protected
105
+ ##
106
+ # Fetch the data and assemble the model
107
+ #
108
+ # Raises rdoc-ref:MetalArchives::Errors::APIError
109
+ #
110
+ def assemble # :nodoc:
111
+ ## Base attributes
112
+ url = "http://www.metal-archives.com/artist/view/id/#{id}"
113
+ response = HTTPClient.get url
114
+
115
+ properties = Parsers::Artist.parse_html response.body
116
+
117
+ ## Biography
118
+ url = "http://www.metal-archives.com/artist/read-more/id/#{id}/field/biography"
119
+ response = HTTPClient.get url
120
+
121
+ properties[:biography] = response.body
122
+
123
+ ## Trivia
124
+ url = "http://www.metal-archives.com/artist/read-more/id/#{id}/field/trivia"
125
+ response = HTTPClient.get url
126
+
127
+ properties[:trivia] = response.body
128
+
129
+ ## Related links
130
+ url = "http://www.metal-archives.com/link/ajax-list/type/person/id/#{id}"
131
+ response = HTTPClient.get url
132
+
133
+ properties[:links] = Parsers::Artist.parse_links_html response.body
134
+
135
+ ## Use constructor to fill properties
136
+ initialize properties
137
+ end
138
+
139
+ class << self
140
+ ##
141
+ # Find by ID
142
+ #
143
+ # Returns rdoc-ref:Artist, even when ID is invalid (because the data is lazily fetched)
144
+ #
145
+ # [+id+]
146
+ # +Integer+
147
+ #
148
+ def find(id)
149
+ Artist.new :id => id
150
+ end
151
+
152
+ ##
153
+ # Find by attributes
154
+ #
155
+ # Returns rdoc-ref:Artist or nil when ID is invalid
156
+ #
157
+ # [+query+]
158
+ # Hash containing one or more of the following keys:
159
+ # - +:name+: +String+
160
+ #
161
+ def find_by(query)
162
+ url = 'http://www.metal-archives.com/search/ajax-artist-search/'
163
+ params = Parsers::Artist.map_params query
164
+
165
+ response = HTTPClient.get url, params
166
+ json = JSON.parse response.body
167
+
168
+ return nil if json['aaData'].empty?
169
+
170
+ data = json['aaData'].first
171
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
172
+
173
+ Artist.new :id => id
174
+ rescue Errors::APIError
175
+ nil
176
+ end
177
+
178
+ ##
179
+ # Search by name
180
+ #
181
+ # Returns (possibly empty) +Array+ of rdoc-ref:Artist
182
+ #
183
+ # [+name+]
184
+ # +String+
185
+ #
186
+ def search(name)
187
+ objects = []
188
+
189
+ url = 'http://www.metal-archives.com/search/ajax-artist-search/'
190
+ query = {
191
+ :name => name,
192
+ :iDisplayStart => 0
193
+ }
194
+
195
+ loop do
196
+ params = Parsers::Artist.map_params query
197
+
198
+ response = HTTPClient.get url, params
199
+ json = JSON.parse response.body
200
+
201
+ json['aaData'].each do |data|
202
+ # Create Artist object for every ID in the results list
203
+ id = Nokogiri::HTML(data.first).xpath('//a/@href').first.value.gsub('\\', '').split('/').last.gsub(/\D/, '').to_i
204
+ objects << Artist.new(:id => id)
205
+ end
206
+
207
+ break if objects.length == json['iTotalRecords']
208
+
209
+ query[:iDisplayStart] += 200
210
+ end
211
+
212
+ objects
213
+ end
214
+ end
215
+ end
216
+ end