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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +46 -0
  3. data/.github/workflows/release.yml +69 -0
  4. data/.gitignore +6 -6
  5. data/.overcommit.yml +35 -0
  6. data/.rspec +1 -0
  7. data/.rubocop.yml +54 -8
  8. data/.rubocop_todo.yml +92 -0
  9. data/CHANGELOG.md +22 -0
  10. data/Gemfile +1 -1
  11. data/LICENSE.md +1 -1
  12. data/README.md +35 -64
  13. data/Rakefile +8 -7
  14. data/bin/console +41 -0
  15. data/bin/setup +8 -0
  16. data/docker-compose.yml +14 -0
  17. data/lib/metal_archives.rb +48 -29
  18. data/lib/metal_archives/{utils/collection.rb → collection.rb} +0 -0
  19. data/lib/metal_archives/configuration.rb +9 -33
  20. data/lib/metal_archives/{error.rb → errors.rb} +0 -0
  21. data/lib/metal_archives/http_client.rb +13 -8
  22. data/lib/metal_archives/{utils/lru_cache.rb → lru_cache.rb} +0 -0
  23. data/lib/metal_archives/middleware/cache_check.rb +2 -4
  24. data/lib/metal_archives/middleware/encoding.rb +2 -2
  25. data/lib/metal_archives/middleware/headers.rb +5 -5
  26. data/lib/metal_archives/middleware/rewrite_endpoint.rb +2 -2
  27. data/lib/metal_archives/models/artist.rb +40 -24
  28. data/lib/metal_archives/models/band.rb +47 -29
  29. data/lib/metal_archives/models/base_model.rb +64 -61
  30. data/lib/metal_archives/models/label.rb +11 -11
  31. data/lib/metal_archives/models/release.rb +17 -15
  32. data/lib/metal_archives/{utils/nil_date.rb → nil_date.rb} +10 -18
  33. data/lib/metal_archives/parsers/artist.rb +62 -31
  34. data/lib/metal_archives/parsers/band.rb +97 -74
  35. data/lib/metal_archives/parsers/label.rb +21 -21
  36. data/lib/metal_archives/parsers/parser.rb +23 -8
  37. data/lib/metal_archives/parsers/release.rb +77 -72
  38. data/lib/metal_archives/{utils/range.rb → range.rb} +5 -2
  39. data/lib/metal_archives/version.rb +12 -1
  40. data/metal_archives.env.example +7 -0
  41. data/metal_archives.gemspec +40 -28
  42. data/nginx/default.conf +60 -0
  43. metadata +126 -65
  44. data/.travis.yml +0 -12
  45. data/spec/configuration_spec.rb +0 -96
  46. data/spec/factories/artist_factory.rb +0 -37
  47. data/spec/factories/band_factory.rb +0 -60
  48. data/spec/factories/nil_date_factory.rb +0 -9
  49. data/spec/factories/range_factory.rb +0 -8
  50. data/spec/models/artist_spec.rb +0 -138
  51. data/spec/models/band_spec.rb +0 -164
  52. data/spec/models/base_model_spec.rb +0 -219
  53. data/spec/models/release_spec.rb +0 -133
  54. data/spec/parser_spec.rb +0 -19
  55. data/spec/spec_helper.rb +0 -111
  56. data/spec/support/factory_girl.rb +0 -5
  57. data/spec/support/metal_archives.rb +0 -33
  58. data/spec/utils/collection_spec.rb +0 -72
  59. data/spec/utils/lru_cache_spec.rb +0 -53
  60. data/spec/utils/nil_date_spec.rb +0 -156
  61. data/spec/utils/range_spec.rb +0 -62
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'date'
4
- require 'nokogiri'
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('#label_info .label_name').first.content
21
+ props[:name] = doc.css("#label_info .label_name").first.content
22
22
 
23
23
  props[:contact] = []
24
- doc.css('#label_contact a').each do |contact|
24
+ doc.css("#label_contact a").each do |contact|
25
25
  props[:contact] << {
26
- :title => contact.content,
27
- :content => contact.attr(:href)
26
+ title: contact.content,
27
+ content: contact.attr(:href),
28
28
  }
29
29
  end
30
30
 
31
- doc.css('#label_info dl').each do |dl|
32
- dl.search('dt').each do |dt|
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 == 'N/A'
35
+ next if content == "N/A"
36
36
 
37
37
  case sanitize(dt.content)
38
- when 'Address:'
38
+ when "Address:"
39
39
  props[:address] = content
40
- when 'Country:'
41
- props[:country] = ParserHelper.parse_country css('a').first.content
42
- when 'Phone number:'
40
+ when "Country:"
41
+ props[:country] = ParserHelper.parse_country css("a").first.content
42
+ when "Phone number:"
43
43
  props[:phone] = content
