Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Done a lot of refactoring and documenting in Scrobbler::User.
  • Loading branch information
xhochy committed Aug 5, 2010
1 parent aa64375 commit bda3744
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 85 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -12,3 +12,5 @@ catalog.xml
tmp tmp
.project .project
.loadpath .loadpath
view*.sh
.chomrium/
48 changes: 43 additions & 5 deletions lib/scrobbler/base.rb
Expand Up @@ -19,6 +19,10 @@ class Base
# By default, there is no cache # By default, there is no cache
@@cache = [] @@cache = []


# Add a cache provider to the caching system
#
# @param [CacheProvider] cache A instance of a cache provider.
# @return [void]
def Base.add_cache(cache) def Base.add_cache(cache)
@@cache << cache @@cache << cache
end end
Expand All @@ -28,7 +32,7 @@ def Base.add_cache(cache)
# This key will be used by all Scrobbler classes and objects. # This key will be used by all Scrobbler classes and objects.
# #
# @param [String] api_key The default API key. # @param [String] api_key The default API key.
# @return [nil] # @return [void]
def Base.api_key=(api_key) def Base.api_key=(api_key)
@@api_key = api_key @@api_key = api_key
end end
Expand All @@ -38,7 +42,7 @@ def Base.api_key=(api_key)
# This secret will be used by all Scrobbler classes and objects. # This secret will be used by all Scrobbler classes and objects.
# #
# @param [String] secret The default API secret. # @param [String] secret The default API secret.
# @return [nil] # @return [void]
def Base.secret=(secret) def Base.secret=(secret)
@@secret = secret @@secret = secret
end end
Expand Down Expand Up @@ -85,7 +89,7 @@ def Base.post_request(api_method, parameters = {})
# Load a request from cache if possible # Load a request from cache if possible
# #
# @param [Hash] parameters The parameters passed as URL params. # @param [Hash] parameters The parameters passed as URL params.
# @return [String,nil] # @return [String]
def Base.load_from_cache(parameters) def Base.load_from_cache(parameters)
@@cache.each do |cache| @@cache.each do |cache|
if cache.has?(parameters) if cache.has?(parameters)
Expand All @@ -99,7 +103,7 @@ def Base.load_from_cache(parameters)
# #
# @param [String] xml The answer from the Last.fm API # @param [String] xml The answer from the Last.fm API
# @param [Hash] parameters The parameters passed as URL params. # @param [Hash] parameters The parameters passed as URL params.
# @return [nil] # @return [void]
def Base.save_to_cache(xml, parameters) def Base.save_to_cache(xml, parameters)
@@cache.each do |cache| @@cache.each do |cache|
if cache.writable? then if cache.writable? then
Expand Down Expand Up @@ -184,7 +188,7 @@ def Base.request(api_method, parameters = {}, request_method = 'get')
# Load information into instance variables. # Load information into instance variables.
# #
# @param [Hash<String,Symbol>] data Each entry will be stored as a variable. # @param [Hash<String,Symbol>] data Each entry will be stored as a variable.
# @return [nil] # @return [void]
def populate_data(data = {}) def populate_data(data = {})
data.each do |key, value| data.each do |key, value|
instance_variable_set("@#{key.to_s}", value) instance_variable_set("@#{key.to_s}", value)
Expand All @@ -199,6 +203,40 @@ def populate_data(data = {})
def request(api_method, parameters = {}, request_method = 'get') def request(api_method, parameters = {}, request_method = 'get')
Base.request(api_method, parameters, request_method) Base.request(api_method, parameters, request_method)
end end

# Generic request method for the most Library funtions
#
# @param [String,Symbol] api_method The method which shall be called.
# @param [Hash] options The parameters passed as URL params.
# @param [String,Symbol] parent the parent XML node to look for.
# @param [Class] element The xml node name which shall be converted
# into an object.
# @return [Array]
def call_pageable(method, parent, element, options={})
options = {:all => true}.merge options
result = []
if options.delete(:all)
doc = Base.request(method, options)
root = nil
doc.root.children.each do |child|
next unless child.name == parent.to_s
root = child
end
total_pages = root['totalPages'].to_i
root.children.each do |child|
next unless child.name == element.to_s.sub("Scrobbler::","").downcase
result << element.new_from_libxml(child)
end
(2..total_pages).each do |i|
options[:page] = i
result.concat call(method, parent, element, options)
end
else
result = call(method, parent, element, options)
end
result
end



