Skip to content

Commit

Permalink
Improve commenting and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
therabidbanana committed May 29, 2011
1 parent 1b2443e commit c5e3632
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 58 deletions.
44 changes: 44 additions & 0 deletions ImplementationGotchas.markdown
@@ -0,0 +1,44 @@
Eventbrite's API is very poorly done. I've catalogued a list of
issues you'll likely encounter if you want to make your own integration with
their API. Most of the code in this library exists simply to fix these
issues - otherwise you could probably just use HTTParty directly.


API Gotchas
------------

A list of sticking points for anyone attempting their own integration with the Eventbrite API:


__/get => /update variable inconsistencies__

* event.id => event.event_id
* event.timezone (Olson format, ex: "US/Central") => event.timezone (GMT offset hours, ex: "GMT-05")
* event.privacy (String representing privacy "Private"|"Public") => event.privacy (Boolean 0 = public)
* event.url => event.personalized_url
* venue.address => venue.adress
* venue.address_2 => venue.adress_2
* venue.name => venue.venue
* event.tickets.ticket.start_date => ticket.start_sales
* event.tickets.ticket.end_date => ticket.end_sales
* event.tickets.ticket.visible (1 is visible) => ticket.hide (y is hidden, n is visible)
* event.tickets.ticket.quantity_available => ticket.quantity

__Fields you can't edit__

* event.category
* event.tags
* event.logo
* ticket.hide (on /ticket_new. You must save the ticket then call /ticket_update to hide)

__Documentation errors__

* /venue_new and /venue_update does not throw an error if "venue" is invalid/non-unique/empty.
* Dates are not technically ISO 8601 (ISO 8601 specifies a "T" - not a space - between date and time, so passing perfectly formatted ISO 8601 datetime strings such as those a standard library would provide will cause errors)
* Error description for event Privacy Error: <pre>"The privacy field must be equal to 0 (public) or 1 (private)"</pre> -> This is the opposite of the actual case. 0 is private and 1 is public, as described in other places within the API.
* /ticket_new and /ticket_update do not throw errors if quantity not set

__Other gotchas__

