spire_io 1.0.0.alpha.5 → 1.0.0.beta.1
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/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
|