44
- when 'Status:'
45
- props[:status] = content.downcase.tr(' ', '_').to_sym
46
- when 'Specialised in:'
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 'Founding date :'
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 'Sub-labels:'
55
+ when "Sub-labels:"
56
56
  # TODO
57
- when 'Online shopping:'
58
- if content == 'Yes'
57
+ when "Online shopping:"
58
+ if content == "Yes"
59
59
  props[:online_shopping] = true
60
- elsif content == 'No'
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 'date'
4
- require 'countries'
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(/^"/, '').gsub(/"$/, '').strip
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(',').each do |genre|
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| ['(early)', '(later)', 'metal'].include? g }.each do |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 'json'
4
- require 'date'
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
- :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
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
- '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
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
- :cd => 'CD',
43
- :cassette => 'Cassette',
44
- :vinyl => 'Vinyl*',
45
- :vhs => 'VHS',
46
- :dvd => 'DVD',
47
- :digital => 'Digital',
48
- :blu_ray => 'Blu-ray*',
49
- :other => 'Other'
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
- 'CD' => :cd,
54
- 'Cassette' => :cassette,
55
- 'VHS' => :vhs,
56
- 'DVD' => :dvd,
57
- 'Digital' => :digital,
58
- 'Other' => :other
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
- :bandName => query[:band_name] || '',
72
- :releaseTitle => query[:title] || '',
73
- :releaseYearFrom => query[:from_year] || '',
74
- :releaseMonthFrom => query[:from_month] || '',
75
- :releaseYearTo => query[:to_year] || '',
76
- :releaseMonthTo => query[:to_month] || '',
77
- :country => map_countries(query[:country]) || '',
78
- :location => query[:location] || '',
79
- :releaseLabelName => query[:label_name] || '',
80
- :releaseCatalogNumber => query[:catalog_id] || '',
81
- :releaseIdentifiers => query[:identifier] || '',
82
- :releaseRecordingInfo => query[:recording_info] || '',
83
- :releaseDescription => query[:version_description] || '',
84
- :releaseNotes => query[:notes] || '',
85
- :genre => query[:genre] || '',
86
- :releaseType => map_types(query[:types]),
87
- :releaseFormat => map_formats(query[:formats])
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('#album_info .album_name a').first.content
109
+ props[:title] = sanitize doc.css("#album_info .album_name a").first.content
106
110
 
107
- doc.css('#album_info dl').each do |dl|
108
- dl.search('dt').each do |dt|
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 == 'N/A'
115
+ next if content == "N/A"
112
116
 
113
117
  case sanitize(dt.content)
114
- when 'Type:'
118
+ when "Type:"
115
119
  props[:type] = map_type content
116
- when 'Release date:'
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 'Catalog ID:'
127
+ when "Catalog ID:"
124
128
  props[:catalog_id] = content
125
- when 'Identifier:'
129
+ when "Identifier:"
126
130
  props[:identifier] = content
127
- when 'Version desc.:'
131
+ when "Version desc.:"
128
132
  props[:version_description] = content
129
- when 'Label:'
133
+ when "Label:"
130
134
  # TODO: label
131
- when 'Format:'
135
+ when "Format:"
132
136
  props[:format] = map_format content
133
- when 'Limitation:'
137
+ when "Limitation:"
134
138
  props[:limitation] = content.to_i
135
- when 'Reviews:'
136
- next if content == 'None yet'
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 && countries.map { |c| c.alpha2 }
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
- !!@begin
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
- !!@end
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
- VERSION = '2.2.0'
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
@@ -0,0 +1,7 @@
1
+ ## Metal Archives endpoint
2
+ # MA_ENDPOINT=https://www.metal-archives.com/
3
+ # MA_ENDPOINT_USER=my_user
4
+ # MA_ENDPOINT_PASSWORD=my_password
5
+
6
+ ## WebMock
7
+ # WEBMOCK_ALLOW_HOST=metal-archives.com
@@ -1,36 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require_relative "lib/metal_archives/version"
5
4
 
6
- require 'metal_archives/version'
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
- Gem::Specification.new do |gem|
9
- gem.authors = ['Florian Dejonckheere']
10
- gem.email = ['florian@floriandejonckheere.be']
11
- gem.summary = 'Metal Archives Ruby API'
12
- gem.homepage = 'http://github.com/floriandejonckheere/metal_archives'
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
- gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
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.add_development_dependency 'byebug'
23
- gem.add_development_dependency 'rake'
24
- gem.add_development_dependency 'rdoc'
25
- gem.add_development_dependency 'rspec'
26
- gem.add_development_dependency 'factory_girl'
27
- gem.add_development_dependency 'faker'
28
- gem.add_development_dependency 'rubocop'
29
- gem.add_development_dependency 'coveralls'
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
- gem.add_dependency 'faraday'
33
- gem.add_dependency 'faraday_throttler'
34
- gem.add_dependency 'nokogiri'
35
- gem.add_dependency 'countries'
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