Skip to content

Instantly share code, notes, and snippets.

@jm
Created August 5, 2010 05:06
Show Gist options
  • Save jm/badc125acb116bec7bd6 to your computer and use it in GitHub Desktop.
Save jm/badc125acb116bec7bd6 to your computer and use it in GitHub Desktop.

wufoo.rb - A Wufoo Ruby wrapper

Jeremy McAnally jeremy@intridea.com http://jeremymcanally.com/

wufoo.rb is an elegant Ruby wrapper based on HTTParty. It's a simple to use library that implements about 98% of the Wufoo API (it does not implement the e-mail login/API key fetching portion of the API since I have not applied to be a partner to get an integration key).

Examples (other than example.rb)

To get a form's entries and view their attributes/answers:

require 'pp'
require 'wufoo'

session = Wufoo.login("subdomain", "key")
form = session.forms.first

form.forms.entries.each do |entry|
  pp entry.attrs
end

To submit an entry to a form:

require 'wufoo'

session = Wufoo.login("subdomain", "key")
session.forms.first.entries.create("Field1" => "My answer!", "Field2" => "Other answer!")

The objects returned from calls like sessions.forms are collection proxies, so (a) the API calls are not made until necessary (i.e., you call an enumerable method like each or first on it), (b) they are queryable (e.g., you can do session.forms.find('my-hash-or-url')), and (c) they act like arrays (so they respond to nearly any enumerable method in Ruby).

I'm using this to write an A/B testing app for forms and maybe a couple of other things to submit to the contest. Thanks for looking!