* Venue object included in event has extra "Lat-Long" attribute, along with "latitude" and "longitude". If you're turning a result into an object, this might cause an error if you don't suspect it.
* Timezones are weird (nothing EventBrite can do about this one): GMT offset for timezones is always computed in standard time (don't adjust for Daylight Savings, unlike UTC offset). This weirdness means that once you save the timezone the Olson description might not match what you think it should (ex. US/Eastern becomes GMT-5 which then becomes America/Bogota)
59 changes: 25 additions & 34 deletions README.markdown
Expand Up @@ -19,6 +19,20 @@ Usage

Some basic (yardoc generated) documentation availabe at: http://therabidbanana.github.com/eventbrite/

This library attempts to create an almost ActiveResource like wrapper
around the Eventbrite api. The following objects are available:

* Attendee (can only be viewed, not edited)
* Discount
* Event (has many Tickets, Attendees, Discounts, has one Venue and
one Organizer)
* Organizer (has many Events)
* Ticket
* User (has many Events, Organizers, Venues)
* Venue



Authentication
--------------
Many methods require user authentication. For these methods, you can pass a user object as an authentication token, and the user's api_key will automatically be used for the request.
Expand All @@ -27,7 +41,7 @@ Example:

Eventbrite::Event.new({"x" => "y"... , "user" => user})

Known Bugs
Known Issues
----------

1. This library's testing coverage is almost zero.
Expand All @@ -46,43 +60,20 @@ Learn more about EventBrite's App Key policy here: [Terms of Service](http://www
Register for your own app key here: [Request a Key](http://www.eventbrite.com/api/key/)


API Gotchas:
--------------------

A list of sticking points for anyone attempting their own integration with the EventBrite API:

__/get => /update variable inconsistencies__

* event.id => event.event_id
* event.timezone (Olson format, ex: "US/Central") => event.timezone (GMT offset hours, ex: "GMT-05")
* event.privacy (String representing privacy "Private"|"Public") => event.privacy (Boolean 0 = public)
* event.url => event.personalized_url
* venue.address => venue.adress
* venue.address_2 => venue.adress_2
* venue.name => venue.venue
* event.tickets.ticket.start_date => ticket.start_sales
* event.tickets.ticket.end_date => ticket.end_sales
* event.tickets.ticket.visible (1 is visible) => ticket.hide (y is hidden, n is visible)
* event.tickets.ticket.quantity_available => ticket.quantity

__Fields you can't edit__

* event.category
* event.tags
* event.logo
* ticket.hide (on /ticket_new. You must save the ticket then call /ticket_update to hide)
API Gotchas
------------

__Documentation errors__
I've compiled a list of issues developers may encounter with the poorly
designed Eventbrite API (in case you're writing your own integration
from scratch in Ruby or any other language).

* /venue_new and /venue_update does not throw an error if "venue" is invalid/non-unique/empty.
* Dates are not technically ISO 8601 (ISO 8601 specifies a "T" - not a space - between date and time, so passing perfectly formatted ISO 8601 datetime strings such as those a standard library would provide will cause errors)
* Error description for event Privacy Error: <pre>"The privacy field must be equal to 0 (public) or 1 (private)"</pre> -> This is the opposite of the actual case. 0 is private and 1 is public, as described in other places within the API.
* /ticket_new and /ticket_update do not throw errors if quantity not set
There are many places where variable names are misspelled, or format is
changed, and many places where the API documentation is flat out wrong
(dates are not ISO8601 anywhere in the eventbrite api, though they claim
they are).

__Other gotchas__
Read the details at: http://therabidbanana.github.com/eventbrite/file.ImplementationGotchas.html

* Venue object included in event has extra "Lat-Long" attribute, along with "latitude" and "longitude". If you're turning a result into an object, this might cause an error if you don't suspect it.
* Timezones are weird (nothing EventBrite can do about this one): GMT offset for timezones is always computed in standard time (don't adjust for Daylight Savings, unlike UTC offset). This weirdness means that once you save the timezone the Olson description might not match what you think it should (ex. US/Eastern becomes GMT-5 which then becomes America/Bogota)

Note on Patches/Pull Requests
-----------------------------
Expand Down
54 changes: 46 additions & 8 deletions lib/eventbrite/api_object.rb
@@ -1,11 +1,18 @@
require 'eventbrite/api_object_class_methods'
require 'eventbrite/api_object_relationships'
module Eventbrite
# @private
# Class for generalizing API interactions
class ApiObject
extend Eventbrite::ApiObjectClassMethods
include Eventbrite::ApiObjectRelationships

attr_accessor :id, :owner
# @private
# Individual attribute tracking
attr_accessor :attributes, :relations, :collections
# @private
# Dirty tracking
attr_accessor :dirty, :dirty_relations, :dirty_collections

def initialize(owner = false, hash = {})
Expand All @@ -18,34 +25,51 @@ def initialize(owner = false, hash = {})
end
end

# @private
# Initializes all attributes and dirtry tracking
def preinit
@attributes = @relations = @collections = {}
@dirty = @dirty_relations = @dirty_collections = {}
end

# @private
# Marks everything as clean.
def clean!
@dirty = @dirty_relations = @dirty_collections = {}
end

# @private
# Callback after initialization
def init; end

# @private
# Standard getter for attributes array
def attribute_get(key); @attributes[key]; end

# @private
# Standard setter for attributes, also tracking dirtiness
def attribute_set(key, val, no_dirty = false)
@dirty[key] = true if(@attributes[key] != val && !no_dirty)
@attributes[key] = val
after_attribute_set
end

# @private
# Callback for when attributes are set.
def after_attribute_set
end


# @private
# Convert id to integer
def id=(new_id,*args)
@id = new_id.to_i
end

# Loads attributes and relations from a hash,
# or makes an API call to retrieve necessary data if load hash is empty
#
# The no_dirty attribute allows you to force a dirty tracking to recognize
# the object as clean.
def load(hash = {}, no_dirty = false)
if hash.nil? || hash.size == 0
response = Eventbrite.call("#{self.class.singlet_name}_get", prep_api_hash('get'))
Expand All @@ -60,7 +84,8 @@ def load(hash = {}, no_dirty = false)
after_load(hash)
end


# @private
# Loads all attributes from hash unless set to ignore.
def init_with_hash(hash, no_dirty = false)
@attributes ||= {}
hash.each do |k, v|
Expand All @@ -74,12 +99,13 @@ def init_with_hash(hash, no_dirty = false)
end
end


# @private
# A callback for after loads
def after_load(hash = {})
hash
end

# @private
# A callback for methods to clean up the hash a bit
# allowing subclasses to insert the user if necessary
def prep_api_hash(method = 'get', hash = {})
Expand All @@ -90,28 +116,33 @@ def prep_api_hash(method = 'get', hash = {})
hash
end

# @private
# Callbacks for individual hash changes
# These are added to the updatable_hash, if appropriate
# These are called by prep_api_hash
def api_hash; {:user => owner}; end
# @private
def update_hash; {:id => id}; end
# @private
def get_hash; {:id => id}; end
# @private
def new_hash; {}; end

# @private
def nested_hash; {:user => owner, :id => id}; end


# Forces a clean load from the remote API. Load can be passed a hash of local
# Forces a clean load from the remote API. #load can be passed a hash of local
# values to avoid an API call, but this circumvents it.
def load!
load({}, true)
end


# @private
# Callback that happens before saving. Allows modification of options
def before_save(opts = {}); opts; end

# Save function. Can alter functionality by changing callbacks
# Save function. Individual objects can alter functionality by changing callbacks
def save(opts = {})
return false unless dirty?
opts.merge!(updatable_hash(self.class.requires))
Expand All @@ -133,14 +164,18 @@ def save(opts = {})
call
end

# @private
# After save callback, only called on a new call
def after_new; end
# @private
# After save callback, only called on an update call
def after_update; end
# @private
# After save callback
def after_save; end


# @private
# Creates a hash of attributes for an update
def updatable_hash(always_dirty = [])
updates = {}
@attributes.each do |k, v|
Expand All @@ -156,22 +191,25 @@ def updatable_hash(always_dirty = [])
updates
end

# @private
def inspect
"#<#{self.class.to_s}:#{self.id} @attributes=#{@attributes.inspect}>"
end

# Returns a simple string representing the class and its attributes
def to_s
"#<#{self.class.to_s}:#{self.id} @attributes=#{@attributes.inspect}>"
end


# Defines whether the object has been loaded from a remote source. If not, then
# we assume it's new when saving.
def loaded?
(!@id.nil? || @id == "")
end

# Something is dirty if it's never been loaded or if the @dirty
# hash contains something.
# hash contains something (which happens when an attribute is changed)
def dirty?
@dirty ||= {}
@dirty.size > 0 || !loaded?
Expand Down

0 comments on commit c5e3632

Please sign in to comment.