# Call a API method # Call a API method
# #
Expand Down
42 changes: 7 additions & 35 deletions lib/scrobbler/library.rb
Expand Up @@ -40,8 +40,10 @@ def add_track


# A list of all the albums in a user's library, with play counts and tag # A list of all the albums in a user's library, with play counts and tag
# counts. # counts.
# @param [Hash<Symbol>] options The options to configure this API call.
# @return [Array<Scrobbler::Tracks>] The artists included in this library.
def albums(options={}) def albums(options={})
request_library('library.getalbums', :albums, Album, options) call_pageable('library.getalbums', :albums, Album, {:user => @user.name}.merge(options))
end end


# A list of all the artists in a user's library, with play counts and tag # A list of all the artists in a user's library, with play counts and tag
Expand All @@ -50,47 +52,17 @@ def albums(options={})
# @param [Hash<Symbol>] options The options to configure this API call. # @param [Hash<Symbol>] options The options to configure this API call.
# @return [Array<Scrobbler::Artist>] The artists included in this library. # @return [Array<Scrobbler::Artist>] The artists included in this library.
def artists(options={}) def artists(options={})
request_library('library.getartists', :artists, Artist, options) call_pageable('library.getartists', :artists, Artist, {:user => @user.name}.merge(options))
end end


# A list of all the tracks in a user's library, with play counts and tag # A list of all the tracks in a user's library, with play counts and tag
# counts. # counts.
# @param [Hash<Symbol>] options The options to configure this API call.
# @return [Array<Scrobbler::Track>] The tracks included in this library.
def tracks(options={}) def tracks(options={})
request_library('library.gettracks', :tracks, Track, options) call_pageable('library.gettracks', :tracks, Track, {:user => @user.name}.merge(options))
end end


# Generic request method for the most Library funtions
#
# @param [String,Symbol] api_method The method which shall be called.
# @param [Hash] options The parameters passed as URL params.
# @param [String,Symbol] parent the parent XML node to look for.
# @param [Class] element The xml node name which shall be converted
# into an object.
# @return [Array]
def request_library(method, parent, element, options={})
options = {:all => true, :user => @user.name}.merge options
result = []
if options.delete(:all)
doc = Base.request(method, options)
root = nil
doc.root.children.each do |child|
next unless child.name == parent.to_s
root = child
end
total_pages = root['totalPages'].to_i
root.children.each do |child|
next unless child.name == element.to_s.sub("Scrobbler::","").downcase
result << element.new_from_libxml(child)
end
(2..total_pages).each do |i|
options[:page] = i
result.concat call(method, parent, element, options)
end
else
result = call(method, parent, element, options)
end
result
end


end end
end end
Expand Down
99 changes: 56 additions & 43 deletions lib/scrobbler/user.rb
Expand Up @@ -2,7 +2,7 @@


require File.expand_path('basexmlinfo.rb', File.dirname(__FILE__)) require File.expand_path('basexmlinfo.rb', File.dirname(__FILE__))


module Scrobbler module Scrobbler
class User < BaseXmlInfo class User < BaseXmlInfo
# Load Helper modules # Load Helper modules
include ImageObjectFuncs include ImageObjectFuncs
Expand All @@ -26,7 +26,7 @@ def find(*args)
# Load the data for this object out of a XML-Node # Load the data for this object out of a XML-Node
# #
# @param [LibXML::XML::Node] node The XML node containing the information # @param [LibXML::XML::Node] node The XML node containing the information
# @return [nil] # @return [void]
def load_from_xml(node) def load_from_xml(node)
# Get all information from the root's children nodes # Get all information from the root's children nodes
node.children.each do |child| node.children.each do |child|
Expand All @@ -53,6 +53,9 @@ def load_from_xml(node)
end #^ do |child| end #^ do |child|
end end


# Create a new Scrobbler:User instance
#
# @param [Hash] data The options to initialize the class
def initialize(data={}) def initialize(data={})
raise ArgumentError unless data.kind_of?(Hash) raise ArgumentError unless data.kind_of?(Hash)
super(data) super(data)
Expand All @@ -67,31 +70,12 @@ def events
call('user.getevents', :events, Event, {:user => @name}) call('user.getevents', :events, Event, {:user => @name})
end end


