bandsintown 0.1.4 → 0.2.0

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.
@@ -1,3 +1,11 @@
1
+ == 0.2.0 2010-06-03
2
+ * Added Events - Create API method (Bandsintown::Event.create)
3
+
4
+ == 0.1.5 2010-04-06
5
+ * Added Venues - Events API method (Bandsintown::Venue#events)
6
+ * Added Venues - Search API method (Bandsintown::Venues.search)
7
+ * Refactored Bandsintown::Venue initialization
8
+
1
9
  == 0.1.4 2010-03-02
2
10
  * Added Events - On Sale Soon API method (Bandsintown::Event.on_sale_soon)
3
11
  * Corrected some typos in Bandsintown::Event spec
@@ -15,6 +15,7 @@ script/txt2html
15
15
  spec/bandsintown/artist_spec.rb
16
16
  spec/bandsintown/base_spec.rb
17
17
  spec/bandsintown/connection_spec.rb
18
+ spec/bandsintown/event/creation_helpers_spec.rb
18
19
  spec/bandsintown/event_spec.rb
19
20
  spec/bandsintown/venue_spec.rb
20
21
  spec/bandsintown_spec.rb
@@ -107,6 +107,48 @@ For more information visit http://www.bandsintown.com/api/requests.
107
107
  :radius => 10
108
108
  })
109
109
 
110
+ === Find venues with name beginning with "House of Blues" near San Diego, CA
111
+
112
+ venues = Bandsintown::Venue.search({
113
+ :query => "House of Blues",
114
+ :location => "San Diego, CA"
115
+ })
116
+
117
+ === Find all upcoming events for a given venue
118
+
119
+ # 1700 => Bandsintown venue ID for Paradise Rock Club in Boston, MA
120
+ venue = Bandsintown::Venue.new(1700)
121
+ events = venue.events
122
+
123
+ === Create an event on bandsintown.com using Bandsintown::Artist and Bandsintown::Venue objects
124
+
125
+ # 1700 => Bandsintown venue ID for Paradise Rock Club in Boston, MA
126
+ Bandsintown::Event.create({
127
+ :artists => [Bandsintown::Artist.new(:name => "The Roots")],
128
+ :datetime => Time.parse("2010-06-01 20:00"),
129
+ :venue => Bandsintown::Venue.new(1700),
130
+ :ticket_url => "http://www.example.com/tickets/123",
131
+ :ticket_price => 19.5,
132
+ :on_sale_datetime => Time.parse("2010-05-01 12:00")
133
+ })
134
+
135
+ === Create an event on bandsintown.com using artist names and venue location hash
136
+
137
+ # 1700 => Bandsintown venue ID for Paradise Rock Club in Boston, MA
138
+ Bandsintown::Event.create({
139
+ :artists => ["Evidence", "Alchemist"],
140
+ :datetime => "2010-06-01T19:30:00",
141
+ :venue => {
142
+ :name => "Paradise Rock Club",
143
+ :city => "Boston",
144
+ :region => "MA",
145
+ :country => "United States"
146
+ },
147
+ :ticket_url => "http://www.example.com/tickets/123",
148
+ :ticket_price => 15,
149
+ :on_sale_datetime => "2010-05-01T19:30:00"
150
+ })
151
+
110
152
  == Links
111
153
 
