purzelrakete-boomloop 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 +2 -0
- data/README.txt +36 -0
- data/bin/boomloop +43 -0
- data/config/credentials.yml +0 -0
- data/lib/authentication/client.rb +37 -0
- data/lib/authentication/credentials.rb +29 -0
- data/lib/authentication/store/base.rb +11 -0
- data/lib/authentication/store/yaml_store.rb +40 -0
- data/lib/boomloop.rb +14 -0
- data/lib/resources/base.rb +84 -0
- data/lib/resources/event.rb +6 -0
- data/lib/resources/event_series.rb +6 -0
- data/lib/resources/place.rb +6 -0
- data/lib/resources/ticket_category.rb +6 -0
- data/lib/version.rb +9 -0
- data/test/boomloop_resource_test.rb +105 -0
- data/test/mocks/monster.rb +6 -0
- data/test/test_helper.rb +8 -0
- metadata +81 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
This is a Ruby API with which you can access boomloop.com.
|
2
|
+
|
3
|
+
== API Documentation
|
4
|
+
|
5
|
+
You can find documentation about the underlying boomloop REST api here: http://boomloop.com/api.
|
6
|
+
|
7
|
+
== Getting started
|
8
|
+
|
9
|
+
You have to register your api client on boomloop.com before using the api. This is
|
10
|
+
a 3 step process.
|
11
|
+
|
12
|
+
1. Start by signing up on boomloop.com and register your application. You can do that
|
13
|
+
here: http://boomloop.com/oauth/new. When you have registered your application, you will
|
14
|
+
receive a consumer key and a consumer secret.
|
15
|
+
|
16
|
+
2. Setup the api like this: boomloop -k <your consumer key> -s <your consumer secret>
|
17
|
+
You will be given a URL – go there and authorize your application.
|
18
|
+
|
19
|
+
3. Go back to your terminal window and press <enter>. You're registered :).
|
20
|
+
|
21
|
+
== Example API Call
|
22
|
+
|
23
|
+
Once you have authorized your client, you can make the first API call like this:
|
24
|
+
|
25
|
+
client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
|
26
|
+
connection = client.create_accesstoken
|
27
|
+
events = connection.get("/events").body
|
28
|
+
|
29
|
+
This returns the raw XML. We'll see how to get Event Objects next.
|
30
|
+
|
31
|
+
The Client takes two arguments - a config key, and an authentication name. When you authorize using
|
32
|
+
the boomloop script, a config key 'api' is created in the YAMLStore. This holds your the access
|
33
|
+
tokens so that you don't need to negotiate them every time you want to make a request.
|
34
|
+
|
35
|
+
|
36
|
+
|
data/bin/boomloop
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
%w(optparse ostruct).each &method(:require)
|
3
|
+
require File.dirname(__FILE__) + '/../lib/boomloop'
|
4
|
+
|
5
|
+
def error(text); puts text; exit; end
|
6
|
+
|
7
|
+
options = OpenStruct.new
|
8
|
+
parser = OptionParser.new
|
9
|
+
parser.banner = %Q{
|
10
|
+
== Usage
|
11
|
+
|
12
|
+
You have to register your api client on boomloop.com before using the api. This is
|
13
|
+
a 3 step process.
|
14
|
+
|
15
|
+
1. Start by signing up on boomloop.com and register your application. You can do that
|
16
|
+
here: http://boomloop.com/oauth/new. When you have registered your application, you will
|
17
|
+
receive a consumer key and a consumer secret.
|
18
|
+
|
19
|
+
2. Setup the api like this: boomloop -k <your consumer key> -s <your consumer secret>
|
20
|
+
You will be given a URL – go there and authorize your application.
|
21
|
+
|
22
|
+
3. Go back to your terminal window and press <enter>. Now do something with the API :).
|
23
|
+
|
24
|
+
}
|
25
|
+
|
26
|
+
parser.on_tail("-h", "--help", "Show this message") { error(parser) }
|
27
|
+
parser.on("-k", "--key KEY", "Your consumer KEY") { |key| options.key = key }
|
28
|
+
parser.on("-s", "--secret SECRET", "Your consumer SECRET") { |secret| options.secret = secret }
|
29
|
+
|
30
|
+
# parse.
|
31
|
+
parser.parse!(ARGV) rescue error(parser)
|
32
|
+
error(parser) unless options.secret && options.key
|
33
|
+
|
34
|
+
# negotiate the tokens
|
35
|
+
client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
|
36
|
+
request_token = client.consume(options.key, options.secret) rescue error("Invalid. Check your credentials at http://boomloop.com/oauth/applications.")
|
37
|
+
puts "\nalmost ready! go to #{ request_token.authorize_url }\n\n press enter when you're done... <waiting>\n"
|
38
|
+
|
39
|
+
while !@access_token && gets
|
40
|
+
@access_token = client.activate rescue nil
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "your keys have been activated. you're ready to use the api. \n\n"
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Boomloop
|
2
|
+
module Authentication
|
3
|
+
class Client
|
4
|
+
attr_accessor :store, :user
|
5
|
+
attr_accessor :consumer, :request_token, :access_token
|
6
|
+
|
7
|
+
def initialize(user, store)
|
8
|
+
self.user = user
|
9
|
+
self.store = store
|
10
|
+
end
|
11
|
+
|
12
|
+
def consume(key, secret)
|
13
|
+
self.create_consumer(key, secret)
|
14
|
+
self.request_token = self.consumer.get_request_token
|
15
|
+
self.store.find(user).update(:consumer_key => key, :consumer_secret => secret)
|
16
|
+
self.request_token
|
17
|
+
end
|
18
|
+
|
19
|
+
def activate
|
20
|
+
if self.request_token
|
21
|
+
self.access_token = self.request_token.get_access_token
|
22
|
+
self.store.find(user).update(:access_key => self.access_token.token, :access_secret => self.access_token.secret)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_consumer(key, secret)
|
27
|
+
self.consumer = OAuth::Consumer.new(key, secret, { :site => "http://boomloop.com" })
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_accesstoken
|
31
|
+
credentials = self.store.find(self.user)
|
32
|
+
self.consumer ||= create_consumer(credentials['consumer_key'], credentials['consumer_secret'])
|
33
|
+
self.access_token ||= OAuth::AccessToken.new(self.consumer, credentials['access_key'], credentials['access_secret'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Boomloop
|
2
|
+
module Authentication
|
3
|
+
class Credentials
|
4
|
+
attr_accessor :username, :store, :options
|
5
|
+
|
6
|
+
def initialize(username, store, options)
|
7
|
+
self.username = username
|
8
|
+
self.store = store
|
9
|
+
self.options = options || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def update(tosave)
|
13
|
+
tosave.each do |key, value|
|
14
|
+
self.options[key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
save
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
self.store.update(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
self.options[key]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Boomloop
|
2
|
+
module Authentication
|
3
|
+
module Store
|
4
|
+
class YAMLStore < Base
|
5
|
+
@@ymlfile = File.dirname(__FILE__) + '/../../../config/credentials.yml'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :config
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.class.load_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load_config
|
16
|
+
File.open(@@ymlfile) do |file|
|
17
|
+
self.config = YAML.load(file) || {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.save_config
|
22
|
+
if self.config
|
23
|
+
File.open(@@ymlfile, "w") do |f|
|
24
|
+
f.write self.config.to_yaml
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def update(credentials)
|
30
|
+
self.class.config[credentials.username] = credentials.options
|
31
|
+
self.class.save_config
|
32
|
+
end
|
33
|
+
|
34
|
+
def find(user)
|
35
|
+
::Boomloop::Authentication::Credentials.new(user, self, self.class.config[user])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/boomloop.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
3
|
+
gem 'oauth'
|
4
|
+
require 'oauth/consumer'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'activesupport'
|
7
|
+
|
8
|
+
module Boomloop
|
9
|
+
class ApiError < ::StandardError; end
|
10
|
+
end
|
11
|
+
|
12
|
+
Dir.glob(File.dirname(__FILE__) + "/**/*.rb").each do |file|
|
13
|
+
require file
|
14
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Boomloop
|
2
|
+
module Resources
|
3
|
+
class Base < ::OpenStruct
|
4
|
+
API_BASE = 'http://api.boomloop.com/'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :connection
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.connection
|
11
|
+
@@connection ||= init
|
12
|
+
end
|
13
|
+
|
14
|
+
# get oauth token
|
15
|
+
def self.init
|
16
|
+
client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
|
17
|
+
self.connection = client.create_accesstoken
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
returned = self.class.create(self)
|
22
|
+
self.instance_variable_set("@table", returned.to_hash)
|
23
|
+
returned
|
24
|
+
end
|
25
|
+
|
26
|
+
def child_resources
|
27
|
+
links = to_hash.keys.select { |key| key =~ /resource-url$/ }
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_hash
|
31
|
+
instance_variable_get("@table")
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_xml
|
35
|
+
self.to_hash.to_xml
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.singular
|
39
|
+
self.to_s.demodulize.tableize.singularize
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.plural
|
43
|
+
self.to_s.demodulize.pluralize.tableize
|
44
|
+
end
|
45
|
+
|
46
|
+
# REST methods
|
47
|
+
def self.index(params = {})
|
48
|
+
url = API_BASE + self.plural
|
49
|
+
url += "?#{ params.to_query }" unless params.empty?
|
50
|
+
xml = connection.get(url)
|
51
|
+
attrs = Hash.from_xml(xml)
|
52
|
+
construct_resource_collection(attrs)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.show(url)
|
56
|
+
xml = connection.get(url)
|
57
|
+
attrs = Hash.from_xml(xml)
|
58
|
+
self.new(attrs[self.singular])
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.create(resource)
|
62
|
+
url = API_BASE + self.plural
|
63
|
+
xml = connection.post(url, resource.to_hash.to_query)
|
64
|
+
self.new(Hash.from_xml(xml)[self.singular])
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.construct_resource_collection(attrs)
|
68
|
+
collection = attrs[self.plural][self.singular]
|
69
|
+
case collection
|
70
|
+
when Array : collection.map { |hash| self.new(hash) }
|
71
|
+
when Hash : self.new(collection)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.transitive_create(resource)
|
76
|
+
if resource.child_resources
|
77
|
+
child_resources.each { |child| transitive_create(child) }
|
78
|
+
else
|
79
|
+
create(resource)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
context "a boomloop resource" do
|
4
|
+
setup do
|
5
|
+
@connection = Boomloop::Resources::Base.connection
|
6
|
+
end
|
7
|
+
|
8
|
+
specify "should be creatable by passing in a hash of values" do
|
9
|
+
monster = Boomloop::Resources::Monster.new(:size => :huge, :affect => :sanguine, :name => "fred")
|
10
|
+
monster.affect.should.be :sanguine
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "should be able to return a hash representation of itself" do
|
14
|
+
qualities = { :size => :huge, :affect => :sanguine, :name => "fred" }
|
15
|
+
monster = Boomloop::Resources::Monster.new(qualities)
|
16
|
+
monster.to_hash.should.equal qualities
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "should be showable when calling Boomloop::Resources::Monster.show('http://api.boomloop.com/monsters/fred')" do
|
20
|
+
@connection.expects(:get).with("http://api.boomloop.com/monsters/fred").at_least_once.returns(%Q[
|
21
|
+
<monster>
|
22
|
+
<resource_url>http://api.boomloop.com/monsters/fred</resource_url>
|
23
|
+
<size>huge</size>
|
24
|
+
<affect>sanguine</affect>
|
25
|
+
<name>fred</name>
|
26
|
+
</monster>
|
27
|
+
])
|
28
|
+
|
29
|
+
monster = Boomloop::Resources::Monster.show('http://api.boomloop.com/monsters/fred')
|
30
|
+
monster.name.should.equal "fred"
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "should be listable when calling Boomloop::Resources::Monster.index" do
|
34
|
+
@connection.expects(:get).with("http://api.boomloop.com/monsters").at_least_once.returns(%Q[
|
35
|
+
<monsters>
|
36
|
+
<monster>
|
37
|
+
<resource_url>http://api.boomloop.com/monsters/fred</resource_url>
|
38
|
+
<size>huge</size>
|
39
|
+
<affect>sanguine</affect>
|
40
|
+
<name>fred</name>
|
41
|
+
</monster>
|
42
|
+
|
43
|
+
<monster>
|
44
|
+
<resource_url>http://api.boomloop.com/monsters/simon</resource_url>
|
45
|
+
<size>middling</size>
|
46
|
+
<affect>indifferent</affect>
|
47
|
+
<name>simon</name>
|
48
|
+
</monster>
|
49
|
+
</monsters>
|
50
|
+
])
|
51
|
+
|
52
|
+
monsters = Boomloop::Resources::Monster.index
|
53
|
+
monsters.map { |monster| monster.name }.should.include "fred"
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should be listable when calling Boomloop::Resources::Monster.index and there is only one result" do
|
57
|
+
@connection.expects(:get).with("http://api.boomloop.com/monsters").at_least_once.returns(%Q[
|
58
|
+
<monsters>
|
59
|
+
<monster>
|
60
|
+
<resource_url>http://api.boomloop.com/monsters/fred</resource_url>
|
61
|
+
<size>huge</size>
|
62
|
+
<affect>sanguine</affect>
|
63
|
+
<name>fred</name>
|
64
|
+
</monster>
|
65
|
+
</monsters>
|
66
|
+
])
|
67
|
+
|
68
|
+
monsters = Boomloop::Resources::Monster.index
|
69
|
+
monsters.name.should.equal "fred"
|
70
|
+
end
|
71
|
+
|
72
|
+
specify "should be listable with attributes when calling Boomloop::Resources::Monster.index(:affect => :sanguine)" do
|
73
|
+
@connection.expects(:get).with("http://api.boomloop.com/monsters?affect=sanguine").at_least_once.returns(%Q[
|
74
|
+
<monsters>
|
75
|
+
<monster>
|
76
|
+
<resource_url>http://api.boomloop.com/monsters/fred</resource_url>
|
77
|
+
<size>huge</size>
|
78
|
+
<affect>sanguine</affect>
|
79
|
+
<name>fred</name>
|
80
|
+
</monster>
|
81
|
+
</monsters>
|
82
|
+
])
|
83
|
+
|
84
|
+
monsters = Boomloop::Resources::Monster.index(:affect => :sanguine)
|
85
|
+
monsters.name.should.equal "fred"
|
86
|
+
end
|
87
|
+
|
88
|
+
specify "should be savable when calling .save on a Monster instance" do
|
89
|
+
intended_resource_url = 'http://api.boomloop.com/monsters/samuel'
|
90
|
+
intended_attributes = { :size => :laughable, :affect => :mordant, :name => "samuel" }
|
91
|
+
|
92
|
+
@connection.expects(:post).with("http://api.boomloop.com/monsters", intended_attributes.to_query).at_least_once.returns(%Q[
|
93
|
+
<monster>
|
94
|
+
<resource_url>#{ intended_resource_url }</resource_url>
|
95
|
+
<size>laughable</size>
|
96
|
+
<affect>mordant</affect>
|
97
|
+
<name>samuel</name>
|
98
|
+
</monster>
|
99
|
+
])
|
100
|
+
|
101
|
+
monster = Boomloop::Resources::Monster.new(intended_attributes)
|
102
|
+
monster.save
|
103
|
+
monster.resource_url.should.equal intended_resource_url
|
104
|
+
end
|
105
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: purzelrakete-boomloop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rany Keddo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-05-21 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: oauth
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.0.0
|
23
|
+
version:
|
24
|
+
description: boomloop is a kick-ass event platform.
|
25
|
+
email: rany@playtype.net
|
26
|
+
executables:
|
27
|
+
- boomloop
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- History.txt
|
32
|
+
- README.txt
|
33
|
+
files:
|
34
|
+
- lib/authentication/client.rb
|
35
|
+
- lib/authentication/credentials.rb
|
36
|
+
- lib/authentication/store/base.rb
|
37
|
+
- lib/authentication/store/yaml_store.rb
|
38
|
+
- lib/boomloop.rb
|
39
|
+
- lib/resources/base.rb
|
40
|
+
- lib/resources/event.rb
|
41
|
+
- lib/resources/event_series.rb
|
42
|
+
- lib/resources/place.rb
|
43
|
+
- lib/resources/ticket_category.rb
|
44
|
+
- lib/version.rb
|
45
|
+
- bin/boomloop
|
46
|
+
- History.txt
|
47
|
+
- README.txt
|
48
|
+
- test/boomloop_resource_test.rb
|
49
|
+
- test/mocks
|
50
|
+
- test/mocks/monster.rb
|
51
|
+
- test/test_helper.rb
|
52
|
+
- config/credentials.yml
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/purzelrakete/boomloop
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --main
|
58
|
+
- README.txt
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.0.1
|
77
|
+
signing_key:
|
78
|
+
specification_version: 2
|
79
|
+
summary: Ruby API for boomloop.com
|
80
|
+
test_files: []
|
81
|
+
|