bandsintown 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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