112
154
  * RDoc[http://bandsintown.rubyforge.org/rdoc]
data/Rakefile CHANGED
@@ -9,7 +9,8 @@ $hoe = Hoe.new('bandsintown', Bandsintown::VERSION) do |p|
9
9
  p.rubyforge_name = p.name # TODO this is default value
10
10
  p.extra_deps = [
11
11
  ['activesupport','>= 2.0.2'],
12
- ['json', '>= 1.1.3']
12
+ ['json', '>= 1.1.3'],
13
+ ['rest-client', '>= 1.5.0']
13
14
  ]
14
15
  p.extra_dev_deps = [
15
16
  ['newgem', ">= #{::Newgem::VERSION}"]
@@ -2,10 +2,10 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  require 'rubygems'
5
- require 'open-uri'
6
5
  require 'cgi'
7
- require 'activesupport'
6
+ require 'active_support'
8
7
  require 'json'
8
+ require 'rest_client'
9
9
 
10
10
  require 'bandsintown/base'
11
11
  require 'bandsintown/connection'
@@ -14,7 +14,7 @@ require 'bandsintown/event'
14
14
  require 'bandsintown/venue'
15
15
 
16
16
  module Bandsintown
17
- VERSION = '0.1.4'
17
+ VERSION = '0.2.0'
18
18
  class APIError < StandardError; end
19
19
  class << self
20
20
  # All Bandsintown API requests require an app_id parameter for identification.
@@ -1,7 +1,7 @@
1
1
  module Bandsintown
2
2
  class Artist < Base
3
3
 
4
- attr_accessor :name, :bandsintown_url, :mbid, :events, :upcoming_events_count
4
+ attr_accessor :name, :mbid, :events, :upcoming_events_count
5
5
 
6
6
  def initialize(options = {})
7
7
  @name = options[:name]
@@ -23,7 +23,7 @@ module Bandsintown
23
23
  #
24
24
  def events
25
25
  return @events unless @events.blank?
26
- @events = self.class.request_and_parse("#{api_name}/events").map { |event| Bandsintown::Event.build_from_json(event) }
26
+ @events = self.class.request_and_parse(:get, "#{api_name}/events").map { |event| Bandsintown::Event.build_from_json(event) }
27
27
  end
28
28
 
29
29
  # Used in api requests as the RESTful resource id for artists (http://api.bandsintown.com/artists/id/method).
@@ -70,16 +70,16 @@ module Bandsintown
70
70
  #
71
71
  def self.get(options = {})
72
72
  request_url = Bandsintown::Artist.new(options).api_name
73
- build_from_json(request_and_parse(request_url))
73
+ build_from_json(request_and_parse(:get, request_url))
74
74
  end
75
75
 
76
76
  def self.build_from_json(json_hash)
77
- artist = Bandsintown::Artist.new({})
78
- artist.name = json_hash['name']
79
- artist.mbid = json_hash['mbid']
80
- artist.bandsintown_url = json_hash['url']
81
- artist.upcoming_events_count = json_hash['upcoming_events_count']
82
- artist
77
+ returning Bandsintown::Artist.new({}) do |artist|
78
+ artist.name = json_hash['name']
79
+ artist.mbid = json_hash['mbid']
80
+ artist.bandsintown_url = json_hash['url']
81
+ artist.upcoming_events_count = json_hash['upcoming_events_count']
82
+ end
83
83
  end
84
84
 
85
85
  def self.resource_path
@@ -3,8 +3,12 @@ module Bandsintown
3
3
 
4
4
  attr_accessor :bandsintown_url
5
5
 
6
- def self.request(api_method, args={})
7
- self.connection.request(self.resource_path, api_method, args)
6
+ def self.request(http_method, api_method, args={})
7
+ case http_method
8
+ when :get then self.connection.get(self.resource_path, api_method, args)
9
+ when :post then self.connection.post(self.resource_path, api_method, args)
10
+ else raise ArgumentError, "only :get and :post requests are supported"
11
+ end
8
12
  end
9
13
 
10
14
  def self.connection
@@ -23,8 +27,18 @@ module Bandsintown
23
27
  end
24
28
  end
25
29
 
26
- def self.request_and_parse(api_method, args={})
27
- parse(request(api_method, args))
30
+ def self.request_and_parse(http_method, api_method, args={})
31
+ parse(request(http_method, api_method, args))
32
+ end
33
+
34
+ def to_hash
35
+ hash = {}
36
+ self.instance_variables.each do |ivar|
37
+ value = self.instance_variable_get(ivar)
38
+ next if value.blank?
39
+ hash[:"#{ivar.gsub('@', '')}"] = value
40
+ end
41
+ hash
28
42
  end
29
43
 
30
44
  end
@@ -1,21 +1,28 @@
1
1
  module Bandsintown
2
2
  class Connection
3
- attr_accessor :base_url
3
+ attr_accessor :base_url, :client
4
4
 
5
5
  def initialize(base_url)
6
6
  @base_url = base_url
7
7
  end
8
8
 
9
- def request(resource_path, method_path, args = {})
10
- request_url = "#{@base_url}/#{resource_path}/#{method_path}?#{encode(args.symbolize_keys)}"
9
+ def get(resource_path, method_path, params = {})
10
+ request_url = "#{@base_url}/#{resource_path}/#{method_path}?#{encode(params.symbolize_keys)}"
11
11
  begin
12
- open(request_url).read
13
- rescue OpenURI::HTTPError => error_response
14
- error_response.io.read
12
+ RestClient.get(request_url)
13
+ rescue RestClient::ResourceNotFound => error_response
14
+ error_response.response
15
15
  end
16
16
  end
17
17
 
18
- private
18
+ def post(resource_path, method_path, body = {})
19
+ request_url = "#{@base_url}/#{resource_path}/#{method_path}?#{encode({})}"
20
+ begin
21
+ RestClient.post(request_url, body.to_json, :content_type => :json, :accept => :json)
22
+ rescue RestClient::ResourceNotFound => error_response
23
+ error_response.response
24
+ end
25
+ end
19
26
 
20
27
  def encode(args = {})
21
28
  start_date = args.delete(:start_date)
@@ -1,6 +1,62 @@
1
1
  module Bandsintown
2
2
  class Event < Base
3
3
 
4
+ # Module with helper methods for formatting Bandsintown::Event.create params
5
+ module CreationHelpers
6
+ ISO_8601_FORMAT = "%Y-%m-%dT%H:%M:%S"
7
+
8
+ def self.included(klass)
9
+ klass.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ # Formats Time/DateTime/Date to ISO 8601 string, or return unmodified if passed as a String.
14
+ def parse_datetime(datetime)
15
+ case datetime
16
+ when Time, DateTime then datetime.strftime(ISO_8601_FORMAT)
17
+ when Date then (datetime + 19.hours).strftime(ISO_8601_FORMAT)
18
+ else datetime
19
+ end
20
+ end
21
+
22
+ # Returns a hash with venue attributes. If bandsintown_id is present this is preferred over location attributes.
23
+ # venue_data can be either a Bandsintown::Venue or a Hash.
24
+ def parse_venue(venue_data)
25
+ hash = venue_data.to_hash
26
+ bandsintown_id = hash[:id] || hash[:bandsintown_id]
27
+ venue = if bandsintown_id.blank?
28
+ {
29
+ :name => hash[:name],
30
+ :address => hash[:address],
31
+ :city => hash[:city],
32
+ :region => hash[:region],
33
+ :postalcode => hash[:postalcode],
34
+ :country => hash[:country],
35
+ :latitude => hash[:latitude],
36
+ :longitude => hash[:longitude]
37
+ }
38
+ else
39
+ { :id => bandsintown_id }
40
+ end
41
+ venue.reject { |k,v| v.blank? }
42
+ end
43
+
44
+ # Returns an array of hashes formatted either { :name => name } or { :mbid => mbid } based on each object in artist_data (can be Bandsintown::Artists, Hashes, or Strings)
45
+ def parse_artists(artist_data)
46
+ artist_data.map do |artist|
47
+ if artist.is_a?(String)
48
+ { :name => artist }
49
+ else
50
+ hash = artist.to_hash
51
+ hash[:mbid].blank? ? { :name => hash[:name] } : { :mbid => hash[:mbid] }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ include CreationHelpers
59
+
4
60
  attr_accessor :bandsintown_id, :datetime, :ticket_url, :artists, :venue, :status, :ticket_status, :on_sale_datetime
5
61
 
6
62
  def tickets_available?
@@ -44,7 +100,7 @@ module Bandsintown
44
100
  #
45
101
  def self.search(options = {})
46
102
  events = []
47
- self.request_and_parse("search", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
103
+ self.request_and_parse(:get, "search", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
48
104
  events
49
105
  end
50
106
 
@@ -53,7 +109,7 @@ module Bandsintown
53
109
  #
54
110
  def self.daily
55
111
  events = []
56
- self.request_and_parse("daily").each { |event| events << Bandsintown::Event.build_from_json(event) }
112
+ self.request_and_parse(:get, "daily").each { |event| events << Bandsintown::Event.build_from_json(event) }
57
113
  events
58
114
  end
59
115
 
@@ -75,7 +131,7 @@ module Bandsintown
75
131
  #
76
132
  def self.recommended(options = {})
77
133
  events = []
78
- self.request_and_parse("recommended", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
134
+ self.request_and_parse(:get, "recommended", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
79
135
  events
80
136
  end
81
137
 
@@ -96,27 +152,92 @@ module Bandsintown
96
152
  #
97
153
  def self.on_sale_soon(options = {})
98
154
  events = []
99
- self.request_and_parse("on_sale_soon", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
155
+ self.request_and_parse(:get, "on_sale_soon", options).each { |event| events << Bandsintown::Event.build_from_json(event) }
100
156
  events
101
157
  end
102
158
 
159
+ #This is used to create an event on bandsintown.com.
160
+ #Unless you have a trusted app_id, events added through the API will need to be approved before they are seen live.
161
+ #Contact Bandsintown if you are often adding events and would like a trusted account.
162
+ #See http://www.bandsintown.com/api/requests#events-create for more information.
163
+ #===options:
164
+ # :artists - an Array of artist data with each element in one of the following formats:
165
+ # * artist name String
166
+ # * Hash of { :name => artist name } or { :mbid => music brainz id }
167
+ # * Bandsintown::Artist object with :mbid or :name
168
+ # :datetime - use one of the following formats:
169
+ # * String in ISO-8601 format: '2010-06-01T19:30:00'
170
+ # * Any object that responds to strftime (Date/Time/DateTime)
171
+ # :venue - use one of the following formats:
172
+ # * Hash of { :id => bandsintown id } or location data (:name, :address, :city, :region, :postalcode, :country, :latitude, :longitude)
173
+ # * :name, :city, :region, :country are required for venues in the United States
174
+ # * :name, :city, :country are required for venues outside the United States
175
+ # * :latitude, :longitude, :address, and :postalcode are optional
176
+ # * Bandsintown::Venue object
177
+ # :on_sale_datetime - use the same formats as :datetime
178
+ # :ticket_url - string with a link to where you can buy tickets to the event
179
+ # :ticket_price - a number or string with ticket price
180
+ #
181
+ #===notes:
182
+ # * :artists, :datetime, and :venue are required, all other options are optional.
183
+ # * If :mbid and :name are available in an artist Hash or Bandsintown::Artist, :mbid is used first.
184
+ # * If :bandsintown_id and location data are given in a venue Hash or Bandsintown::Venue, :bandsintown_id is used first.
185
+ # * If you have a trusted app_id, this method will return a populated Bandsintown::Event object.
186
+ #
187
+ #===examples:
188
+ #Create an event for Evidence and Alchemist at House of Blues - San Diego on June 1st, 2010 using Bandsintown::Artists and Bandsintown::Venue:
189
+ # evidence = Bandsintown::Artist.new(:name => "Evidence")
190
+ # alchemist = Bandsintown::Artist.new(:name => "Alchemist")
191
+ # venue = Bandsintown::Venue.new(727861) # id for House of Blues - San Diego
192
+ # Bandsintown::Event.create(:artists => [evidence, alchemist], :venue => venue, :datetime => "2010-06-01T19:30:00")
193
+ # => "Event submitted successfully (pending approval)"
194
+ #
195
+ #Create an event for The Roots at Paradise Rock Club in Boston on June 1st, 2010 using artist name, venue location data, including full ticketing and price data:
196
+ # paradise = { :name => "Paradise Rock Club", :address => "967 Commonwealth Ave", :city => "Boston", :region => "MA", :country => "United States" }
197
+ # Bandsintown::Event.create(:artists => ["The Roots"], :venue => paradise, :datetime => Time.parse("2010-06-01 20:00"), :on_sale_datetime => Time.parse("2010-05-01 12:00"), :ticket_url => "http://www.example.com/tickets/123", :ticket_price => 19.5)
198
+ # => "Event submitted successfully (pending approval)"
199
+ #
200
+ #Create an event with a trusted app_id
201
+ # Bandsintown::Event.create(:artists => ["The Roots"], :datetime => Date.today, :venue => { :id => 123 })
202
+ # => Bandsintown::Event with populated attributes
203
+ #
204
+ def self.create(options = {})
205
+ event_data = {
206
+ :artists => self.parse_artists(options[:artists]),
207
+ :venue => self.parse_venue(options[:venue]),
208
+ :datetime => self.parse_datetime(options[:datetime]),
209
+ :on_sale_datetime => self.parse_datetime(options[:on_sale_datetime]),
210
+ :ticket_url => options[:ticket_url],
211
+ :ticket_price => options[:ticket_price]
212
+ }.reject { |k,v| v.blank? }
213
+
214
+ response = self.request_and_parse(:post, "create", :event => event_data)
215
+
216
+ if response.key?("message")
217
+ response["message"]
218
+ else
219
+ Bandsintown::Event.build_from_json(response["event"])
220
+ end
221
+ end
222
+
103
223
  def self.resource_path
104
224
  "events"
105
225
  end
106
226
 
107
227
  def self.build_from_json(json_hash)
108
- event = Bandsintown::Event.new
109
- event.bandsintown_id = json_hash["id"]
110
- event.bandsintown_url = json_hash["url"]
111
- event.datetime = Time.parse(json_hash["datetime"])
112
- event.ticket_url = json_hash["ticket_url"]
113
- event.status = json_hash["status"]
114
- event.ticket_status = json_hash["ticket_status"]
115
- event.on_sale_datetime = Time.parse(json_hash["on_sale_datetime"]) rescue nil
116
- event.venue = Bandsintown::Venue.new(json_hash["venue"])
117
- event.artists = json_hash["artists"].map { |artist| Bandsintown::Artist.new(artist.symbolize_keys) }
118
- event
228
+ returning Bandsintown::Event.new do |event|
229
+ event.bandsintown_id = json_hash["id"]
230
+ event.bandsintown_url = json_hash["url"]
231
+ event.datetime = Time.parse(json_hash["datetime"])
232
+ event.ticket_url = json_hash["ticket_url"]
233
+ event.status = json_hash["status"]
234
+ event.ticket_status = json_hash["ticket_status"]
235
+ event.on_sale_datetime = Time.parse(json_hash["on_sale_datetime"]) rescue nil
236
+ event.venue = Bandsintown::Venue.build_from_json(json_hash["venue"])
237
+ event.artists = json_hash["artists"].map { |artist| Bandsintown::Artist.new(artist.symbolize_keys) }
238
+ end
119
239
  end
240
+
120
241
 
121
242
  end
122
243
  end
@@ -1,18 +1,73 @@
1
1
  module Bandsintown
2
2
  class Venue < Base
3
+ attr_accessor :name, :bandsintown_id, :city, :region, :country, :latitude, :longitude, :events
3
4
 
4
- attr_accessor :name, :bandsintown_id, :region, :city, :country, :latitude, :longitude
5
+ #Note - address and postalcode are not returned in API responses, but they are accepted when passing venue data to Bandsintown::Event.create.
6
+ attr_accessor :address, :postalcode
5
7
 
6
- def initialize(args={})
7
- @name = args["name"]
8
- @bandsintown_url = args["url"]
9
- @bandsintown_id = args["id"]
10
- @region = args["region"]
11
- @city = args["city"]
12
- @country = args["country"]
13
- @latitude = args["latitude"]
14
- @longitude = args["longitude"]
15
- end
8
+ def initialize(bandsintown_id)
9
+ @bandsintown_id = bandsintown_id
10
+ end
11
+
12
+ #Returns an array of Bandsintown::Event objects for each of the venues's upcoming events available through bandsintown.com.
13
+ #See http://www.bandsintown.com/api/requests#venues-events for more information.
14
+ #
15
+ #====example:
16
+ #
17
+ # # using Paradise Rock Club in Boston, MA (bandsintown id 1700)
18
+ # venue = Bandsintown::Venue.new(1700)
19
+ # upcoming_paradise_events = venue.events
20
+ #
21
+ def events
22
+ @events ||= self.class.request_and_parse(:get, "#{@bandsintown_id}/events").map { |event_hash| Bandsintown::Event.build_from_json(event_hash) }
23
+ end
16
24
 
25
+ #Returns an array of Bandsintown::Venue objects matching the options passed.
26
+ #See http://www.bandsintown.com/api/requests#venues-search for more information.
27
+ #====options:
28
+ # :query - a string to match the beginning of venue names
29
+ # :location - a string with one of the following formats:
30
+ # * 'city, state' for United States and Canada
31
+ # * 'city, country' for other countries
32
+ # * 'latitude,longitude'
33
+ # * ip address - will use the location of the passed ip address
34
+ # * 'use_geoip' - will use the location of the ip address that made the request
35
+ # :radius - a number in miles. API default is 25, maximum is 150.
36
+ # :per_page - number of results per response. Default is 5, maximum is 100.
37
+ # :page - offset for paginated results. API default is 1.
38
+ #
39
+ #====notes:
40
+ #:query is required for this request, all other arguments are optional.
41
+ #
42
+ #====examples:
43
+ #All venues (first page w/ 5 results) with name beginning with 'House of Blues':
44
+ # Bandsintown::Venue.search(:query => 'House of Blues')
45
+ #
46
+ #All venues (first page w/ 5 results) with name beginning with 'House of' within 25 miles of San Diego, CA:
47
+ # Bandsintown::Venue.search(:query => "House of", :location => "San Diego, CA")
48
+ #
49
+ #Second page of all venues near the request's ip address with name beginning with "Club" and 100 results per page:
50
+ # Bandsintown::Venue.search(:query => "Club", :per_page => 100, :page => 2, :location => "use_geoip")
51
+ #
52
+ def self.search(options = {})
53
+ self.request_and_parse(:get, "search", options).map { |venue_hash| Bandsintown::Venue.build_from_json(venue_hash) }
54
+ end
55
+
56
+ def self.resource_path
57
+ "venues"
58
+ end
59
+
60
+ def self.build_from_json(args={})
61
+ returning Bandsintown::Venue.new(args['id']) do |v|
62
+ v.name = args["name"]
63
+ v.bandsintown_url = args["url"]
64
+ v.bandsintown_id = args["id"]
65
+ v.region = args["region"]
66
+ v.city = args["city"]
67
+ v.country = args["country"]
68
+ v.latitude = args["latitude"]
69
+ v.longitude = args["longitude"]
70
+ end
71
+ end
17
72
  end
18
73
  end