Refactoring Artist model
This commit is contained in:
parent
23d27756b8
commit
ab78050fec
5
Rakefile
5
Rakefile
|
@ -6,3 +6,8 @@ RSpec::Core::RakeTask.new("spec")
|
|||
|
||||
task :default => :spec
|
||||
task :test => :spec
|
||||
|
||||
desc "Open an irb session preloaded with this library"
|
||||
task :console do
|
||||
sh "irb -rubygems -I lib -r musicbrainz.rb"
|
||||
end
|
||||
|
|
|
@ -4,18 +4,25 @@ require "socket"
|
|||
require "nokogiri"
|
||||
require "cgi"
|
||||
|
||||
module MusicBrainz
|
||||
module Tools
|
||||
end
|
||||
module Parsers
|
||||
end
|
||||
end
|
||||
|
||||
require "version"
|
||||
require "deprecated"
|
||||
|
||||
module MusicBrainz
|
||||
autoload :Base, "musicbrainz/base"
|
||||
autoload :Artist, "musicbrainz/artist"
|
||||
autoload :ReleaseGroup, "musicbrainz/release_group"
|
||||
autoload :Release, "musicbrainz/release"
|
||||
autoload :Track, "musicbrainz/track"
|
||||
require "musicbrainz/base"
|
||||
require "musicbrainz/artist"
|
||||
require "musicbrainz/release_group"
|
||||
require "musicbrainz/release"
|
||||
require "musicbrainz/track"
|
||||
|
||||
module Tools
|
||||
autoload :Cache, "tools/cache"
|
||||
autoload :Proxy, "tools/proxy"
|
||||
end
|
||||
end
|
||||
require "tools/cache"
|
||||
require "tools/proxy"
|
||||
|
||||
require "parsers/base"
|
||||
require "parsers/artist"
|
||||
require "parsers/release_group"
|
||||
|
|
|
@ -1,81 +1,78 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
module MusicBrainz
|
||||
class Artist < MusicBrainz::Base
|
||||
attr_accessor :id, :type, :name, :country, :date_begin, :date_end, :urls
|
||||
@release_groups
|
||||
class Artist < Base
|
||||
|
||||
field :id, String
|
||||
field :type, String
|
||||
field :name, String
|
||||
field :country, String
|
||||
field :date_begin, Time
|
||||
field :date_end, Time
|
||||
field :urls, Hash
|
||||
|
||||
def release_groups
|
||||
if @release_groups.nil? and not self.id.nil?
|
||||
@release_groups = []
|
||||
Nokogiri::XML(self.class.load(:release_group, :artist => self.id)).css('release-group').each do |rg|
|
||||
@release_groups << MusicBrainz::ReleaseGroup.parse_xml(rg)
|
||||
end
|
||||
@release_groups ||= nil
|
||||
if @release_groups.nil? and !id.nil?
|
||||
@release_groups = self.class.load({
|
||||
:parser => :artist_release_groups,
|
||||
:create_models => MusicBrainz::ReleaseGroup
|
||||
}, {
|
||||
:resource => :release_group,
|
||||
:artist => id
|
||||
})
|
||||
@release_groups.sort!{ |a, b| a.first_release_date <=> b.first_release_date }
|
||||
end
|
||||
@release_groups.sort{ |a, b| a.first_release_date <=> b.first_release_date }
|
||||
@release_groups
|
||||
end
|
||||
|
||||
def self.find mbid
|
||||
res = self.load :artist, :id => mbid, :inc => [:url_rels]
|
||||
return nil if res.nil?
|
||||
@artist = self.parse_xml(Nokogiri::XML(res))
|
||||
end
|
||||
|
||||
def self.parse_xml xml
|
||||
@artist = MusicBrainz::Artist.new
|
||||
@artist.id = self.safe_get_attr(xml, 'artist', 'id')
|
||||
@artist.type = self.safe_get_attr(xml, 'artist', 'type')
|
||||
@artist.name = self.safe_get_value(xml, 'artist > name').gsub(/[`’]/, "'")
|
||||
@artist.country = self.safe_get_value(xml, 'artist > country')
|
||||
@artist.date_begin = self.safe_get_value(xml, 'artist > life-span > begin')
|
||||
@artist.date_end = self.safe_get_value(xml, 'artist > life-span > end')
|
||||
@artist.urls = {}
|
||||
xml.css('relation-list[target-type="url"] > relation').each do |rel|
|
||||
@artist.urls[rel.attr('type').downcase.split(' ').join('_').to_sym] = rel.css('target').text
|
||||
class << self
|
||||
def find(mbid)
|
||||
load({
|
||||
:parser => :artist_model,
|
||||
:create_model => MusicBrainz::Artist
|
||||
}, {
|
||||
:resource => :artist,
|
||||
:id => mbid,
|
||||
:inc => [:url_rels]
|
||||
})
|
||||
end
|
||||
@artist
|
||||
end
|
||||
|
||||
def self.discography mbid
|
||||
artist = self.find(mbid)
|
||||
artist.release_groups.each {|rg| rg.releases.each {|r| r.tracks } }
|
||||
artist
|
||||
end
|
||||
def search(name)
|
||||
artists = load({
|
||||
:parser => :artist_search
|
||||
}, {
|
||||
:resource => :artist,
|
||||
:query => CGI.escape(name).gsub(/\!/, '\!') + '~',
|
||||
:limit => 50
|
||||
})
|
||||
|
||||
def self.find_by_name name
|
||||
matches = self.search name
|
||||
matches.length.zero? ? nil : self.find(matches.first[:mbid])
|
||||
end
|
||||
|
||||
def self.search name
|
||||
artists = []
|
||||
xml = Nokogiri::XML(self.load(:artist, :query => CGI.escape(name).gsub(/\!/, '\!') + '~', :limit => 50))
|
||||
xml.css('artist-list > artist').each do |a|
|
||||
artist = {
|
||||
:name => a.first_element_child.text.gsub(/[`’]/, "'"),
|
||||
:sort_name => self.safe_get_value(a, 'sort-name').gsub(/[`’]/, "'"),
|
||||
:weight => 0,
|
||||
:desc => self.safe_get_value(a, 'disambiguation'),
|
||||
:type => self.safe_get_attr(a, nil, 'type'),
|
||||
:mbid => self.safe_get_attr(a, nil, 'id')
|
||||
artists.each { |artist|
|
||||
if artist[:name].downcase == name.downcase
|
||||
artist[:weight] += 80
|
||||
elsif artist[:name].downcase.gsub(/\s/, "") == name.downcase.gsub(/\s/, "")
|
||||
artist[:weight] += 25
|
||||
elsif artist[:aliases].include? name
|
||||
artist[:weight] += 20
|
||||
elsif artist[:aliases].map { |item| item.downcase }.include?(name.downcase)
|
||||
artist[:weight] += 10
|
||||
elsif artist[:aliases].map { |item| item.downcase.gsub(/\s/, "") }.include?(name.downcase.gsub(/\s/, ""))
|
||||
artist[:weight] += 5
|
||||
end
|
||||
}
|
||||
aliases = a.css('alias-list > alias').map{ |item| item.text }
|
||||
if artist[:name] == name
|
||||
artist[:weight] += 100
|
||||
elsif artist[:name].downcase == name.downcase
|
||||
artist[:weight] += 50
|
||||
elsif artist[:name].downcase.gsub(/\s/, '') == name.downcase.gsub(/\s/, '')
|
||||
artist[:weight] += 25
|
||||
elsif aliases.include? name
|
||||
artist[:weight] += 20
|
||||
elsif aliases.map{ |item| item.downcase }.include? name.downcase
|
||||
artist[:weight] += 10
|
||||
elsif aliases.map{ |item| item.downcase.gsub(/\s/, '') }.include? name.downcase.gsub(/\s/, '')
|
||||
artist[:weight] += 5
|
||||
end
|
||||
artists << artist
|
||||
artists.sort{ |a, b| b[:weight] <=> a[:weight] }.take(10)
|
||||
end
|
||||
|
||||
def discography(mbid)
|
||||
artist = find(mbid)
|
||||
artist.release_groups.each { |rg| rg.releases.each { |r| r.tracks } }
|
||||
artist
|
||||
end
|
||||
|
||||
def find_by_name(name)
|
||||
matches = search(name)
|
||||
matches.length.zero? ? nil : find(matches.first[:mbid])
|
||||
end
|
||||
artists.sort{ |a, b| b[:weight] <=> a[:weight] }.take(10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,71 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
module MusicBrainz
|
||||
class Base
|
||||
def self.safe_get_attr(xml, path, name)
|
||||
node = path.nil? ? xml : (xml.css(path).first unless xml.css(path).empty?)
|
||||
node.attr(name) unless node.nil? or node.attr(name).nil?
|
||||
class << self
|
||||
def field(name, type)
|
||||
@fields ||= {}
|
||||
@fields[name] = type
|
||||
|
||||
define_method(name) {
|
||||
instance_variable_get("@#{name}")
|
||||
}
|
||||
define_method("#{name}=") { |val|
|
||||
instance_variable_set("@#{name}", validate_type(val, type))
|
||||
}
|
||||
end
|
||||
|
||||
def load(params, query)
|
||||
parser = MusicBrainz::Parsers.get_by_name(params[:parser])
|
||||
xml = MusicBrainz::Tools::Proxy.load(query)
|
||||
result = parser[:const].send(parser[:method], Nokogiri::XML(xml))
|
||||
if params[:create_model]
|
||||
result_model = params[:create_model].new
|
||||
result.each { |field, value|
|
||||
result_model.send("#{field}=".to_sym, value)
|
||||
}
|
||||
result_model
|
||||
elsif params[:create_models]
|
||||
result_models = []
|
||||
result.each { |item|
|
||||
result_model = params[:create_models].new
|
||||
item.each { |field, value|
|
||||
result_model.send("#{field}=".to_sym, value)
|
||||
}
|
||||
result_models << result_model
|
||||
}
|
||||
result_models
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.safe_get_value(xml, path)
|
||||
xml.css(path).first.text unless xml.css(path).empty?
|
||||
def initialize
|
||||
self.class.instance_variable_get("@fields").each { |name, type|
|
||||
instance_variable_set("@#{name}", nil)
|
||||
}
|
||||
end
|
||||
|
||||
def self.load(*args)
|
||||
MusicBrainz::Tools::Proxy.load(*args)
|
||||
def validate_type(val, type)
|
||||
if type == Integer
|
||||
val.to_i
|
||||
elsif type == Float
|
||||
val.to_f
|
||||
elsif type == String
|
||||
val.to_s
|
||||
elsif type == Time
|
||||
if val.nil? or val == ""
|
||||
val = "2030-12-31"
|
||||
elsif val.split("-").length == 1
|
||||
val << "-12-31"
|
||||
elsif val.split("-").length == 2
|
||||
val << "-31"
|
||||
end
|
||||
Time.utc(*val.split("-"))
|
||||
else
|
||||
val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
module MusicBrainz
|
||||
class ReleaseGroup < MusicBrainz::Base
|
||||
attr_accessor :id, :type, :title, :disambiguation, :first_release_date
|
||||
@releases
|
||||
|
||||
field :id, String
|
||||
field :type, String
|
||||
field :title, String
|
||||
field :disambiguation, String
|
||||
field :first_release_date, Time
|
||||
|
||||
def releases
|
||||
if @releases.nil? and not self.id.nil?
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
module MusicBrainz
|
||||
module Parsers
|
||||
class Artist < Base
|
||||
class << self
|
||||
def model(xml)
|
||||
res = {
|
||||
:id => safe_get_attr(xml, "artist", "id"),
|
||||
:type => safe_get_attr(xml, "artist", "type"),
|
||||
:name => safe_get_value(xml, "artist > name").gsub(/[`’]/, "'"),
|
||||
:country => safe_get_value(xml, "artist > country"),
|
||||
:date_begin => safe_get_value(xml, "artist > life-span > begin"),
|
||||
:date_end => safe_get_value(xml, "artist > life-span > end"),
|
||||
:urls => {}
|
||||
}
|
||||
xml.css("relation-list[target-type='url'] > relation").each { |rel|
|
||||
res[:urls][rel.attr("type").downcase.split(" ").join("_").to_sym] = rel.css("target").text
|
||||
}
|
||||
res
|
||||
end
|
||||
|
||||
def search(xml)
|
||||
artists = []
|
||||
xml.css("artist-list > artist").each do |a|
|
||||
artists << {
|
||||
:name => a.first_element_child.text.gsub(/[`’]/, "'"),
|
||||
:sort_name => self.safe_get_value(a, "sort-name").gsub(/[`’]/, "'"),
|
||||
:weight => 0,
|
||||
:desc => self.safe_get_value(a, "disambiguation"),
|
||||
:type => self.safe_get_attr(a, nil, "type"),
|
||||
:mbid => self.safe_get_attr(a, nil, "id"),
|
||||
:aliases => a.css("alias-list > alias").map { |item| item.text }
|
||||
}
|
||||
end
|
||||
artists
|
||||
end
|
||||
|
||||
def release_groups(xml)
|
||||
release_groups = []
|
||||
xml.css("release-group").each do |rg|
|
||||
release_groups << MusicBrainz::Parsers::ReleaseGroup.model(rg)
|
||||
end
|
||||
release_groups
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
module MusicBrainz
|
||||
module Parsers
|
||||
class << self
|
||||
def get_by_name(name)
|
||||
case name
|
||||
when :artist_model
|
||||
return { :const => MusicBrainz::Parsers::Artist, :method => :model }
|
||||
when :artist_search
|
||||
return { :const => MusicBrainz::Parsers::Artist, :method => :search }
|
||||
when :artist_release_groups
|
||||
return { :const => MusicBrainz::Parsers::Artist, :method => :release_groups }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Base
|
||||
class << self
|
||||
def safe_get_attr(xml, path, name)
|
||||
node = path.nil? ? xml : (xml.css(path).first unless xml.css(path).empty?)
|
||||
node.attr(name) unless node.nil? or node.attr(name).nil?
|
||||
end
|
||||
|
||||
def safe_get_value(xml, path)
|
||||
xml.css(path).first.text unless xml.css(path).empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
|
||||
module MusicBrainz
|
||||
module Parsers
|
||||
class ReleaseGroup < Base
|
||||
class << self
|
||||
def model(xml)
|
||||
{
|
||||
:id => safe_get_attr(xml, nil, "id"),
|
||||
:type => safe_get_attr(xml, nil, "type"),
|
||||
:title => safe_get_value(xml, "title"),
|
||||
:disambiguation => safe_get_value(xml, "disambiguation"),
|
||||
:first_release_date => safe_get_value(xml, "first-release-date")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,8 +21,8 @@ module MusicBrainz
|
|||
@@tries_limit = num.to_i
|
||||
end
|
||||
|
||||
def self.load(resourse, params = {})
|
||||
url = WEB_SERVICE_URL + resourse.to_s.gsub('_', '-') + '/' + (params[:id].to_s || '')
|
||||
def self.load(params = {})
|
||||
url = WEB_SERVICE_URL + params[:resource].to_s.gsub('_', '-') + '/' + (params[:id].to_s || '')
|
||||
params.delete(:id) unless params[:id].nil?
|
||||
url << '?' + params.map{ |k, v|
|
||||
k = k.to_s.gsub('_', '-')
|
||||
|
|
|
@ -21,7 +21,7 @@ describe MusicBrainz::Artist do
|
|||
it "finds name first than alias" do
|
||||
matches = MusicBrainz::Artist.search('Chris Martin')
|
||||
matches.length.should be > 0
|
||||
matches.first[:name].should == "Chris Martin"
|
||||
matches.first[:mbid].should == "af2ab893-3212-4226-9e73-73a1660b6952"
|
||||
end
|
||||
|
||||
it "gets correct result by name" do
|
||||
|
@ -35,7 +35,7 @@ describe MusicBrainz::Artist do
|
|||
artist.type.should == "Group"
|
||||
artist.name.should == "Kasabian"
|
||||
artist.country.should == "GB"
|
||||
artist.date_begin.should == "1999"
|
||||
artist.date_begin.year.should == 1999
|
||||
end
|
||||
|
||||
it "gets correct artist's release groups" do
|
||||
|
|
Loading…
Reference in New Issue