metal_archives 0.2.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 +7 -0
- data/.gitignore +11 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +0 -0
- data/README.md +88 -0
- data/Rakefile +14 -0
- data/lib/metal_archives.rb +22 -0
- data/lib/metal_archives/configuration.rb +81 -0
- data/lib/metal_archives/error.rb +35 -0
- data/lib/metal_archives/http_client.rb +75 -0
- data/lib/metal_archives/models/artist.rb +216 -0
- data/lib/metal_archives/models/band.rb +291 -0
- data/lib/metal_archives/models/base_model.rb +153 -0
- data/lib/metal_archives/models/label.rb +112 -0
- data/lib/metal_archives/models/range.rb +58 -0
- data/lib/metal_archives/parsers/artist.rb +103 -0
- data/lib/metal_archives/parsers/band.rb +160 -0
- data/lib/metal_archives/parsers/label.rb +68 -0
- data/lib/metal_archives/parsers/parser_helper.rb +79 -0
- data/lib/metal_archives/version.rb +6 -0
- data/metal_archives.gemspec +28 -0
- data/test/base_model_test.rb +87 -0
- data/test/configuration_test.rb +57 -0
- data/test/parser_helper_test.rb +37 -0
- data/test/property/artist_property_test.rb +43 -0
- data/test/property/band_property_test.rb +94 -0
- data/test/query/artist_query_test.rb +44 -0
- data/test/query/band_query_test.rb +84 -0
- data/test/range_test.rb +41 -0
- data/test/test_helper.rb +26 -0
- metadata +214 -0
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
data/Gemfile
ADDED
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
|