metal_archives 2.2.0 → 2.2.3
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 +5 -5
- data/.github/workflows/ci.yml +46 -0
- data/.github/workflows/release.yml +69 -0
- data/.gitignore +6 -6
- data/.overcommit.yml +35 -0
- data/.rspec +1 -0
- data/.rubocop.yml +54 -8
- data/.rubocop_todo.yml +92 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +1 -1
- data/LICENSE.md +1 -1
- data/README.md +35 -64
- data/Rakefile +8 -7
- data/bin/console +41 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +14 -0
- data/lib/metal_archives.rb +48 -29
- data/lib/metal_archives/{utils/collection.rb → collection.rb} +0 -0
- data/lib/metal_archives/configuration.rb +9 -33
- data/lib/metal_archives/{error.rb → errors.rb} +0 -0
- data/lib/metal_archives/http_client.rb +13 -8
- data/lib/metal_archives/{utils/lru_cache.rb → lru_cache.rb} +0 -0
- data/lib/metal_archives/middleware/cache_check.rb +2 -4
- data/lib/metal_archives/middleware/encoding.rb +2 -2
- data/lib/metal_archives/middleware/headers.rb +5 -5
- data/lib/metal_archives/middleware/rewrite_endpoint.rb +2 -2
- data/lib/metal_archives/models/artist.rb +40 -24
- data/lib/metal_archives/models/band.rb +47 -29
- data/lib/metal_archives/models/base_model.rb +64 -61
- data/lib/metal_archives/models/label.rb +11 -11
- data/lib/metal_archives/models/release.rb +17 -15
- data/lib/metal_archives/{utils/nil_date.rb → nil_date.rb} +10 -18
- data/lib/metal_archives/parsers/artist.rb +62 -31
- data/lib/metal_archives/parsers/band.rb +97 -74
- data/lib/metal_archives/parsers/label.rb +21 -21
- data/lib/metal_archives/parsers/parser.rb +23 -8
- data/lib/metal_archives/parsers/release.rb +77 -72
- data/lib/metal_archives/{utils/range.rb → range.rb} +5 -2
- data/lib/metal_archives/version.rb +12 -1
- data/metal_archives.env.example +7 -0
- data/metal_archives.gemspec +40 -28
- data/nginx/default.conf +60 -0
- metadata +126 -65
- data/.travis.yml +0 -12
- data/spec/configuration_spec.rb +0 -96
- data/spec/factories/artist_factory.rb +0 -37
- data/spec/factories/band_factory.rb +0 -60
- data/spec/factories/nil_date_factory.rb +0 -9
- data/spec/factories/range_factory.rb +0 -8
- data/spec/models/artist_spec.rb +0 -138
- data/spec/models/band_spec.rb +0 -164
- data/spec/models/base_model_spec.rb +0 -219
- data/spec/models/release_spec.rb +0 -133
- data/spec/parser_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -111
- data/spec/support/factory_girl.rb +0 -5
- data/spec/support/metal_archives.rb +0 -33
- data/spec/utils/collection_spec.rb +0 -72
- data/spec/utils/lru_cache_spec.rb +0 -53
- data/spec/utils/nil_date_spec.rb +0 -156
- data/spec/utils/range_spec.rb +0 -62
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "date"
|
4
|
+
require "nokogiri"
|
5
5
|
|
6
6
|
module MetalArchives
|
7
7
|
module Parsers
|
@@ -18,46 +18,46 @@ module MetalArchives
|
|
18
18
|
props = {}
|
19
19
|
doc = Nokogiri::HTML(response)
|
20
20
|
|
21
|
-
props[:name] = doc.css(
|
21
|
+
props[:name] = doc.css("#label_info .label_name").first.content
|
22
22
|
|
23
23
|
props[:contact] = []
|
24
|
-
doc.css(
|
24
|
+
doc.css("#label_contact a").each do |contact|
|
25
25
|
props[:contact] << {
|
26
|
-
:
|
27
|
-
:
|
26
|
+
title: contact.content,
|
27
|
+
content: contact.attr(:href),
|
28
28
|
}
|
29
29
|
end
|
30
30
|
|
31
|
-
doc.css(
|
32
|
-
dl.search(
|
31
|
+
doc.css("#label_info dl").each do |dl|
|
32
|
+
dl.search("dt").each do |dt|
|
33
33
|
content = sanitize(dt.next_element.content)
|
34
34
|
|
35
|
-
next if content ==
|
35
|
+
next if content == "N/A"
|
36
36
|
|
37
37
|
case sanitize(dt.content)
|
38
|
-
when
|
38
|
+
when "Address:"
|
39
39
|
props[:address] = content
|
40
|
-
when
|
41
|
-
props[:country] = ParserHelper.parse_country css(
|
42
|
-
when
|
40
|
+
when "Country:"
|
41
|
+
props[:country] = ParserHelper.parse_country css("a").first.content
|
42
|
+
when "Phone number:"
|
43
43
|
props[:phone] = content
|
44
|
-
when
|
45
|
-
props[:status] = content.downcase.tr(
|
46
|
-
when
|
44
|
+
when "Status:"
|
45
|
+
props[:status] = content.downcase.tr(" ", "_").to_sym
|
46
|
+
when "Specialised in:"
|
47
47
|
props[:specializations] = ParserHelper.parse_genre content
|
48
|
-
when
|
48
|
+
when "Founding date :"
|
49
49
|
begin
|
50
50
|
dof = Date.parse content
|
51
51
|
props[:date_founded] = NilDate.new dof.year, dof.month, dof.day
|
52
52
|
rescue ArgumentError => e
|
53
53
|
props[:date_founded] = NilDate.parse content
|
54
54
|
end
|
55
|
-
when
|
55
|
+
when "Sub-labels:"
|
56
56
|
# TODO
|
57
|
-
when
|
58
|
-
if content ==
|
57
|
+
when "Online shopping:"
|
58
|
+
if content == "Yes"
|
59
59
|
props[:online_shopping] = true
|
60
|
-
elsif content ==
|
60
|
+
elsif content == "No"
|
61
61
|
props[:online_shopping] = false
|
62
62
|
end
|
63
63
|
else
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "date"
|
4
|
+
require "countries"
|
5
5
|
|
6
6
|
module MetalArchives
|
7
7
|
##
|
@@ -28,7 +28,7 @@ module MetalArchives
|
|
28
28
|
# Return +String+
|
29
29
|
#
|
30
30
|
def sanitize(input)
|
31
|
-
input.gsub(/^"/,
|
31
|
+
input.gsub(/^"/, "").gsub(/"$/, "").strip
|
32
32
|
end
|
33
33
|
|
34
34
|
##
|
@@ -48,7 +48,7 @@ module MetalArchives
|
|
48
48
|
def parse_genre(input)
|
49
49
|
genres = []
|
50
50
|
# Split fields
|
51
|
-
input.split(
|
51
|
+
input.split(",").each do |genre|
|
52
52
|
##
|
53
53
|
# Start with a single empty genre string. Split the genre by spaces
|
54
54
|
# and process each component. If a component does not have a slash,
|
@@ -63,14 +63,14 @@ module MetalArchives
|
|
63
63
|
# 'Traditional Heavy', 'Traditional Power',
|
64
64
|
# 'Classical Heavy', 'Classical Power']
|
65
65
|
#
|
66
|
-
temp = [
|
67
|
-
genre.downcase.split.reject { |g| [
|
68
|
-
if g.include?
|
66
|
+
temp = [""]
|
67
|
+
genre.downcase.split.reject { |g| ["(early)", "(later)", "metal"].include? g }.each do |g|
|
68
|
+
if g.include? "/"
|
69
69
|
# Duplicate all WIP genres
|
70
70
|
temp2 = temp.dup
|
71
71
|
|
72
72
|
# Assign first and last components to temp and temp2 respectively
|
73
|
-
split = g.split
|
73
|
+
split = g.split "/"
|
74
74
|
temp.map! { |t| t.empty? ? split.first.capitalize : "#{t.capitalize} #{split.first.capitalize}" }
|
75
75
|
temp2.map! { |t| t.empty? ? split.last.capitalize : "#{t.capitalize} #{split.last.capitalize}" }
|
76
76
|
|
@@ -84,6 +84,21 @@ module MetalArchives
|
|
84
84
|
end
|
85
85
|
genres.uniq
|
86
86
|
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Parse year range
|
90
|
+
#
|
91
|
+
def parse_year_range(input)
|
92
|
+
r = input.split("-")
|
93
|
+
date_start = (r.first == "?" ? nil : NilDate.new(r.first.to_i))
|
94
|
+
date_end = if r.length > 1
|
95
|
+
(r.last == "?" || r.last == "present" ? nil : NilDate.new(r.last.to_i))
|
96
|
+
else
|
97
|
+
date_start.dup
|
98
|
+
end
|
99
|
+
|
100
|
+
MetalArchives::Range.new date_start, date_end
|
101
|
+
end
|
87
102
|
end
|
88
103
|
end
|
89
104
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "json"
|
4
|
+
require "date"
|
5
5
|
|
6
6
|
module MetalArchives
|
7
7
|
module Parsers
|
@@ -11,52 +11,56 @@ module MetalArchives
|
|
11
11
|
class Release < Parser # :nodoc:
|
12
12
|
class << self
|
13
13
|
TYPE_TO_QUERY = {
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
14
|
+
full_length: 1,
|
15
|
+
live: 2,
|
16
|
+
demo: 3,
|
17
|
+
single: 4,
|
18
|
+
ep: 5,
|
19
|
+
video: 6,
|
20
|
+
boxed_set: 7,
|
21
|
+
split: 8,
|
22
|
+
compilation: 10,
|
23
|
+
split_video: 12,
|
24
|
+
collaboration: 13,
|
25
25
|
}.freeze
|
26
26
|
|
27
27
|
TYPE_TO_SYM = {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
28
|
+
"Full-length" => :full_length,
|
29
|
+
"Live album" => :live,
|
30
|
+
"Demo" => :demo,
|
31
|
+
"Single" => :single,
|
32
|
+
"EP" => :ep,
|
33
|
+
"Video" => :video,
|
34
|
+
"Boxed set" => :boxed_set,
|
35
|
+
"Split" => :split,
|
36
|
+
"Compilation" => :compilation,
|
37
|
+
"Split video" => :split_video,
|
38
|
+
"Collaboration" => :collaboration,
|
39
39
|
}.freeze
|
40
40
|
|
41
41
|
FORMAT_TO_QUERY = {
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
42
|
+
cd: "CD",
|
43
|
+
cassette: "Cassette",
|
44
|
+
vinyl: "Vinyl*",
|
45
|
+
vhs: "VHS",
|
46
|
+
dvd: "DVD",
|
47
|
+
"2dvd": "2DVD",
|
48
|
+
digital: "Digital",
|
49
|
+
blu_ray: "Blu-ray*",
|
50
|
+
other: "Other",
|
51
|
+
unknown: "Unknown",
|
50
52
|
}.freeze
|
51
53
|
|
52
54
|
FORMAT_TO_SYM = {
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
"CD" => :cd,
|
56
|
+
"Cassette" => :cassette,
|
57
|
+
"VHS" => :vhs,
|
58
|
+
"DVD" => :dvd,
|
59
|
+
"2DVD" => :"2dvd",
|
60
|
+
"Digital" => :digital,
|
61
|
+
"Other" => :other,
|
62
|
+
"Unknown" => :unknown,
|
63
|
+
}.freeze
|
60
64
|
|
61
65
|
##
|
62
66
|
# Map attributes to MA attributes
|
@@ -68,23 +72,23 @@ module MetalArchives
|
|
68
72
|
#
|
69
73
|
def map_params(query)
|
70
74
|
params = {
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
75
|
+
bandName: query[:band_name] || "",
|
76
|
+
releaseTitle: query[:title] || "",
|
77
|
+
releaseYearFrom: query[:from_year] || "",
|
78
|
+
releaseMonthFrom: query[:from_month] || "",
|
79
|
+
releaseYearTo: query[:to_year] || "",
|
80
|
+
releaseMonthTo: query[:to_month] || "",
|
81
|
+
country: map_countries(query[:country]) || "",
|
82
|
+
location: query[:location] || "",
|
83
|
+
releaseLabelName: query[:label_name] || "",
|
84
|
+
releaseCatalogNumber: query[:catalog_id] || "",
|
85
|
+
releaseIdentifiers: query[:identifier] || "",
|
86
|
+
releaseRecordingInfo: query[:recording_info] || "",
|
87
|
+
releaseDescription: query[:version_description] || "",
|
88
|
+
releaseNotes: query[:notes] || "",
|
89
|
+
genre: query[:genre] || "",
|
90
|
+
releaseType: map_types(query[:types]),
|
91
|
+
releaseFormat: map_formats(query[:formats]),
|
88
92
|
}
|
89
93
|
|
90
94
|
params
|
@@ -102,38 +106,38 @@ module MetalArchives
|
|
102
106
|
props = {}
|
103
107
|
doc = Nokogiri::HTML response
|
104
108
|
|
105
|
-
props[:title] = sanitize doc.css(
|
109
|
+
props[:title] = sanitize doc.css("#album_info .album_name a").first.content
|
106
110
|
|
107
|
-
doc.css(
|
108
|
-
dl.search(
|
111
|
+
doc.css("#album_info dl").each do |dl|
|
112
|
+
dl.search("dt").each do |dt|
|
109
113
|
content = sanitize dt.next_element.content
|
110
114
|
|
111
|
-
next if content ==
|
115
|
+
next if content == "N/A"
|
112
116
|
|
113
117
|
case sanitize(dt.content)
|
114
|
-
when
|
118
|
+
when "Type:"
|
115
119
|
props[:type] = map_type content
|
116
|
-
when
|
120
|
+
when "Release date:"
|
117
121
|
begin
|
118
122
|
props[:date_released] = NilDate.parse content
|
119
123
|
rescue MetalArchives::Errors::ArgumentError => e
|
120
124
|
dr = Date.parse content
|
121
125
|
props[:date_released] = NilDate.new dr.year, dr.month, dr.day
|
122
126
|
end
|
123
|
-
when
|
127
|
+
when "Catalog ID:"
|
124
128
|
props[:catalog_id] = content
|
125
|
-
when
|
129
|
+
when "Identifier:"
|
126
130
|
props[:identifier] = content
|
127
|
-
when
|
131
|
+
when "Version desc.:"
|
128
132
|
props[:version_description] = content
|
129
|
-
when
|
133
|
+
when "Label:"
|
130
134
|
# TODO: label
|
131
|
-
when
|
135
|
+
when "Format:"
|
132
136
|
props[:format] = map_format content
|
133
|
-
when
|
137
|
+
when "Limitation:"
|
134
138
|
props[:limitation] = content.to_i
|
135
|
-
when
|
136
|
-
next if content ==
|
139
|
+
when "Reviews:"
|
140
|
+
next if content == "None yet"
|
137
141
|
# TODO: reviews
|
138
142
|
else
|
139
143
|
raise MetalArchives::Errors::ParserError, "Unknown token: #{dt.content}"
|
@@ -142,7 +146,7 @@ module MetalArchives
|
|
142
146
|
end
|
143
147
|
|
144
148
|
props
|
145
|
-
rescue => e
|
149
|
+
rescue StandardError => e
|
146
150
|
e.backtrace.each { |b| MetalArchives.config.logger.error b }
|
147
151
|
raise Errors::ParserError, e
|
148
152
|
end
|
@@ -158,7 +162,7 @@ module MetalArchives
|
|
158
162
|
# +Array+ containing one or more +String+s
|
159
163
|
#
|
160
164
|
def map_countries(countries)
|
161
|
-
countries
|
165
|
+
countries&.map(&:alpha2)
|
162
166
|
end
|
163
167
|
|
164
168
|
##
|
@@ -220,6 +224,7 @@ module MetalArchives
|
|
220
224
|
# Returns +Symbol+, see rdoc-ref:Release.format
|
221
225
|
#
|
222
226
|
def map_format(format)
|
227
|
+
return :cd if format =~ /CD/
|
223
228
|
return :vinyl if format =~ /[Vv]inyl/
|
224
229
|
return :blu_ray if format =~ /[Bb]lu.?[Rr]ay/
|
225
230
|
|
@@ -34,14 +34,14 @@ module MetalArchives
|
|
34
34
|
# Whether start of range is present
|
35
35
|
#
|
36
36
|
def begin?
|
37
|
-
|
37
|
+
!@begin.nil?
|
38
38
|
end
|
39
39
|
|
40
40
|
##
|
41
41
|
# Whether end of range is present
|
42
42
|
#
|
43
43
|
def end?
|
44
|
-
|
44
|
+
!@end.nil?
|
45
45
|
end
|
46
46
|
|
47
47
|
##
|
@@ -59,6 +59,9 @@ module MetalArchives
|
|
59
59
|
# Compare begin if end is equal
|
60
60
|
return comp_begin if comp_begin.zero?
|
61
61
|
|
62
|
+
return nil unless self.begin.is_a?(Integer) && self.end.is_a?(Integer)
|
63
|
+
return nil unless other.begin.is_a?(Integer) && other.end.is_a?(Integer)
|
64
|
+
|
62
65
|
# Compare actual range
|
63
66
|
(self.end - self.begin) <=> (other.end - other.begin)
|
64
67
|
end
|
@@ -4,5 +4,16 @@ module MetalArchives
|
|
4
4
|
##
|
5
5
|
# MetalArchives API version
|
6
6
|
#
|
7
|
-
|
7
|
+
module Version
|
8
|
+
MAJOR = 2
|
9
|
+
MINOR = 2
|
10
|
+
PATCH = 3
|
11
|
+
PRE = nil
|
12
|
+
|
13
|
+
VERSION = [MAJOR, MINOR, PATCH].compact.join(".")
|
14
|
+
|
15
|
+
STRING = [VERSION, PRE].compact.join("-")
|
16
|
+
end
|
17
|
+
|
18
|
+
VERSION = MetalArchives::Version::STRING
|
8
19
|
end
|
data/metal_archives.gemspec
CHANGED
@@ -1,36 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require_relative "lib/metal_archives/version"
|
5
4
|
|
6
|
-
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "metal_archives"
|
7
|
+
spec.version = MetalArchives::VERSION
|
8
|
+
spec.authors = ["Florian Dejonckheere"]
|
9
|
+
spec.email = ["florian@floriandejonckheere.be"]
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
spec.summary = "Metal Archives Ruby API"
|
12
|
+
spec.description = "Ruby API layer that transparently queries, scrapes and caches Metal Archives' website"
|
13
|
+
spec.homepage = "http://github.com/floriandejonckheere/metal_archives"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.8")
|
13
16
|
|
14
|
-
|
15
|
-
gem.executables = []
|
16
|
-
gem.test_files = gem.files.grep(%r{^spec/})
|
17
|
-
gem.name = 'metal_archives'
|
18
|
-
gem.require_paths = %w[lib]
|
19
|
-
gem.version = MetalArchives::VERSION
|
20
|
-
gem.license = 'MIT'
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/floriandejonckheere/metal_archives"
|
21
18
|
|
22
|
-
gem.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
gem.add_development_dependency 'debase'
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "bin"
|
25
|
+
spec.executables = spec.files.grep(%r(^bin/)) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
31
27
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
spec.add_runtime_dependency "countries"
|
29
|
+
spec.add_runtime_dependency "faraday"
|
30
|
+
spec.add_runtime_dependency "faraday_throttler"
|
31
|
+
spec.add_runtime_dependency "nokogiri"
|
32
|
+
spec.add_runtime_dependency "zeitwerk"
|
33
|
+
|
34
|
+
spec.add_development_dependency "byebug"
|
35
|
+
spec.add_development_dependency "debase"
|
36
|
+
spec.add_development_dependency "factory_bot"
|
37
|
+
spec.add_development_dependency "fasterer"
|
38
|
+
spec.add_development_dependency "ffaker"
|
39
|
+
spec.add_development_dependency "flay"
|
40
|
+
spec.add_development_dependency "overcommit"
|
41
|
+
spec.add_development_dependency "pronto"
|
42
|
+
spec.add_development_dependency "rake"
|
43
|
+
spec.add_development_dependency "rdoc"
|
44
|
+
spec.add_development_dependency "reek"
|
45
|
+
spec.add_development_dependency "rspec"
|
46
|
+
spec.add_development_dependency "rubocop"
|
47
|
+
spec.add_development_dependency "webmock"
|
36
48
|
end
|