require 'wufoo'
require 'pp'
session = Wufoo.login("subdomain here", "api key here")
puts "Wufoo example script ftw"
puts
puts "*" * 80
puts
session.forms.each do |form|
puts "Form: #{form.name}"
puts "Entries count: #{form.entries.size}"
puts "-" * 80
puts
end
puts "creating an entry on the first form..."
session.forms.first.entries.create("Field1" => "Gnarly barley.")
puts "Form: #{session.forms.first.name}"
puts "Entries count: #{session.forms.first.entries.size}"
puts
session.reports.each do |form|
puts "Form: #{form.name}"
puts "Entries count: #{form.entries.size}"
puts "-" * 80
puts
end
require 'wufoo'
require 'test/unit'
class WufooSessionTest < Test::Unit::TestCase
def setup
@session ||= Wufoo.login("things", "w00t")
end
def test_return_value
assert @session.is_a?(Wufoo::Session)
end
def test_api_key
assert_equal "w00t", @session.api_key
end
def test_subdomain
assert_equal "things", @session.subdomain
end
def test_credentials
creds = {:username => "w00t", :password => "n3rdZr00l"}
assert_equal creds, @session.credentials
end
def test_base_url
assert_equal "https://things.wufoo.com/api/v3/forms", (@session.base_url % "forms")
end
def test_users
assert @session.users.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::User, @session.users.resource_class
assert_nil @session.users.parent_path
end
def test_reports
assert @session.reports.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Report, @session.reports.resource_class
assert_nil @session.reports.parent_path
end
def test_forms
assert @session.forms.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Form, @session.forms.resource_class
assert_nil @session.forms.parent_path
end
end
class StubResource
class <<self
def collection_path(parent_path)
"whatevers"
end
def get_all(session, parent_path)
a = ["one", "two", "three"]
class <<a
attr_accessor :called
def size
@called = true
9
end
end
a
end
def get(session, id, parent_path)
"hello"
end
end
end
class ResourceCollectionTest < Test::Unit::TestCase
def setup
@collection ||= Wufoo::ResourceCollection.new(StubResource, "fake session")
end
def test_delegation
assert_equal 9, @collection.size
assert @collection.to_a.called
end
def test_reload
assert_equal ["one", "two", "three"], @collection.reload
assert_equal ["one", "two", "three"], @collection.to_a
end
def test_find
assert_equal "hello", @collection.find(1234)
end
end
class PostableCollectionTest < Test::Unit::TestCase
def setup
@collection ||= Wufoo::PostableCollection.new(StubResource, Wufoo.login("hi", "hello"))
class <<@collection
def post_resource(url, attrs)
{"Success" => "1", "EntryID" => "1"}
end
end
end
def test_create
assert @collection.create("Field1" => "Hello!")
end
def test_create!
assert_nothing_raised do
@collection.create!("Field1" => "Hello!")
end
end
end
class ResourceTest < Test::Unit::TestCase
class Fake < Wufoo::Resource
end
class Party < Wufoo::Resource
resource_collection_name "parties"
end
def setup
@session ||= Wufoo.login("hello", "goodbye")
end
def test_base_name
assert "Fake", Fake.base_name
end
def test_collection_path
assert_equal "fakes.json", Fake.collection_path
end
def test_collection_path_with_parent
assert_equal "realies/19/fakes.json", Fake.collection_path("realies/19/%s")
end
def test_resource_path
assert_equal "fakes/19.json", Fake.resource_path(19)
end
def test_resource_path_with_parent
assert_equal "realies/288/fakes/23.json", Fake.resource_path(23, "realies/288/%s")
end
def test_collection_name_manual_set
assert_equal "parties.json", Party.collection_path
end
def test_from_params
obj = Fake.from_params({'thing' => "first", 'what' => "second"}, @session)
assert_equal ({'thing' => "first", 'what' => "second"}), obj.attrs
end
def test_attr_methods
obj = Fake.from_params({'thing' => "first", 'what' => "second"}, @session)
assert_equal "first", obj.thing
end
end
class UserTest < Test::Unit::TestCase
def setup
@u ||= Wufoo::User.from_params({'Image' => 'hiya.jpg'}, Wufoo.login("dude", "dudette"))
end
def test_avatar_url
assert_equal "https://dude.wufoo.com/api/v3/images/avatars/big/hiya.jpg", @u.avatar_url
end
def test_avatar_url
assert_equal "https://dude.wufoo.com/api/v3/images/avatars/big/hiya.jpg", @u.avatar_url
end
end
class FormTest < Test::Unit::TestCase
def setup
@form = Wufoo::Form.from_params({'Name' => "Untitled", 'Hash' => "1234"}, Wufoo.login("pirate", "princess"))
end
def test_parental_path
assert_equal "forms/1234/%s", @form.parental_path
end
def test_fields
assert @form.fields.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Field, @form.fields.resource_class
assert_equal "forms/1234/%s", @form.fields.parent_path
end
def test_entries
assert @form.entries.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Entry, @form.entries.resource_class
assert_equal "forms/1234/%s", @form.entries.parent_path
end
def test_comments
assert @form.comments.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Comment, @form.comments.resource_class
assert_equal "forms/1234/%s", @form.comments.parent_path
end
end
class ReportTest < Test::Unit::TestCase
def setup
@report = Wufoo::Report.from_params({'Name' => "Untitled", 'Hash' => "1234"}, Wufoo.login("pirate", "princess"))
end
def test_parental_path
assert_equal "reports/1234/%s", @report.parental_path
end
def test_fields
assert @report.fields.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Field, @report.fields.resource_class
assert_equal "reports/1234/%s", @report.fields.parent_path
end
def test_entries
assert @report.entries.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Entry, @report.entries.resource_class
assert_equal "reports/1234/%s", @report.entries.parent_path
end
def test_widgets
assert @report.widgets.is_a?(Wufoo::ResourceCollection)
assert_equal Wufoo::Widget, @report.widgets.resource_class
assert_equal "reports/1234/%s", @report.widgets.parent_path
end
end
# gem install httparty
# ruby wufoo.rb
require 'rubygems'
require 'httparty'
require 'forwardable'
module Wufoo
API_VERSION = 3
BASE_URL = "https://%s.wufoo.com/api/v%s/%s"
class <<self
def login(subdomain, api_key)
Session.new(subdomain, api_key)
end
end
class Session
attr_accessor :subdomain, :api_key
def initialize(subdomain, api_key)
@subdomain = subdomain
@api_key = api_key
end
def credentials
{:username => @api_key, :password => "n3rdZr00l"}
end
def base_url
Wufoo::BASE_URL.dup % [@subdomain, Wufoo::API_VERSION, "%s"]
end
def users
ResourceCollection.new(User, self)
end
def forms
ResourceCollection.new(Form, self)
end
def reports
ResourceCollection.new(Report , self)
end
end
class ResourceCollection
extend Forwardable
attr_accessor :resource_class, :parent_path
def initialize(klass, session, parent_path=nil)
@resource_class = klass
@parent_path = parent_path
@session = session
end
def to_a
@array ||= reload
end
def reload
@array = @resource_class.get_all(@session, @parent_path)
end
alias_method :all, :to_a
def find(id)
@resource_class.get(@session, id, @parent_path)
end
def_delegators :to_a, :clear, :first, :push, :shift, :size, :length, :each, :map, :select, :collect
end
class PostableCollection < ResourceCollection
def create(attrs)
url = @session.base_url % @resource_class.collection_path(@parent_path)
response = post_resource(url, attrs)
if response['Success'].to_s == "1"
reload
response['EntryID']
else
false
end
end
def create!(attrs)
raise "Save failed!" unless create(attrs)
end
alias_method :submit, :create
alias_method :submit!, :create!
def post_resource(url, attrs)
HTTParty.post(url, :body => attrs, :basic_auth => @session.credentials)
end
end
class Resource
class <<self
def resource_collection_name(value)
@collection_path = value
end
def base_name
@base_name ||= name.gsub(/(.*)\:\:/, '').downcase
end
def collection_path(parent_path="%s")
(parent_path || "%s") % (@collection_path ? "#{@collection_path}.json" : "#{base_name}s.json")
end
def resource_path(id, parent_path="%s")
(parent_path || "%s") % (@collection_path ? "#{@collection_path}/#{id}" : "#{base_name}s/#{id}.json")
end
def get_all(session, parent_path=nil)
url = session.base_url % collection_path(parent_path)
HTTParty.get(url, :basic_auth => session.credentials).first.last.map {|values| from_params(values, session) }
end
def get(session, identifier, parent_path=nil)
url = session.base_url % resource_path(identifier, parent_path)
from_params(HTTParty.get(url, :basic_auth => session.credentials), session)
end
def from_params(params, session)
new_object = new
new_object.attrs = params
new_object.session = session
new_object
end
end
attr_accessor :attrs, :session
def initialize(attrs={})
@attrs = attrs
end
def method_missing(m, *args)
camel_cased = m.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
@attrs[m.to_s] or @attrs[camel_cased] or super
end
end
class User < Resource
def avatar_url(size="big")
session.base_url % "images/avatars/#{size}/#{self.image}"
end
end
class Form < Resource
def parental_path
"forms/#{self.hash}/%s"
end
def fields
ResourceCollection.new(Field, session, parental_path)
end
def entries
PostableCollection.new(Entry, session, parental_path)
end
def comments
ResourceCollection.new(Comment, session, parental_path)
end
def hash
@attrs['Hash']
end
end
class Report < Resource
def parental_path
"reports/#{self.hash}/%s"
end
def fields
ResourceCollection.new(Field, session, parental_path)
end
def widgets
ResourceCollection.new(Widget, session, parental_path)
end
def entries
PostableCollection.new(Entry, session, parental_path)
end
def hash
@attrs['Hash']
end
end
# Owned by forms and reports
class Entry < Resource
resource_collection_name "entries"
end
# Owned by forms and reports
class Field < Resource
end
# Owned by reports
class Widget < Resource
end
# Owned by forms
class Comment < Resource
end
# Owned by forms
class WebHook < Resource
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment