Stability improvements, new release

This commit is contained in:
Gregory Eremin
2013-01-20 16:36:52 +04:00
parent 7d5bde8616
commit 531fcc9908
21 changed files with 322 additions and 211 deletions
+2
View File
@@ -0,0 +1,2 @@
require "musicbrainz"
MB = MusicBrainz
+36 -20
View File
@@ -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
+23 -5
View File
@@ -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
+16 -3
View File
@@ -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
+11 -15
View File
@@ -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
+24 -10
View File
@@ -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
+8 -12
View File
@@ -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
+8 -11
View File
@@ -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
+4 -6
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
module MusicBrainz
VERSION = "0.8"
VERSION = "0.7.2"
end