# Get a list of the user's friends on Last.fm. # Get a list of the user's friends on Last.fm.
#
# @param [Hash] opts Parameters for the API request
# @return [Array<Scrobbler::User>]
def friends(opts={}) def friends(opts={})
result = [] call_pageable('user.getfriends', :friends, User, {:user => @name}.merge(opts))
if opts.delete :all
params = {:page => 1, :limit => 50, :user => @name}.merge(opts)
doc = Base.request('user.getfriends', params)
root = nil
doc.root.children.each do |child|
next unless child.name == 'friends'
root = child
end
total_pages = root['totalPages'].to_i
root.children.each do |child|
next unless child.name == 'user'
result << Scrobbler::User.new_from_libxml(child)
end
(2..total_pages).each do |i|
params[:page] = i
result.concat call('user.getfriends', :friends, User, params)
end
else
params = {:page => 1, :limit => 50, :user => @name}.merge(opts)
result = call('user.getfriends', :friends, User, params)
end
result
end end


# Get information about a user profile. # Get information about a user profile.
Expand All @@ -101,37 +85,44 @@ def load_info
end end


# Get the last 50 tracks loved by a user. # Get the last 50 tracks loved by a user.
#
# @return [Array<Scrobbler::Track>]
def loved_tracks def loved_tracks
call('user.getlovedtracks', :lovedtracks, Track, {:user => @name}) call('user.getlovedtracks', :lovedtracks, Track, {:user => @name})
end end


# Get a list of a user's neighbours on Last.fm. # Get a list of a user's neighbours on Last.fm.
#
# @return [Array<Scrobbler::Track>]
def neighbours def neighbours
call('user.getneighbours', :neighbours, User, {:user => @name}) call('user.getneighbours', :neighbours, User, {:user => @name})
end end


# Get a paginated list of all events a user has attended in the past. # Get a paginated list of all events a user has attended in the past.
def past_events(format=:ics) def past_events(format=:ics)
# This needs a Event class, which is yet not available # This needs a Event class, which is yet not available
raise NotImplementedError raise NotImplementedError
end end


# Get a list of a user's playlists on Last.fm. # Get a list of a user's playlists on Last.fm.
#
# @return [Array<Scrobbler::Playlist>]
def playlists def playlists
call('user.getplaylists', :playlists, Playlist, {:user => @name}) call('user.getplaylists', :playlists, Playlist, {:user => @name})
end end


# Get a list of the recent tracks listened to by this user. Indicates now # Get a list of the recent tracks listened to by this user. Indicates now
# playing track if the user is currently listening. # playing track if the user is currently listening.
# #
# Possible parameters: # @param [Hash] parameters
# - limit: An integer used to limit the number of tracks returned. # @return [Array<Scrobbler::Track>]
def recent_tracks(parameters={}) def recent_tracks(parameters={})
parameters.merge!({:user => @name}) call('user.getrecenttracks', :recenttracks, Track, {:user => @name}.merge(parameters))
call('user.getrecenttracks', :recenttracks, Track, parameters)
end end


# Get Last.fm artist recommendations for a user # Get Last.fm artist recommendations for a user
#
# @return [Array<Scrobbler::Artist>]
def recommended_artists def recommended_artists
# This function require authentication, but SimpleAuth is not yet 2.0 # This function require authentication, but SimpleAuth is not yet 2.0
raise NotImplementedError raise NotImplementedError
Expand All @@ -151,50 +142,69 @@ def shouts
end end


# Get the top albums listened to by a user. You can stipulate a time period. # Get the top albums listened to by a user. You can stipulate a time period.
# Sends the overall chart by default. # Sends the overall chart by default.
#
# @return [Array<Scrobbler::Album>]
def top_albums(period=:overall) def top_albums(period=:overall)
call('user.gettopalbums', :topalbums, Album, {:user => @name, :period => period}) call('user.gettopalbums', :topalbums, Album, {:user => @name, :period => period})
end end


# Get the top artists listened to by a user. You can stipulate a time # Get the top artists listened to by a user. You can stipulate a time
# period. Sends the overall chart by default. # period. Sends the overall chart by default.
#
# @return [Array<Scrobbler::Artist>]
def top_artists(period=:overall) def top_artists(period=:overall)
call('user.gettopartists', :topartists, Artist, {:user => @name, :period => period}) call('user.gettopartists', :topartists, Artist, {:user => @name, :period => period})
end end


# Get the top tags used by this user. # Get the top tags used by this user.
#
# @return [Array<Scrobbler::Tag>]
def top_tags def top_tags
call('user.gettoptags', :toptags, Tag, {:user => @name}) call('user.gettoptags', :toptags, Tag, {:user => @name})
end end


