sandro-rlastfm 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.0.2 / 2009-03-09
2
+ * Birthday
3
+
4
+ == 0.0.1 / 2009-03-09
5
+ * Initial Release
data/README.txt ADDED
@@ -0,0 +1,63 @@
1
+ rlastfm
2
+ by Sandro Turriate
3
+ http://turriate.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Extremely minimal Last.fm wrapper.
8
+ This wrapper allows you to lookup an Artist by name, get their albums, and the tracks for each album.
9
+
10
+ == SYNOPSIS:
11
+
12
+ >> Rlastfm.api_key = "your lastfm api key"
13
+ >> Rlastfm.debug = true # watch the requests you make
14
+
15
+ >> artist = Rlastfm::Artist.new('radiohead')
16
+ => #<Rlastfm::Artist:0x115bed0 @initialized_name="radiohead">
17
+
18
+ >> artist.name
19
+ => "Radiohead"
20
+
21
+ >> artist.albums
22
+ [#<Rlastfm::Album:0xd598>, #<Rlastfm::Album:0xd599>]
23
+
24
+ >> artist.albums.first.name
25
+ => "In Rainbows"
26
+
27
+ >> artist.albums.first.tracks
28
+ [#<Rlastfm::Track:0x18f38f0>, #<Rlastfm::Track:0x18f38f1>]
29
+
30
+ == REQUIREMENTS:
31
+
32
+ * hpricot
33
+ * httparty
34
+ * fakeweb (to run specs)
35
+
36
+ == INSTALL:
37
+
38
+ FIXME
39
+
40
+ == LICENSE:
41
+
42
+ (The MIT License)
43
+
44
+ Copyright (c) 2008
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining
47
+ a copy of this software and associated documentation files (the
48
+ 'Software'), to deal in the Software without restriction, including
49
+ without limitation the rights to use, copy, modify, merge, publish,
50
+ distribute, sublicense, and/or sell copies of the Software, and to
51
+ permit persons to whom the Software is furnished to do so, subject to
52
+ the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
60
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
61
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
62
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
63
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'rlastfm'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'rlastfm'
22
+ PROJ.summary = "minimal Last.fm wrapper."
23
+ PROJ.description = %Q(#{PROJ.summary}
24
+ This wrapper allows you to lookup an Artist by name, get their albums, and the tracks for each album.)
25
+ PROJ.authors = 'Sandro Turriate'
26
+ PROJ.email = 'sandro.turriate@gmail.com'
27
+ PROJ.url = 'http://turriate.com'
28
+ PROJ.version = Rlastfm::VERSION
29
+ PROJ.rubyforge.name = 'rlastfm'
30
+ PROJ.gem.development_dependencies = []
31
+
32
+ PROJ.spec.opts << '--color'
33
+ PROJ.ignore_file = '.gitignore'
34
+
35
+ # EOF
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), %w(lib rlastfm))
@@ -0,0 +1,30 @@
1
+ class Hash
2
+ def stringify_keys
3
+ inject({}) do |options, (key, value)|
4
+ options[key.to_s] = value
5
+ options
6
+ end
7
+ end
8
+
9
+ # Destructively convert all keys to strings.
10
+ def stringify_keys!
11
+ keys.each do |key|
12
+ self[key.to_s] = delete(key)
13
+ end
14
+ self
15
+ end
16
+
17
+ # Return a new hash with all keys converted to symbols.
18
+ def symbolize_keys
19
+ inject({}) do |options, (key, value)|
20
+ options[(key.to_sym rescue key) || key] = value
21
+ options
22
+ end
23
+ end
24
+
25
+ # Destructively convert all keys to symbols.
26
+ def symbolize_keys!
27
+ self.replace(self.symbolize_keys)
28
+ end
29
+ end
30
+
data/lib/rlastfm.rb ADDED
@@ -0,0 +1,83 @@
1
+ module Rlastfm
2
+ # :stopdoc:
3
+ VERSION = '0.0.2'
4
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
5
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
6
+
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
29
+ end
30
+
31
+ # Utility method used to require all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = nil )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each {|rb| require rb}
42
+ end
43
+
44
+ class << self
45
+ attr_accessor :api_key, :debug
46
+ end
47
+
48
+ self.debug = false
49
+ end # module Rlastfm
50
+
51
+ %w(rubygems hpricot open-uri httparty).each { |l| require l }
52
+
53
+ module HTTParty
54
+ class Request
55
+ def perform_with_debug
56
+ if Rlastfm.debug
57
+ puts "Performing request to: #{uri.to_s}"
58
+ end
59
+ perform_without_debug
60
+ end
61
+
62
+ alias_method :perform_without_debug, :perform
63
+ alias_method :perform, :perform_with_debug
64
+
65
+ def query_string_with_sorting(uri)
66
+ unsorted = query_string_without_sorting(uri)
67
+ unsorted.split("&").sort.join("&") if unsorted
68
+ end
69
+
70
+ alias_method :query_string_without_sorting, :query_string
71
+ alias_method :query_string, :query_string_with_sorting
72
+ end
73
+ end
74
+
75
+
76
+ helper_libs = File.join(File.dirname(__FILE__), %w(rlastfm helpers *.rb))
77
+ Dir.glob(helper_libs).each { |l| require l }
78
+
79
+ require File.join(File.dirname(__FILE__), 'core_extensions')
80
+
81
+ Rlastfm.require_all_libs_relative_to(__FILE__)
82
+
83
+ # EOF
@@ -0,0 +1,42 @@
1
+ module Rlastfm
2
+ class Album
3
+ include Helpers::Scraper
4
+
5
+ attr_reader :raw
6
+ attr_accessor :artist, :images, :name, :play_count, :rank, :url
7
+
8
+ def initialize(hash={})
9
+ @raw = hash.stringify_keys!
10
+ @images = hash['images'] || hash['image']
11
+ @name = hash['name']
12
+ @play_count = hash['play_count'] || hash['playcount']
13
+ @rank = hash['rank']
14
+ @url = hash['url']
15
+ if (a = hash['artist'])
16
+ @artist = a.is_a?(Hash) ? a['name'] : a
17
+ end
18
+ end
19
+
20
+ def tracks
21
+ @tracks ||= get_tracks
22
+ end
23
+
24
+ private
25
+
26
+ def get_tracks
27
+ doc = doc_for(url)
28
+ artist_container = doc.at(".albumHead h1")
29
+ artist = artist_container.at("a").inner_text
30
+ track_container = doc.at("#albumTracklist")
31
+
32
+ track_container.search("tbody tr").map do |row|
33
+ Track.new \
34
+ :artist => artist,
35
+ :album => name,
36
+ :number => row.at(".positionCell").to_plain_text,
37
+ :name => row.at(".subjectCell a").inner_text,
38
+ :duration => row.at(".durationCell").to_plain_text
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,67 @@
1
+ module Rlastfm
2
+ class Artist
3
+ extend Helpers::RemoteAccessor
4
+ include Helpers::Scraper
5
+ include HTTParty
6
+
7
+ base_uri "http://ws.audioscrobbler.com/2.0/"
8
+
9
+ remote_reader :name, :mbid, :stats, :url, :bio
10
+ remote_reader :from => :image, :to => :images
11
+
12
+ def self.top_twenty
13
+ doc = doc_for("/charts/artist")
14
+ top_twenty = doc.at('.mediumImageChart')
15
+ top_twenty.search('tr').map do |artist_row|
16
+ name = artist_row.search('.subject < a').last
17
+ new name
18
+ end
19
+ end
20
+
21
+ def initialize(name)
22
+ self.class.default_params :api_key => Rlastfm.api_key
23
+ @initialized_name = name
24
+ end
25
+
26
+ def album_by_name(album_name)
27
+ albums.detect {|a| a.name == album_name}
28
+ end
29
+
30
+ def albums
31
+ @albums ||= begin
32
+ response = self.class.get('', :query => {:method => "artist.gettopalbums", :artist => name})
33
+ album_collection = response['lfm']['topalbums']['album']
34
+ album_collection.map{|album_attrs| Album.new album_attrs} if album_collection
35
+ end
36
+ end
37
+
38
+ def exists?
39
+ ! mbid.nil?
40
+ end
41
+
42
+ def raw
43
+ @raw ||= remote_sync!
44
+ end
45
+
46
+ def similar_artists
47
+ @similar_artists ||= begin
48
+ raw['similar']['artist'].map do |artist|
49
+ Artist.new artist['name']
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def remote_sync!
57
+ response = self.class.get('', :query => {:method => 'artist.getinfo', :artist => @initialized_name})
58
+ @synced = true
59
+ @raw = response['lfm']['artist']
60
+ end
61
+
62
+ def synced?
63
+ @synced == true
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,50 @@
1
+ module Rlastfm
2
+ module Helpers
3
+ module RemoteAccessor
4
+ def remote_reader(*accessors)
5
+ accessors.each do |reader|
6
+ case reader
7
+ when Symbol
8
+ add_reader reader
9
+ when Hash
10
+ add_mapped_reader reader
11
+ else
12
+ raise "remote_reader requires a Hash or Symbol"
13
+ end
14
+ end
15
+ end
16
+
17
+ def add_mapped_reader(mapping)
18
+ remote_reader_name = mapping.delete(:from)
19
+ reader_name = mapping.delete(:to)
20
+ raise "remote_accessor_mapping requires :from and :to arguments" unless remote_reader_name && reader_name
21
+
22
+ add_reader reader_name, remote_reader_name
23
+ end
24
+
25
+ def add_reader(reader_name, remote_reader_name=nil)
26
+ remote_reader_name ||= reader_name
27
+ define_method reader_name do
28
+ raw[remote_reader_name.to_s]
29
+ end
30
+
31
+ memoize_method reader_name
32
+ end
33
+
34
+ def memoize_method(method_name)
35
+ i_var_name = :"@#{method_name}"
36
+ method_without = :"#{method_name}_without_memoization"
37
+ method_with = :"#{method_name}_with_memoization"
38
+
39
+ class_eval do
40
+ define_method method_with do
41
+ instance_variable_get(i_var_name) || instance_variable_set(i_var_name, send(method_without))
42
+ end
43
+ alias_method method_without, method_name
44
+ alias_method method_name, method_with
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,38 @@
1
+ module Rlastfm
2
+ module Helpers
3
+ module Scraper
4
+ def self.included(base)
5
+ base.class_eval do
6
+ protected
7
+
8
+ extend ProtectedClassMethods
9
+ include ProtectedInstanceMethods
10
+ end
11
+ end
12
+
13
+ module ProtectedClassMethods
14
+ def last_fm_uri(path)
15
+ URI::HTTP.build :host => "www.last.fm", :path => path
16
+ end
17
+
18
+ def doc_for(url_or_path)
19
+ url_or_path = URI.parse(url_or_path)
20
+ url = url_or_path.relative? ? last_fm_uri(url_or_path.to_s).to_s : url_or_path
21
+
22
+ if Rlastfm.debug
23
+ puts "Releasing scraper on: #{url}"
24
+ end
25
+
26
+ doc = Hpricot open(url)
27
+ end
28
+ end
29
+
30
+ module ProtectedInstanceMethods
31
+ def doc_for(url_or_path)
32
+ self.class.doc_for(url_or_path)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,10 @@
1
+ module Rlastfm
2
+ class Track
3
+ attr_accessor :album, :artist, :duration, :name, :number
4
+
5
+ def initialize(hash={})
6
+ hash.each {|k,v| instance_variable_set("@#{k}", v) }
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,23 @@
1
+ module Rlastfm
2
+ class Venue
3
+ include Helpers::Scraper
4
+
5
+ def initialize(id)
6
+ @venue_id = id
7
+ end
8
+
9
+ def id
10
+ @venue_id
11
+ end
12
+
13
+ def photo
14
+ @photo ||= begin
15
+ url = "/venue/#{id}"
16
+ doc = doc_for(url)
17
+ image = doc.at('#catalogueImage')
18
+ image.attributes['src']
19
+ end
20
+ end
21
+ end
22
+ end
23
+