spire_io 1.0.0.alpha.5 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/spire/api/account.rb +60 -0
- data/lib/spire/api/channel.rb +34 -0
- data/lib/spire/api/requestable.rb +103 -0
- data/lib/spire/api/resource.rb +109 -0
- data/lib/spire/api/session.rb +166 -0
- data/lib/spire/api/subscription.rb +79 -0
- data/lib/spire/api.rb +165 -0
- data/lib/spire_io.rb +19 -89
- metadata +30 -5
@@ -0,0 +1,60 @@
|
|
1
|
+
class Spire
|
2
|
+
class API
|
3
|
+
|
4
|
+
class Account < Resource
|
5
|
+
def resource_name
|
6
|
+
"account"
|
7
|
+
end
|
8
|
+
|
9
|
+
define_request(:billing_subscription) do |info|
|
10
|
+
billing = properties["billing"]
|
11
|
+
{
|
12
|
+
:method => :put,
|
13
|
+
:url => billing["url"],
|
14
|
+
:body => info.to_json,
|
15
|
+
:headers => {
|
16
|
+
"Accept" => media_type, "Content-Type" => media_type,
|
17
|
+
"Authorization" => "Capability #{billing["capability"]}"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
define_request(:billing_invoices) do
|
23
|
+
invoices = properties["billing"]["invoices"]
|
24
|
+
{
|
25
|
+
:method => :get,
|
26
|
+
:url => invoices["url"],
|
27
|
+
:headers => {
|
28
|
+
"Accept" => "application/json",
|
29
|
+
"Authorization" => "Capability #{invoices["capability"]}"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
define_request(:billing_invoices_upcoming) do
|
35
|
+
upcoming = properties["billing"]["invoices"]["upcoming"]
|
36
|
+
{
|
37
|
+
:method => :get,
|
38
|
+
:url => upcoming["url"],
|
39
|
+
:headers => {
|
40
|
+
"Accept" => "application/json",
|
41
|
+
"Authorization" => "Capability #{upcoming["capability"]}"
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Updates and subscribe the account to a billing plan
|
47
|
+
# @param [Object] info data containing billing description
|
48
|
+
# @return [Account]
|
49
|
+
def billing_subscription(info)
|
50
|
+
response = request(:billing_subscription)
|
51
|
+
raise "Error attempting to update account billing: (#{response.status}) #{response.body}" if response.status != 200
|
52
|
+
properties = response.data
|
53
|
+
#@resources["account"] = JSON.parse(response.body)
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Spire
|
2
|
+
class API
|
3
|
+
|
4
|
+
class Channel < Resource
|
5
|
+
def resource_name
|
6
|
+
"channel"
|
7
|
+
end
|
8
|
+
|
9
|
+
define_request(:publish) do |string|
|
10
|
+
{
|
11
|
+
:method => :post,
|
12
|
+
:url => @url,
|
13
|
+
:body => string,
|
14
|
+
:headers => {
|
15
|
+
"Authorization" => "Capability #{@capability}",
|
16
|
+
"Accept" => @spire.mediaType("message"),
|
17
|
+
"Content-Type" => @spire.mediaType("message")
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def publish(content)
|
23
|
+
body = {:content => content}.to_json
|
24
|
+
response = request(:publish, body)
|
25
|
+
unless response.status == 201
|
26
|
+
raise "Error publishing to #{self.class.name}: (#{response.status}) #{response.body}"
|
27
|
+
end
|
28
|
+
message = response.data
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
class Spire
|
4
|
+
class API
|
5
|
+
|
6
|
+
module Requestable
|
7
|
+
|
8
|
+
def self.included(mod)
|
9
|
+
mod.module_eval do
|
10
|
+
extend(ClassMethods)
|
11
|
+
include(InstanceMethods)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def requests
|
17
|
+
@requests ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_request(name)
|
21
|
+
if req = requests[name]
|
22
|
+
req
|
23
|
+
elsif superclass.respond_to?(:get_request)
|
24
|
+
superclass.get_request(name)
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def define_request(name, &block)
|
31
|
+
requests[name] = block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module InstanceMethods
|
36
|
+
def prepare_request(name, *args)
|
37
|
+
if block = self.class.get_request(name)
|
38
|
+
options = self.instance_exec(*args, &block)
|
39
|
+
Request.new(@client, options)
|
40
|
+
else
|
41
|
+
raise ArgumentError, "No request has been defined for #{name.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def request(name, *args)
|
46
|
+
prepare_request(name, *args).exec
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Request
|
51
|
+
attr_accessor :url
|
52
|
+
def initialize(client, options)
|
53
|
+
@client = client
|
54
|
+
@method = options.delete(:method)
|
55
|
+
@url = options.delete(:url)
|
56
|
+
@options = options
|
57
|
+
@options[:headers] = {
|
58
|
+
"User-Agent" => "Ruby spire.io client"
|
59
|
+
}.merge(@options[:headers] || {})
|
60
|
+
end
|
61
|
+
|
62
|
+
def headers
|
63
|
+
@options[:headers]
|
64
|
+
end
|
65
|
+
|
66
|
+
def body
|
67
|
+
@options[:body]
|
68
|
+
end
|
69
|
+
|
70
|
+
def body=(val)
|
71
|
+
@options[:body] = val
|
72
|
+
end
|
73
|
+
|
74
|
+
def query
|
75
|
+
@options[:query]
|
76
|
+
end
|
77
|
+
|
78
|
+
def exec
|
79
|
+
response = Response.new(@client.send(@method, @url, @options))
|
80
|
+
headers = response.headers
|
81
|
+
content_type = headers.values_at("Content-Type", "content-type").compact.first
|
82
|
+
if content_type =~ /json/ && [200, 201].include?(response.status)
|
83
|
+
# FIXME: detect appropriate response headers, rather than rescuing
|
84
|
+
begin
|
85
|
+
response.data = API.deserialize(response.body)
|
86
|
+
rescue
|
87
|
+
end
|
88
|
+
end
|
89
|
+
response
|
90
|
+
end
|
91
|
+
|
92
|
+
class Response < SimpleDelegator
|
93
|
+
attr_accessor :data
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class Spire
|
2
|
+
class API
|
3
|
+
class Resource
|
4
|
+
include Requestable
|
5
|
+
|
6
|
+
define_request(:get) do
|
7
|
+
{
|
8
|
+
:method => :get,
|
9
|
+
:url => @url,
|
10
|
+
:headers => {
|
11
|
+
"Authorization" => "Capability #{@capability}",
|
12
|
+
"Accept" => media_type
|
13
|
+
}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
define_request(:update) do |properties|
|
18
|
+
{
|
19
|
+
:method => :put,
|
20
|
+
:url => @url,
|
21
|
+
:body => properties.to_json,
|
22
|
+
:headers => {
|
23
|
+
"Authorization" => "Capability #{@capability}",
|
24
|
+
"Accept" => media_type,
|
25
|
+
"Content-Type" => media_type
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
define_request(:delete) do
|
31
|
+
{
|
32
|
+
:method => :delete,
|
33
|
+
:url => @url,
|
34
|
+
:headers => {
|
35
|
+
"Authorization" => "Capability #{@capability}",
|
36
|
+
"Accept" => media_type,
|
37
|
+
"Content-Type" => media_type,
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :url, :capability, :properties
|
43
|
+
|
44
|
+
def initialize(spire, data)
|
45
|
+
@spire = spire
|
46
|
+
@client = spire.client
|
47
|
+
@url = data["url"]
|
48
|
+
@capability = data["capability"]
|
49
|
+
@properties = data
|
50
|
+
end
|
51
|
+
|
52
|
+
def key
|
53
|
+
properties["key"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(name, *args)
|
57
|
+
if description = schema["properties"][name.to_s]
|
58
|
+
properties[name.to_s]
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def [](name)
|
65
|
+
properties[name]
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
def get
|
73
|
+
response = request(:get)
|
74
|
+
unless response.status == 200
|
75
|
+
raise "Error retrieving #{self.class.name}: (#{response.status}) #{response.body}"
|
76
|
+
end
|
77
|
+
@properties = response.data
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def update(properties)
|
82
|
+
response = request(:update, properties)
|
83
|
+
unless response.status == 200
|
84
|
+
raise "Error updating #{self.class.name}: (#{response.status}) #{response.body}"
|
85
|
+
end
|
86
|
+
@properties = response.data
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def delete
|
91
|
+
response = request(:delete)
|
92
|
+
unless response.status == 204
|
93
|
+
raise "Error deleting #{self.class.name}: (#{response.status}) #{response.body}"
|
94
|
+
end
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
def schema
|
99
|
+
@spire.schema[resource_name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def media_type
|
103
|
+
schema["mediaType"]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
@@ -0,0 +1,166 @@
|
|
1
|
+
class Spire
|
2
|
+
class API
|
3
|
+
|
4
|
+
class Session
|
5
|
+
include Requestable
|
6
|
+
|
7
|
+
define_request(:account) do
|
8
|
+
resource = @resources["account"]
|
9
|
+
{
|
10
|
+
:method => :get,
|
11
|
+
:url => resource["url"],
|
12
|
+
:headers => {
|
13
|
+
"Authorization" => "Capability #{resource["capability"]}",
|
14
|
+
"Accept" => @spire.mediaType("account")
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
define_request(:channels) do
|
20
|
+
collection = @resources["channels"]
|
21
|
+
request = {
|
22
|
+
:method => :get,
|
23
|
+
:url => collection["url"],
|
24
|
+
:headers => {
|
25
|
+
"Authorization" => "Capability #{collection["capability"]}",
|
26
|
+
"Accept" => @spire.mediaType("channels"),
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
define_request(:channel_by_name) do |name|
|
32
|
+
collection = @resources["channels"]
|
33
|
+
request = {
|
34
|
+
:method => :get,
|
35
|
+
:url => collection["url"],
|
36
|
+
:query => {:name => name},
|
37
|
+
:headers => {
|
38
|
+
"Authorization" => "Capability #{collection["capability"]}",
|
39
|
+
"Accept" => @spire.mediaType("channels"),
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
define_request(:create_channel) do |name|
|
45
|
+
collection = @resources["channels"]
|
46
|
+
{
|
47
|
+
:method => :post,
|
48
|
+
:url => collection["url"],
|
49
|
+
:body => { :name => name }.to_json,
|
50
|
+
:headers => {
|
51
|
+
"Authorization" => "Capability #{collection["capability"]}",
|
52
|
+
"Accept" => @spire.mediaType("channel"),
|
53
|
+
"Content-Type" => @spire.mediaType("channel")
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
define_request(:create_subscription) do |subscription_name, channel_urls|
|
59
|
+
collection = @resources["subscriptions"]
|
60
|
+
{
|
61
|
+
:method => :post,
|
62
|
+
:url => collection["url"],
|
63
|
+
:body => {
|
64
|
+
:channels => channel_urls,
|
65
|
+
:name => subscription_name
|
66
|
+
}.to_json,
|
67
|
+
:headers => {
|
68
|
+
"Authorization" => "Capability #{collection["capability"]}",
|
69
|
+
"Accept" => @spire.mediaType("subscription"),
|
70
|
+
"Content-Type" => @spire.mediaType("subscription")
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :url, :resources, :schema, :capability
|
76
|
+
|
77
|
+
def initialize(spire, data)
|
78
|
+
@spire = spire
|
79
|
+
@client = spire.client
|
80
|
+
@schema = spire.schema["session"]
|
81
|
+
@url = data["url"]
|
82
|
+
@capability = data["capability"]
|
83
|
+
@resources = data["resources"]
|
84
|
+
end
|
85
|
+
|
86
|
+
def account
|
87
|
+
@account ||= account!
|
88
|
+
end
|
89
|
+
|
90
|
+
def account!
|
91
|
+
@account = API::Account.new(@spire, @resources["account"]).get
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_channel(name)
|
95
|
+
response = request(:create_channel, name)
|
96
|
+
unless response.status == 201
|
97
|
+
raise "Error creating Channel: (#{response.status}) #{response.body}"
|
98
|
+
end
|
99
|
+
properties = response.data
|
100
|
+
channels[name] = API::Channel.new(@spire, properties)
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_subscription(subscription_name, channel_names)
|
104
|
+
channel_urls = channel_names.flatten.map { |name| self.channels[name].url rescue nil }.compact
|
105
|
+
response = request(:create_subscription, subscription_name, channel_urls)
|
106
|
+
unless response.status == 201
|
107
|
+
raise "Error creating Subscription: (#{response.status}) #{response.body}"
|
108
|
+
end
|
109
|
+
data = response.data
|
110
|
+
subscriptions[data["name"]] = API::Subscription.new(@spire, data)
|
111
|
+
end
|
112
|
+
|
113
|
+
def channels!
|
114
|
+
response = request(:channels)
|
115
|
+
unless response.status == 200
|
116
|
+
raise "Error retrieving Channels: (#{response.status}) #{response.body}"
|
117
|
+
end
|
118
|
+
channels_data = response.data
|
119
|
+
@channels = {}
|
120
|
+
channels_data.each do |name, properties|
|
121
|
+
@channels[name] = API::Channel.new(@spire, properties)
|
122
|
+
end
|
123
|
+
@channels
|
124
|
+
end
|
125
|
+
|
126
|
+
def channels
|
127
|
+
@channels ||= channels!
|
128
|
+
end
|
129
|
+
|
130
|
+
# Not yet in Spire API
|
131
|
+
#define_request(:subscriptions) do
|
132
|
+
#{
|
133
|
+
#:method => :get,
|
134
|
+
#:url => @resources["subscriptions"]["url"],
|
135
|
+
#:headers => {
|
136
|
+
#"Authorization" => "Capability #{@resources["subscriptions"]["capability"]}",
|
137
|
+
#"Accept" => @spire.mediaType("subscription"),
|
138
|
+
#}
|
139
|
+
#}
|
140
|
+
#end
|
141
|
+
|
142
|
+
def subscriptions
|
143
|
+
if @subscriptions
|
144
|
+
@subscriptions
|
145
|
+
else
|
146
|
+
# TODO Fix this to use an API call once Spire supports it
|
147
|
+
@subscriptions = {}
|
148
|
+
channels.each do |name, channel|
|
149
|
+
if subs = channel.properties["subscriptions"]
|
150
|
+
subs.each do |key, sub|
|
151
|
+
@subscriptions[key] = API::Subscription.new(@spire, sub)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
@subscriptions
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def subscriptions!
|
160
|
+
raise "unimplemented"
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class Spire
|
2
|
+
class API
|
3
|
+
|
4
|
+
class Subscription < Resource
|
5
|
+
|
6
|
+
attr_reader :last
|
7
|
+
def resource_name
|
8
|
+
"subscription"
|
9
|
+
end
|
10
|
+
|
11
|
+
define_request(:messages) do |options|
|
12
|
+
{
|
13
|
+
:method => :get,
|
14
|
+
:url => @url,
|
15
|
+
:query => {
|
16
|
+
"timeout" => options[:timeout],
|
17
|
+
"last-message" => options[:last],
|
18
|
+
"order-by" => options[:order_by],
|
19
|
+
"delay" => options[:delay]
|
20
|
+
},
|
21
|
+
:headers => {
|
22
|
+
"Authorization" => "Capability #{@capability}",
|
23
|
+
"Accept" => @spire.mediaType("events")
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def listeners
|
29
|
+
@listeners ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_listener(&block)
|
33
|
+
listeners << block
|
34
|
+
block
|
35
|
+
end
|
36
|
+
|
37
|
+
def retrieve_messages(options={})
|
38
|
+
options[:last] ||= "0"
|
39
|
+
options[:delay] ||= 0
|
40
|
+
options[:order_by] ||= "desc"
|
41
|
+
|
42
|
+
response = request(:messages, options)
|
43
|
+
unless response.status == 200
|
44
|
+
raise "Error retrieving messages from #{self.class.name}: (#{response.status}) #{response.body}"
|
45
|
+
end
|
46
|
+
messages = response.data["messages"]
|
47
|
+
@last = messages.last["timestamp"] unless messages.empty?
|
48
|
+
messages.each do |message|
|
49
|
+
listeners.each do |listener|
|
50
|
+
listener.call(message)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
messages
|
54
|
+
end
|
55
|
+
|
56
|
+
def poll(options={})
|
57
|
+
# timeout option of 0 means no long poll,
|
58
|
+
# so we force it here.
|
59
|
+
options[:timeout] = 0
|
60
|
+
options[:last] = @last
|
61
|
+
retrieve_messages(options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def long_poll(options={})
|
65
|
+
options[:timeout] ||= 30
|
66
|
+
options[:last] = @last
|
67
|
+
retrieve_messages(options)
|
68
|
+
end
|
69
|
+
|
70
|
+
def listen(options={})
|
71
|
+
loop do
|
72
|
+
break unless yield(long_poll(options))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/spire/api.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
gem "excon"
|
3
|
+
require "excon"
|
4
|
+
gem "json"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
require "spire/api/requestable"
|
8
|
+
require "spire/api/resource"
|
9
|
+
require "spire/api/session"
|
10
|
+
require "spire/api/account"
|
11
|
+
require "spire/api/channel"
|
12
|
+
require "spire/api/subscription"
|
13
|
+
|
14
|
+
class Spire
|
15
|
+
|
16
|
+
class API
|
17
|
+
include Requestable
|
18
|
+
|
19
|
+
def self.deserialize(string)
|
20
|
+
JSON.parse(string, :symbolize_names => false)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :client, :description, :schema
|
24
|
+
def initialize(url="https://api.spire.io", options={})
|
25
|
+
@version = options[:version] || "1.0"
|
26
|
+
@client = Excon
|
27
|
+
@url = url
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"#<Spire::API:0x#{object_id.to_s(16)} @url=#{@url.dump}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
define_request(:discover) do
|
35
|
+
{
|
36
|
+
:method => :get,
|
37
|
+
:url => @url,
|
38
|
+
:headers => {"Accept" => "application/json"}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
define_request(:create_session) do |key|
|
43
|
+
{
|
44
|
+
:method => :post,
|
45
|
+
:url => @description["resources"]["sessions"]["url"],
|
46
|
+
:body => {:key => key}.to_json,
|
47
|
+
:headers => {
|
48
|
+
"Accept" => mediaType("session"),
|
49
|
+
"Content-Type" => mediaType("account")
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
define_request(:login) do |email, password|
|
55
|
+
{
|
56
|
+
:method => :post,
|
57
|
+
:url => @description["resources"]["sessions"]["url"],
|
58
|
+
:body => { :email => email, :password => password }.to_json,
|
59
|
+
:headers => {
|
60
|
+
"Accept" => mediaType("session"),
|
61
|
+
"Content-Type" => mediaType("account")
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
define_request(:create_account) do |info|
|
67
|
+
{
|
68
|
+
:method => :post,
|
69
|
+
:url => @description["resources"]["accounts"]["url"],
|
70
|
+
:body => {
|
71
|
+
:email => info[:email],
|
72
|
+
:password => info[:password],
|
73
|
+
:password_confirmation => info[:password_confirmation]
|
74
|
+
}.to_json,
|
75
|
+
:headers => {
|
76
|
+
"Accept" => mediaType("session"),
|
77
|
+
"Content-Type" => mediaType("account")
|
78
|
+
}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
define_request(:password_reset) do |email|
|
83
|
+
{
|
84
|
+
:method => :post,
|
85
|
+
:url => @description["resources"]["accounts"]["url"],
|
86
|
+
:query => { :email => email },
|
87
|
+
:body => ""
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
define_request(:billing) do
|
92
|
+
{
|
93
|
+
:method => :get,
|
94
|
+
:url => @description["resources"]["billing"]["url"],
|
95
|
+
:headers => {
|
96
|
+
"Accept" => "application/json"
|
97
|
+
}
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def discover
|
103
|
+
response = request(:discover)
|
104
|
+
raise "Error during discovery: #{response.status}" if response.status != 200
|
105
|
+
@description = response.data
|
106
|
+
@schema = @description["schema"][@version]
|
107
|
+
end
|
108
|
+
|
109
|
+
def mediaType(name)
|
110
|
+
schema[name]["mediaType"]
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_session(key)
|
114
|
+
response = request(:create_session, key)
|
115
|
+
raise "Error starting a key-based session" if response.status != 201
|
116
|
+
session_data = response.data
|
117
|
+
API::Session.new(self, session_data)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Authenticates a session using a login and password
|
121
|
+
def login(login, password)
|
122
|
+
response = request(:login, login, password)
|
123
|
+
raise "Error attemping to login: (#{response.status}) #{response.body}" if response.status != 201
|
124
|
+
session_data = response.data
|
125
|
+
API::Session.new(self, session_data)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Register for a new spire account, and authenticates as the newly created account
|
129
|
+
# @param [String] :email Email address of new account
|
130
|
+
# @param [String] :password Password of new account
|
131
|
+
def create_account(info)
|
132
|
+
response = request(:create_account, info)
|
133
|
+
if response.status != 201
|
134
|
+
raise "Error attempting to register: (#{response.status}) #{response.body}"
|
135
|
+
end
|
136
|
+
session_data = response.data
|
137
|
+
API::Session.new(self, session_data)
|
138
|
+
end
|
139
|
+
|
140
|
+
def password_reset_request(email)
|
141
|
+
response = request(:password_reset)
|
142
|
+
unless response.status == 202
|
143
|
+
raise "Error requesting password reset: (#{response.status}) #{response.body}"
|
144
|
+
end
|
145
|
+
response
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns a billing object than contains a list of all the plans available
|
149
|
+
# @param [String] info optional object description
|
150
|
+
# @return [Billing]
|
151
|
+
def billing(info=nil)
|
152
|
+
response = request(:billing)
|
153
|
+
raise "Error getting billing plans: #{response.status}" if response.status != 200
|
154
|
+
API::Billing.new(self, response.data)
|
155
|
+
end
|
156
|
+
|
157
|
+
class Billing < Resource
|
158
|
+
def resource_name
|
159
|
+
"billing"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
data/lib/spire_io.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
require "delegate"
|
2
|
-
gem "excon"
|
3
|
-
require "excon"
|
4
|
-
gem "json"
|
5
|
-
require "json"
|
6
2
|
|
7
3
|
require "spire/api"
|
8
4
|
|
9
5
|
class Spire
|
10
6
|
|
11
|
-
|
7
|
+
# How many times we will try to fetch a channel or subscription after
|
8
|
+
# getting a 409 Conflict.
|
12
9
|
RETRY_CREATION_LIMIT = 3
|
13
10
|
|
14
11
|
attr_accessor :api, :session, :resources
|
@@ -16,19 +13,13 @@ class Spire
|
|
16
13
|
def initialize(url="https://api.spire.io")
|
17
14
|
@api = Spire::API.new(url)
|
18
15
|
@url = url
|
19
|
-
@channels = {}
|
20
|
-
@subscriptions = {}
|
21
16
|
@channel_error_counts = {}
|
22
17
|
@subscription_error_counts = {}
|
23
18
|
discover
|
24
19
|
end
|
25
20
|
|
26
21
|
def key
|
27
|
-
@resources["account"]["key"]
|
28
|
-
end
|
29
|
-
|
30
|
-
def mediaType(name)
|
31
|
-
@description["schema"]["1.0"][name]["mediaType"]
|
22
|
+
@session.resources["account"]["key"]
|
32
23
|
end
|
33
24
|
|
34
25
|
def discover
|
@@ -56,16 +47,8 @@ class Spire
|
|
56
47
|
self
|
57
48
|
end
|
58
49
|
|
59
|
-
def key
|
60
|
-
@session.resources["account"]["key"]
|
61
|
-
end
|
62
|
-
|
63
50
|
def password_reset_request(email)
|
64
|
-
|
65
|
-
unless response.status == 202
|
66
|
-
raise "Error requesting password reset: (#{response.status}) #{response.body}"
|
67
|
-
end
|
68
|
-
response
|
51
|
+
@api.password_reset_request(email)
|
69
52
|
end
|
70
53
|
|
71
54
|
|
@@ -78,47 +61,8 @@ class Spire
|
|
78
61
|
# See Spire docs for available settings
|
79
62
|
def update(info)
|
80
63
|
@session.account.update(info)
|
81
|
-
#response = request(:update_account, info)
|
82
|
-
#raise "Error attempting to update account: (#{response.status}) #{response.body}" if response.status != 200
|
83
|
-
#@resources["account"] = JSON.parse(response.body)
|
84
|
-
self
|
85
|
-
end
|
86
|
-
|
87
|
-
def retrieve_session
|
88
|
-
response = request(:session)
|
89
|
-
cache_session(JSON.parse(response.body))
|
90
|
-
raise "Error reloading session: #{response.status}" if response.status != 200
|
91
64
|
self
|
92
|
-
end
|
93
|
-
|
94
|
-
def cache_session(data)
|
95
|
-
@session = data
|
96
|
-
@resources = @session["resources"]
|
97
|
-
retrieve_channels
|
98
|
-
end
|
99
|
-
|
100
|
-
def retrieve_channels
|
101
|
-
response = request(:channels)
|
102
|
-
unless response.status == 200
|
103
|
-
raise "Error retrieving channels: (#{response.status}) #{response.body}"
|
104
|
-
end
|
105
|
-
cache_channels(JSON.parse(response.body))
|
106
65
|
end
|
107
|
-
|
108
|
-
def cache_channels(data)
|
109
|
-
@channels = {}
|
110
|
-
data.each do |name, properties|
|
111
|
-
@channels[name] = Channel.new(self, properties)
|
112
|
-
cache_channel_subscriptions(properties["subscriptions"])
|
113
|
-
end
|
114
|
-
@channels
|
115
|
-
end
|
116
|
-
|
117
|
-
def cache_channel_subscriptions(data)
|
118
|
-
data.each do |name, properties|
|
119
|
-
@subscriptions[name] = Subscription.new(self, properties)
|
120
|
-
end
|
121
|
-
end
|
122
66
|
|
123
67
|
# Returns a channel object for the named channel
|
124
68
|
# @param [String] name Name of channel returned
|
@@ -131,6 +75,10 @@ class Spire
|
|
131
75
|
@session.channels
|
132
76
|
end
|
133
77
|
|
78
|
+
def channels!
|
79
|
+
@session.channels!
|
80
|
+
end
|
81
|
+
|
134
82
|
# Creates a channel on spire. Returns a Channel object. Note that this will
|
135
83
|
# fail with a 409 if a channel with the same name exists.
|
136
84
|
def find_or_create_channel(name)
|
@@ -207,10 +155,7 @@ class Spire
|
|
207
155
|
# @return [Account]
|
208
156
|
def billing_subscription(info)
|
209
157
|
@session.account.billing_subscription(info)
|
210
|
-
|
211
|
-
#raise "Error attempting to update account billing: (#{response.status}) #{response.body}" if response.status != 200
|
212
|
-
#@resources["account"] = JSON.parse(response.body)
|
213
|
-
#self
|
158
|
+
self
|
214
159
|
end
|
215
160
|
|
216
161
|
|
@@ -278,11 +223,17 @@ class Spire
|
|
278
223
|
__getobj__.add_listener(&listener)
|
279
224
|
end
|
280
225
|
|
281
|
-
def remove_listener(
|
282
|
-
if
|
283
|
-
listener = listeners
|
226
|
+
def remove_listener(arg)
|
227
|
+
if arg.is_a? String
|
228
|
+
listener = listeners.delete(arg)
|
229
|
+
else
|
230
|
+
name, _listener = listeners.detect {|k,v| v == arg }
|
231
|
+
listener = listeners.delete(name)
|
232
|
+
end
|
233
|
+
|
234
|
+
if listener
|
235
|
+
__getobj__.listeners.delete(listener)
|
284
236
|
end
|
285
|
-
__getobj__.listeners.delete(listener)
|
286
237
|
end
|
287
238
|
|
288
239
|
def wrap_listener(&block)
|
@@ -321,25 +272,4 @@ class Spire
|
|
321
272
|
|
322
273
|
end
|
323
274
|
|
324
|
-
|
325
|
-
## Object representing a Spire billing
|
326
|
-
##
|
327
|
-
## You can get all the billing plans by calling the method billing in Spire object
|
328
|
-
## * spire = Spire.new
|
329
|
-
## * billing = spire.billing()
|
330
|
-
## * plans = billing.plans
|
331
|
-
#class Billing
|
332
|
-
#def initialize(spire,properties)
|
333
|
-
#@spire = spire
|
334
|
-
#@properties = properties
|
335
|
-
#end
|
336
|
-
|
337
|
-
#def url
|
338
|
-
#@properties["url"]
|
339
|
-
#end
|
340
|
-
|
341
|
-
#def plans
|
342
|
-
#@properties["plans"]
|
343
|
-
#end
|
344
|
-
#end
|
345
275
|
end
|
metadata
CHANGED
@@ -1,24 +1,25 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spire_io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196353
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
-
|
11
|
-
-
|
12
|
-
version: 1.0.0.
|
10
|
+
- beta
|
11
|
+
- 1
|
12
|
+
version: 1.0.0.beta.1
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Dan Yoder
|
16
16
|
- Daniel Lockhart dlockhart@spire.io
|
17
|
+
- Matthew King mking@spire.io
|
17
18
|
autorequire:
|
18
19
|
bindir: bin
|
19
20
|
cert_chain: []
|
20
21
|
|
21
|
-
date: 2012-01
|
22
|
+
date: 2012-02-01 00:00:00 -08:00
|
22
23
|
default_executable:
|
23
24
|
dependencies:
|
24
25
|
- !ruby/object:Gem::Dependency
|
@@ -81,11 +82,28 @@ dependencies:
|
|
81
82
|
version: "0.7"
|
82
83
|
type: :development
|
83
84
|
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: redcarpet
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 11
|
94
|
+
segments:
|
95
|
+
- 2
|
96
|
+
- 1
|
97
|
+
- 0
|
98
|
+
version: 2.1.0
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
84
101
|
description: "\t\tThe spire_io gem allows you to quickly and easily use the spire.io service\n\
|
85
102
|
\t\tusing Ruby. See http://www.spire.io/ for more.\n"
|
86
103
|
email:
|
87
104
|
- dan@spire.io
|
88
105
|
-
|
106
|
+
-
|
89
107
|
executables: []
|
90
108
|
|
91
109
|
extensions: []
|
@@ -93,6 +111,13 @@ extensions: []
|
|
93
111
|
extra_rdoc_files: []
|
94
112
|
|
95
113
|
files:
|
114
|
+
- lib/spire/api/account.rb
|
115
|
+
- lib/spire/api/channel.rb
|
116
|
+
- lib/spire/api/requestable.rb
|
117
|
+
- lib/spire/api/resource.rb
|
118
|
+
- lib/spire/api/session.rb
|
119
|
+
- lib/spire/api/subscription.rb
|
120
|
+
- lib/spire/api.rb
|
96
121
|
- lib/spire_io.rb
|
97
122
|
has_rdoc: true
|
98
123
|
homepage: https://github.com/spire-io/spire.io.rb
|