# Get the top tracks listened to by a user. You can stipulate a time period. # Get the top tracks listened to by a user. You can stipulate a time period.
# Sends the overall chart by default. # Sends the overall chart by default.
#
# @return [Array<Scrobbler::Track>]
def top_tracks(period=:overall) def top_tracks(period=:overall)
call('user.gettoptracks', :toptracks, Track, {:user => @name, :period => period}) call('user.gettoptracks', :toptracks, Track, {:user => @name, :period => period})
end end


# Setup the parameters for a *chart API call # Setup the parameters for a *chart API call
def setup_chart_params(from=nil, to=nil) #
# @param [Class] type
# @return [Array]
def get_chart(type, from, to)
parameters = {:user => @name} parameters = {:user => @name}
parameters[:from] = from unless from.nil? parameters[:from] = from unless from.nil?
parameters[:to] = to unless to.nil? parameters[:to] = to unless to.nil?
parameters downType = type.to_s.sub("Scrobbler::","").downcase
call('user.getweekly'+ downType + 'chart',
'weekly' + downType + 'chart', type, parameters)
end end


# Get an album chart for a user profile, for a given date range. If no date # Get an album chart for a user profile, for a given date range. If no date
# range is supplied, it will return the most recent album chart for this # range is supplied, it will return the most recent album chart for this
# user. # user.
#
# @param [int] from Starttime
# @param [int] to Endtime
# @return [Array<Scrobbler::Album>]
def weekly_album_chart(from=nil, to=nil) def weekly_album_chart(from=nil, to=nil)
parameters = setup_chart_params(from, to) get_chart(Album, from, to)
call('user.getweeklyalbumchart', :weeklyalbumchart, Album, parameters)
end end


# Get an artist chart for a user profile, for a given date range. If no date # Get an artist chart for a user profile, for a given date range. If no date
# range is supplied, it will return the most recent artist chart for this # range is supplied, it will return the most recent artist chart for this
# user. # user.
#
# @param [int] from Starttime
# @param [int] to Endtime
# @return [Array<Scrobbler::Artist>]
def weekly_artist_chart(from=nil, to=nil) def weekly_artist_chart(from=nil, to=nil)
parameters = setup_chart_params(from, to) get_chart(Artist, from, to)
call('user.getweeklyartistchart', :weeklyartistchart, Artist, parameters)
end end


# Get a list of available charts for this user, expressed as date ranges # Get a list of available charts for this user, expressed as date ranges
Expand All @@ -207,9 +217,12 @@ def weekly_chart_list(force=false)
# Get a track chart for a user profile, for a given date range. If no date # Get a track chart for a user profile, for a given date range. If no date
# range is supplied, it will return the most recent track chart for this # range is supplied, it will return the most recent track chart for this
# user. # user.
#
# @param [int] from Starttime
# @param [int] to Endtime
# @return [Array<Scrobbler::Track>]
def weekly_track_chart(from=nil, to=nil) def weekly_track_chart(from=nil, to=nil)
parameters = setup_chart_params(from, to) get_chart(Track, from, to)
call('user.getweeklytrackchart', :weeklytrackchart, Track, parameters)
end end


# Shout on this user's shoutbox # Shout on this user's shoutbox
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/xml/user/friends.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<lfm status="ok"> <lfm status="ok">
<friends for="RJ" total="84" page="1" perPage="50" totalPages="2"> <friends for="RJ" total="84" page="1" perPage="50" totalPages="1">
<user> <user>
<name>lobsterclaw</name> <name>lobsterclaw</name>
<realname>Laura Weiss</realname> <realname>Laura Weiss</realname>
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/rest.rb
Expand Up @@ -103,7 +103,7 @@ def register_fw(uri, *args)
'user', 'topalbums.xml') 'user', 'topalbums.xml')
register_fw('user=jnunemaker&api_key=foo123&method=user.getneighbours', register_fw('user=jnunemaker&api_key=foo123&method=user.getneighbours',
'user', 'neighbours.xml') 'user', 'neighbours.xml')
register_fw('user=jnunemaker&page=1&limit=50&api_key=foo123&method=user.getfriends', register_fw('user=jnunemaker&api_key=foo123&method=user.getfriends',
'user', 'friends.xml') 'user', 'friends.xml')
register_fw('user=jnunemaker&api_key=foo123&method=user.getrecenttracks', register_fw('user=jnunemaker&api_key=foo123&method=user.getrecenttracks',
'user', 'recenttracks.xml') 'user', 'recenttracks.xml')
Expand Down

0 comments on commit bda3744

Please sign in to comment.