Stability improvements, new release
This commit is contained in:
+36
-20
@@ -1,5 +1,9 @@
|
||||
module MusicBrainz
|
||||
module Client
|
||||
class Client
|
||||
include ClientModules::TransparentProxy
|
||||
include ClientModules::FailsafeProxy
|
||||
include ClientModules::CachingProxy
|
||||
|
||||
def http
|
||||
@faraday ||= Faraday.new do |f|
|
||||
f.request :url_encoded # form-encode POST params
|
||||
@@ -11,14 +15,18 @@ module MusicBrainz
|
||||
def load(resource, query, params)
|
||||
raise Exception.new("You need to run MusicBrainz.configure before querying") if MusicBrainz.config.nil?
|
||||
|
||||
response = contents_of(build_url(resource, query))
|
||||
xml = Nokogiri::XML.parse(response).remove_namespaces!.xpath('/metadata')
|
||||
data = params[:binding].parse(xml)
|
||||
url = build_url(resource, query)
|
||||
response = get_contents(url)
|
||||
|
||||
return nil if response[:status] != 200
|
||||
|
||||
xml = Nokogiri::XML.parse(response[:body]).remove_namespaces!.xpath('/metadata')
|
||||
data = binding_class_for(params[:binding]).parse(xml)
|
||||
|
||||
if params[:create_model]
|
||||
params[:create_model].new(data)
|
||||
model_class_for(params[:create_model]).new(data)
|
||||
elsif params[:create_models]
|
||||
models = data.map{ |item| params[:create_models].new(item) }
|
||||
models = data.map{ |item| model_class_for(params[:create_models]).new(item) }
|
||||
models.sort!{ |a, b| a.send(params[:sort]) <=> b.send(params[:sort]) } if params[:sort]
|
||||
models
|
||||
else
|
||||
@@ -26,13 +34,7 @@ module MusicBrainz
|
||||
end
|
||||
end
|
||||
|
||||
def contents_of(url)
|
||||
if method_defined? :get_contents
|
||||
get_contents url
|
||||
else
|
||||
http.get url
|
||||
end
|
||||
end
|
||||
private
|
||||
|
||||
def build_url(resource, params)
|
||||
"#{MusicBrainz.config.web_service_url}#{resource.to_s.gsub('_', '-')}" <<
|
||||
@@ -40,17 +42,31 @@ module MusicBrainz
|
||||
params.map do |key, value|
|
||||
key = key.to_s.gsub('_', '-')
|
||||
value = if value.is_a?(Array)
|
||||
value.map{ |el| el.to_s.gsub('_', '-') }.join('+')
|
||||
value.map{ |el| el.to_s.gsub('_', '-') }.join(?+)
|
||||
else
|
||||
value.to_s
|
||||
end
|
||||
"#{key}=#{value}"
|
||||
end.join('&')
|
||||
[key, value].join(?=)
|
||||
end.join(?&)
|
||||
end
|
||||
|
||||
include ClientModules::TransparentProxy
|
||||
include ClientModules::FailsafeProxy
|
||||
include ClientModules::CachingProxy
|
||||
extend self
|
||||
def binding_class_for(key)
|
||||
MusicBrainz::Bindings.const_get(constantized(key))
|
||||
end
|
||||
|
||||
def model_class_for(key)
|
||||
MusicBrainz.const_get(constantized(key))
|
||||
end
|
||||
|
||||
def constantized(key)
|
||||
key.to_s.split(?_).map(&:capitalize).join.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
module ClientHelper
|
||||
def client
|
||||
@client ||= Client.new
|
||||
end
|
||||
end
|
||||
extend ClientHelper
|
||||
end
|
||||
|
||||
@@ -10,22 +10,26 @@ module MusicBrainz
|
||||
end
|
||||
|
||||
def get_contents(url)
|
||||
return super unless MusicBrainz.config.perform_caching
|
||||
return super unless caching?
|
||||
|
||||
token = Digest::SHA256.hexdigest(url)
|
||||
file_path = "#{cache_path}/#{token[0..1]}/#{token[2..3]}/#{token[4..-1]}.xml"
|
||||
hash = Digest::SHA256.hexdigest(url)
|
||||
dir_path = [cache_path, *(0..2).map{ |i| hash.slice(2*i, 2) }].join(?/)
|
||||
file_path = [dir_path, '/', hash.slice(6, 58), '.xml'].join
|
||||
|
||||
response = nil
|
||||
response = { body: nil, status: 500 }
|
||||
|
||||
if File.exist?(file_path)
|
||||
response = File.open(file_path, 'rb').gets
|
||||
response = {
|
||||
body: File.open(file_path, 'rb').gets,
|
||||
status: 200
|
||||
}
|
||||
else
|
||||
response = super
|
||||
unless response.nil? or response.empty?
|
||||
FileUtils.mkdir_p file_path.split('/')[0..-2].join('/')
|
||||
if response[:status] == 200
|
||||
FileUtils.mkpath(dir_path)
|
||||
File.open(file_path, 'wb') do |f|
|
||||
f.puts response
|
||||
f.chmod 0755
|
||||
f.puts(response[:body])
|
||||
f.chmod(0755)
|
||||
f.close
|
||||
end
|
||||
end
|
||||
@@ -33,6 +37,10 @@ module MusicBrainz
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def caching?
|
||||
MusicBrainz.config.perform_caching
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,21 +2,36 @@ module MusicBrainz
|
||||
module ClientModules
|
||||
module FailsafeProxy
|
||||
def get_contents(url)
|
||||
response = nil
|
||||
return super unless failsafe?
|
||||
|
||||
response = { body: nil, status: 500 }
|
||||
MusicBrainz.config.tries_limit.times do
|
||||
time_passed = Time.now.to_f - @last_query_time ||= 0.0
|
||||
if time_passed < MusicBrainz.config.query_interval
|
||||
sleep(MusicBrainz.config.query_interval - time_passed)
|
||||
end
|
||||
|
||||
response = super
|
||||
@last_query_time = Time.now.to_f
|
||||
|
||||
break if response.status == 200
|
||||
break if response[:status] == 200
|
||||
end
|
||||
|
||||
response.body rescue nil
|
||||
response
|
||||
end
|
||||
|
||||
def time_passed
|
||||
Time.now.to_f - @last_query_time ||= 0.0
|
||||
end
|
||||
|
||||
def time_to_wait
|
||||
MusicBrainz.config.query_interval - time_passed
|
||||
end
|
||||
|
||||
def ready?
|
||||
time_passed > MusicBrainz.config.query_interval
|
||||
end
|
||||
|
||||
def wait_util_ready!
|
||||
sleep(time_to_wait) unless ready?
|
||||
@last_query_time = Time.now.to_f
|
||||
end
|
||||
|
||||
def failsafe?
|
||||
MusicBrainz.config.tries_limit > 1 && MusicBrainz.config.query_interval.to_f > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,10 @@ module MusicBrainz
|
||||
module ClientModules
|
||||
module TransparentProxy
|
||||
def get_contents(url)
|
||||
http.get url
|
||||
response = http.get(url)
|
||||
{ body: response.body, status: response.status }
|
||||
rescue
|
||||
{ body: nil, status: 500 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,24 +19,42 @@ module MusicBrainz
|
||||
@perform_caching = DEFAULT_PERFORM_CACHING
|
||||
end
|
||||
|
||||
def user_agent_string
|
||||
def valid?
|
||||
%w[ app_name app_version contact ].each do |param|
|
||||
raise "#{param} must be set" if instance_variable_get("@#{param}").nil?
|
||||
unless instance_variable_defined?(:"@#{param}")
|
||||
raise Exception.new("Application identity parameter '#{param}' missing")
|
||||
end
|
||||
end
|
||||
|
||||
"#{@app_name}/#{@app_version} ( #{@contact} )"
|
||||
unless tries_limit.nil? && query_interval.nil?
|
||||
raise Exception.new("'tries_limit' parameter must be 1 or greater") if tries_limit.to_i < 1
|
||||
raise Exception.new("'query_interval' parameter must be greater than zero") if query_interval.to_f < 0
|
||||
end
|
||||
if perform_caching
|
||||
raise Exception.new("'cache_path' parameter must be set") if cache_path.nil?
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
module Configurable
|
||||
def configure
|
||||
raise "Configuration missing" unless block_given?
|
||||
raise Exception.new("Configuration block missing") unless block_given?
|
||||
yield @config ||= MusicBrainz::Configuration.new
|
||||
config.valid?
|
||||
end
|
||||
|
||||
def config
|
||||
raise Exception.new("Configuration missing") unless instance_variable_defined?(:@config)
|
||||
@config
|
||||
end
|
||||
|
||||
def apply_test_configuration!
|
||||
configure do |c|
|
||||
c.app_name = "gem musicbrainz (development mode)"
|
||||
c.app_version = MusicBrainz::VERSION
|
||||
c.contact = `git config user.email`.chomp
|
||||
end
|
||||
end
|
||||
end
|
||||
extend Configurable
|
||||
end
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
module MusicBrainz
|
||||
class Middleware < Faraday::Middleware
|
||||
def call(env)
|
||||
env[:request_headers]["User-Agent"] = MusicBrainz.config.user_agent_string
|
||||
env[:request_headers]["Via"] = "gem musicbrainz/#{VERSION} (#{GH_PAGE_URL})"
|
||||
|
||||
env[:request_headers].merge!(
|
||||
"User-Agent" => user_agent_string,
|
||||
"Via" => via_string
|
||||
)
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def user_agent_string
|
||||
"#{config.app_name}/#{config.app_version} ( #{config.contact} )"
|
||||
end
|
||||
|
||||
def via_string
|
||||
"gem musicbrainz/#{VERSION} (#{GH_PAGE_URL})"
|
||||
end
|
||||
|
||||
def config
|
||||
MusicBrainz.config
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
module MusicBrainz
|
||||
class Artist
|
||||
include BaseModel
|
||||
|
||||
class Artist < BaseModel
|
||||
field :id, String
|
||||
field :type, String
|
||||
field :name, String
|
||||
field :country, String
|
||||
field :date_begin, Time
|
||||
field :date_end, Time
|
||||
field :date_begin, Date
|
||||
field :date_end, Date
|
||||
field :urls, Hash
|
||||
|
||||
attr_writer :release_groups
|
||||
|
||||
def release_groups
|
||||
@release_groups ||= Client::load(:release_group, { artist: id }, {
|
||||
binding: MusicBrainz::Bindings::ArtistReleaseGroups,
|
||||
create_models: MusicBrainz::ReleaseGroup,
|
||||
@release_groups ||= client.load(:release_group, { artist: id }, {
|
||||
binding: :artist_release_groups,
|
||||
create_models: :release_group,
|
||||
sort: :first_release_date
|
||||
}) unless @id.nil?
|
||||
end
|
||||
|
||||
class << self
|
||||
def find(id)
|
||||
Client.load(:artist, { id: id, inc: [:url_rels] }, {
|
||||
binding: MusicBrainz::Bindings::Artist,
|
||||
create_model: MusicBrainz::Artist
|
||||
client.load(:artist, { id: id, inc: [:url_rels] }, {
|
||||
binding: :artist,
|
||||
create_model: :artist
|
||||
})
|
||||
end
|
||||
|
||||
def search(name)
|
||||
name = CGI.escape(name).gsub(/\!/, '\!')
|
||||
|
||||
Client.load(:artist, { query: "artist:#{name}", limit: 10 }, {
|
||||
binding: MusicBrainz::Bindings::ArtistSearch
|
||||
client.load(:artist, { query: "artist:#{name}", limit: 10 }, {
|
||||
binding: :artist_search
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
@@ -1,29 +1,43 @@
|
||||
module MusicBrainz
|
||||
module BaseModel
|
||||
def self.included(klass)
|
||||
class BaseModel
|
||||
def self.inherited(klass)
|
||||
klass.send(:include, InstanceMethods)
|
||||
klass.send(:extend, ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def field(name, type)
|
||||
self.class_exec do
|
||||
attr_reader name
|
||||
fields[name] = type
|
||||
attr_reader name
|
||||
|
||||
define_method("#{name}=") do |val|
|
||||
instance_variable_set("@#{name}", validate_type(val, type))
|
||||
end
|
||||
define_method("#{name}=") do |val|
|
||||
instance_variable_set("@#{name}", validate_type(val, type))
|
||||
end
|
||||
end
|
||||
|
||||
def fields
|
||||
instance_variable_set(:@fields, {}) unless instance_variable_defined?(:@fields)
|
||||
instance_variable_get(:@fields)
|
||||
end
|
||||
|
||||
def client
|
||||
MusicBrainz.client
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def initialize(params = {})
|
||||
params.each do |field, value|
|
||||
self.send :"#{field}=", value
|
||||
self.send(:"#{field}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
def client
|
||||
MusicBrainz.client
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_type(val, type)
|
||||
if type == Integer
|
||||
val.to_i
|
||||
@@ -31,7 +45,7 @@ module MusicBrainz
|
||||
val.to_f
|
||||
elsif type == String
|
||||
val.to_s
|
||||
elsif type == Time
|
||||
elsif type == Date
|
||||
if val.nil? or val == ""
|
||||
val = "2030-12-31"
|
||||
elsif val.split("-").length == 1
|
||||
@@ -39,7 +53,7 @@ module MusicBrainz
|
||||
elsif val.split("-").length == 2
|
||||
val << "-31"
|
||||
end
|
||||
Time.utc(*val.split("-"))
|
||||
Date.new(*val.split(?-).map(&:to_i))
|
||||
else
|
||||
val
|
||||
end
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
module MusicBrainz
|
||||
class Release
|
||||
include BaseModel
|
||||
|
||||
class Release < BaseModel
|
||||
field :id, String
|
||||
field :title, String
|
||||
field :status, String
|
||||
field :format, String
|
||||
field :date, Time
|
||||
field :date, Date
|
||||
field :country, String
|
||||
|
||||
attr_writer :tracks
|
||||
|
||||
def tracks
|
||||
@tracks ||= Client::load(:release, { id: id, inc: [:recordings, :media], limit: 100 }, {
|
||||
binding: MusicBrainz::Bindings::ReleaseTracks,
|
||||
create_models: MusicBrainz::Track,
|
||||
@tracks ||= client.load(:release, { id: id, inc: [:recordings, :media], limit: 100 }, {
|
||||
binding: :release_tracks,
|
||||
create_models: :track,
|
||||
sort: :position
|
||||
}) unless @id.nil?
|
||||
end
|
||||
|
||||
class << self
|
||||
def find(id)
|
||||
Client.load(:release, { id: id, inc: [:media] }, {
|
||||
binding: MusicBrainz::Bindings::Release,
|
||||
create_model: MusicBrainz::Release
|
||||
client.load(:release, { id: id, inc: [:media] }, {
|
||||
binding: :release,
|
||||
create_model: :release
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
module MusicBrainz
|
||||
class ReleaseGroup
|
||||
include BaseModel
|
||||
|
||||
class ReleaseGroup < BaseModel
|
||||
field :id, String
|
||||
field :type, String
|
||||
field :title, String
|
||||
field :desc, String
|
||||
field :first_release_date, Time
|
||||
field :first_release_date, Date
|
||||
|
||||
alias_method :disambiguation, :desc
|
||||
attr_writer :releases
|
||||
|
||||
def releases
|
||||
@releases ||= Client::load(:release, { release_group: id, inc: [:media], limit: 100 }, {
|
||||
binding: MusicBrainz::Bindings::ReleaseGroupReleases,
|
||||
create_models: MusicBrainz::Release,
|
||||
@releases ||= client.load(:release, { release_group: id, inc: [:media], limit: 100 }, {
|
||||
binding: :release_group_releases,
|
||||
create_models: :release,
|
||||
sort: :date
|
||||
}) unless @id.nil?
|
||||
end
|
||||
|
||||
class << self
|
||||
def find(id)
|
||||
Client.load(:release_group, { id: id }, {
|
||||
binding: MusicBrainz::Bindings::ReleaseGroup,
|
||||
create_model: MusicBrainz::ReleaseGroup
|
||||
client.load(:release_group, { id: id }, {
|
||||
binding: :release_group,
|
||||
create_model: :release_group
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module MusicBrainz
|
||||
class Track
|
||||
include BaseModel
|
||||
|
||||
class Track < BaseModel
|
||||
field :position, Integer
|
||||
field :recording_id, String
|
||||
field :title, String
|
||||
@@ -9,9 +7,9 @@ module MusicBrainz
|
||||
|
||||
class << self
|
||||
def find(id)
|
||||
Client.load(:recording, { id: id }, {
|
||||
binding: MusicBrainz::Bindings::Track,
|
||||
create_model: MusicBrainz::Track
|
||||
client.load(:recording, { id: id }, {
|
||||
binding: :track,
|
||||
create_model: :track
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module MusicBrainz
|
||||
VERSION = "0.8"
|
||||
VERSION = "0.7.2"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user