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?
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								lib/parsers/artist.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/parsers/artist.rb
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										30
									
								
								lib/parsers/base.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/parsers/base.rb
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										19
									
								
								lib/parsers/release_group.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/parsers/release_group.rb
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user