pachube-stream 0.0.3
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/.gitignore +4 -0
- data/Gemfile +15 -0
- data/README.rdoc +109 -0
- data/Rakefile +2 -0
- data/autotest/discover.rb +1 -0
- data/examples/subscribe.rb +32 -0
- data/lib/pachube-stream/client.rb +80 -0
- data/lib/pachube-stream/connection.rb +117 -0
- data/lib/pachube-stream/html_request.rb +15 -0
- data/lib/pachube-stream/methods.rb +29 -0
- data/lib/pachube-stream/request.rb +51 -0
- data/lib/pachube-stream/version.rb +3 -0
- data/lib/pachube-stream.rb +15 -0
- data/pachube-stream.gemspec +27 -0
- data/spec/fixtures/pachube/not_authorized.json +6 -0
- data/spec/fixtures/pachube/subscribe.json +5 -0
- data/spec/fixtures/pachube/subscribe_data_stream.json +64 -0
- data/spec/fixtures/pachube/subscribe_request.json +9 -0
- data/spec/pachube_spec.rb +106 -0
- data/spec/spec_helper.rb +51 -0
- metadata +165 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
== PachubeStream
|
2
|
+
|
3
|
+
PachubeStream gives you an API for the Pachube TCP Stream using EventMachine
|
4
|
+
|
5
|
+
http://api.pachube.com/v2/beta/#tcp-socket-and-websocket-connections
|
6
|
+
|
7
|
+
== Quickstart
|
8
|
+
|
9
|
+
require "pachube-stream"
|
10
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
11
|
+
|
12
|
+
Look at the examples directory for exactly that!
|
13
|
+
|
14
|
+
== Connection Methods
|
15
|
+
|
16
|
+
The methods on the connection are what Pachube look for when making a request; this in-turn will
|
17
|
+
generate the correct method value in the 'http json'
|
18
|
+
|
19
|
+
=== Example
|
20
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
21
|
+
request = connection.subscribe("/feeds/504")
|
22
|
+
|
23
|
+
Results In the following JSON request
|
24
|
+
|
25
|
+
{
|
26
|
+
"method" : "subscribe",
|
27
|
+
"resource" : "/feeds/504",
|
28
|
+
"headers" :
|
29
|
+
{
|
30
|
+
"X-PachubeApiKey" : "API_KEY"
|
31
|
+
},
|
32
|
+
"token" : "subscribe"
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
=== Subscribe
|
37
|
+
|
38
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
39
|
+
request = connection.subscribe("/feeds/504")
|
40
|
+
request.on_datastream do |response|
|
41
|
+
puts response
|
42
|
+
end
|
43
|
+
|
44
|
+
=== Unsubscribe
|
45
|
+
|
46
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
47
|
+
request = connection.unsubscribe("/feeds/504")
|
48
|
+
request.on_compelete do |response|
|
49
|
+
puts response
|
50
|
+
end
|
51
|
+
|
52
|
+
=== Get
|
53
|
+
|
54
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
55
|
+
request = connection.get("/feeds/504")
|
56
|
+
request.on_get do |response|
|
57
|
+
puts response
|
58
|
+
end
|
59
|
+
|
60
|
+
=== Put
|
61
|
+
|
62
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
63
|
+
request = connection.put("/feeds/504")
|
64
|
+
request.on_complete do |response|
|
65
|
+
puts response
|
66
|
+
end
|
67
|
+
|
68
|
+
=== Delete
|
69
|
+
|
70
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
71
|
+
request = connection.delete("/feeds/504")
|
72
|
+
request.on_complete do |response|
|
73
|
+
puts response
|
74
|
+
end
|
75
|
+
|
76
|
+
=== Post
|
77
|
+
|
78
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
79
|
+
request = connection.post("/feeds/504")
|
80
|
+
request.on_complete do |response|
|
81
|
+
puts response
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
The way we make a request is to send json in the format of a HTTP request; you only have
|
86
|
+
to give extra options when you want to provide your own headers, params and body etc;
|
87
|
+
|
88
|
+
Therefore when using the connection Methods
|
89
|
+
|
90
|
+
When can create this 'http json request' with PachubeStream::HttpRequest passing in a hash
|
91
|
+
|
92
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
93
|
+
request = connection.put("/feeds/504", PachubeStream::HttpRequest.new(:body => {}, :params => {}, :headers => {}))
|
94
|
+
|
95
|
+
request.on_complete do |response|
|
96
|
+
puts response
|
97
|
+
end
|
98
|
+
|
99
|
+
== Defaults:
|
100
|
+
|
101
|
+
When creating a connection you can specify the host and port these have defaults
|
102
|
+
|
103
|
+
host # => beta.pachube.com
|
104
|
+
port # => 8081
|
105
|
+
|
106
|
+
|
107
|
+
== Respect
|
108
|
+
|
109
|
+
Respect goes out to the twitter-stream Gem as one took some concepts form that; nice!
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { ["rspec2"] }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
$: << 'lib' << '../lib'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'eventmachine'
|
4
|
+
require '../lib/pachube-stream'
|
5
|
+
|
6
|
+
EM.run do
|
7
|
+
connection = PachubeStream::Connection.connect(:api_key => ENV["PACHUBE_API_KEY"])
|
8
|
+
|
9
|
+
connection.on_reconnect do |timeout, reconnect_retries|
|
10
|
+
puts timeout
|
11
|
+
puts reconnect_retries
|
12
|
+
end
|
13
|
+
|
14
|
+
connection.on_max_reconnects do |timeout, reconnect_retries|
|
15
|
+
puts timeout
|
16
|
+
puts reconnect_retries
|
17
|
+
end
|
18
|
+
|
19
|
+
feed = connection.subscribe("/feeds/6643") # random Feed
|
20
|
+
|
21
|
+
feed.on_datastream do |response|
|
22
|
+
puts response
|
23
|
+
end
|
24
|
+
|
25
|
+
feed.on_complete do |response|
|
26
|
+
puts response
|
27
|
+
end
|
28
|
+
|
29
|
+
feed.on_error do |response|
|
30
|
+
puts response
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# @param [EventMachine::Connection] conn
|
5
|
+
# @param [String] api_key
|
6
|
+
# @param [Hash] options defaults {}
|
7
|
+
def initialize(conn, api_key, options = {})
|
8
|
+
@conn = conn
|
9
|
+
@api_key = api_key
|
10
|
+
@options = options
|
11
|
+
@requests = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] response
|
15
|
+
#
|
16
|
+
# @return [Hash]
|
17
|
+
# @todo refactor ugly as Sin
|
18
|
+
def process_data(response)
|
19
|
+
parsed_response = parse_response(response)
|
20
|
+
status_ok = parsed_response["status"] && parsed_response["status"] != 200
|
21
|
+
if request = @requests[parsed_response["token"]]
|
22
|
+
if status_ok
|
23
|
+
call_block_for_request(request, parsed_response)
|
24
|
+
else
|
25
|
+
call_error_for_request_block(request, parsed_response)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
if status_ok
|
29
|
+
@conn.on_response_block.call(parsed_response) if @conn.on_response_block
|
30
|
+
else
|
31
|
+
receive_error(parsed_response)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [String] response
|
37
|
+
#
|
38
|
+
# @return [Hash]
|
39
|
+
def parse_response(response)
|
40
|
+
begin
|
41
|
+
Yajl::Parser.parse(response)
|
42
|
+
rescue Exception => e
|
43
|
+
receive_error("#{e.class}: " + [e.message, e.backtrace].flatten.join("\n\t"))
|
44
|
+
@conn.close_connection
|
45
|
+
return
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def receive_error(error)
|
50
|
+
@conn.on_error_block.call(error) if @conn.on_error_block
|
51
|
+
end
|
52
|
+
|
53
|
+
# we send the request and also keep the request with the token
|
54
|
+
# as its key so we can attach callback to requests
|
55
|
+
def send_request(method, resource, html_request = {}, token = nil, &block)
|
56
|
+
@request = Request.new(@api_key, method, resource, html_request, token)
|
57
|
+
@requests[@request.token] = @request
|
58
|
+
@conn.send_data(@request.to_json)
|
59
|
+
@request
|
60
|
+
end
|
61
|
+
|
62
|
+
# finds the correct callback based on the token which
|
63
|
+
# has the method call in its sig
|
64
|
+
def call_block_for_request(request, parsed_response)
|
65
|
+
case request.token.gsub(/:.*/, "")
|
66
|
+
when "subscribe" && !parsed_response["body"].nil?
|
67
|
+
request.on_datastream_block.call(parsed_response) if request.on_datastream_block
|
68
|
+
when "get" && !parsed_response["body"].nil?
|
69
|
+
request.on_get_block.call(parsed_response) if request.on_get_block
|
70
|
+
else
|
71
|
+
request.on_complete_block.call(parsed_response) if request.on_complete_block
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def call_error_for_request_block(request, parsed_response)
|
76
|
+
request.on_error_block.call(parsed_response) if request.on_error_block
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
class Connection < EventMachine::Connection
|
3
|
+
include RequestMethods
|
4
|
+
|
5
|
+
NF_RECONNECT_START = 0.25
|
6
|
+
NF_RECONNECT_ADD = 0.25
|
7
|
+
NF_RECONNECT_MAX = 16
|
8
|
+
RECONNECT_MAX = 320
|
9
|
+
RETRIES_MAX = 10
|
10
|
+
|
11
|
+
attr_accessor :options, :on_init_callback, :api_key, :on_response_block, :on_error_block
|
12
|
+
attr_accessor :reconnect_callback, :max_reconnects_callback, :nf_last_reconnect, :reconnect_retries
|
13
|
+
|
14
|
+
# @todo tidy as crap
|
15
|
+
def self.connect(options = {})
|
16
|
+
api_key = options[:api_key]
|
17
|
+
raise ArgumentError.new("You need to supply an API Key") unless api_key
|
18
|
+
host = options[:host] || "beta.pachube.com"
|
19
|
+
port = options[:port] || 8081
|
20
|
+
EventMachine.connect host, port, self, options
|
21
|
+
rescue EventMachine::ConnectionError => e
|
22
|
+
conn = EventMachine::FailedConnection.new(req)
|
23
|
+
conn.error = e.message
|
24
|
+
conn.fail
|
25
|
+
conn
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(options)
|
29
|
+
@options = options
|
30
|
+
@api_key = options[:api_key]
|
31
|
+
@timeout = options[:timeout] || 0
|
32
|
+
@reconnect_retries = 0
|
33
|
+
@immediate_reconnect = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def client
|
37
|
+
@client ||= PachubeStream::Client.new(self, @api_key, @options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def post_init
|
41
|
+
set_comm_inactivity_timeout @timeout if @timeout > 0
|
42
|
+
@on_inited_callback.call if @on_inited_callback
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_reconnect(&block)
|
46
|
+
@reconnect_callback = block
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_max_reconnects(&block)
|
50
|
+
@max_reconnects_callback = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_response_block(&block)
|
54
|
+
@on_response_block = block
|
55
|
+
end
|
56
|
+
|
57
|
+
def on_error_block(&block)
|
58
|
+
@on_error_block = block
|
59
|
+
end
|
60
|
+
|
61
|
+
def stop
|
62
|
+
@gracefully_closed = true
|
63
|
+
close_connection
|
64
|
+
end
|
65
|
+
|
66
|
+
def immediate_reconnect
|
67
|
+
@immediate_reconnect = true
|
68
|
+
@gracefully_closed = false
|
69
|
+
close_connection
|
70
|
+
end
|
71
|
+
|
72
|
+
def unbind
|
73
|
+
schedule_reconnect unless @gracefully_closed
|
74
|
+
end
|
75
|
+
|
76
|
+
def receive_data(response)
|
77
|
+
client.process_data(response)
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
def schedule_reconnect
|
82
|
+
timeout = reconnect_timeout
|
83
|
+
@reconnect_retries += 1
|
84
|
+
if (timeout <= RECONNECT_MAX) && (@reconnect_retries <= RETRIES_MAX)
|
85
|
+
reconnect_after(timeout)
|
86
|
+
else
|
87
|
+
@max_reconnects_callback.call(timeout, @reconnect_retries) if @max_reconnects_callback
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def reconnect_after(timeout)
|
92
|
+
@reconnect_callback.call(timeout, @reconnect_retries) if @reconnect_callback
|
93
|
+
|
94
|
+
if timeout == 0
|
95
|
+
reconnect @options[:host], @options[:port]
|
96
|
+
else
|
97
|
+
EventMachine.add_timer(timeout) do
|
98
|
+
reconnect @options[:host], @options[:port]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def reconnect_timeout
|
104
|
+
if @immediate_reconnect
|
105
|
+
@immediate_reconnect = false
|
106
|
+
return 0
|
107
|
+
end
|
108
|
+
if @nf_last_reconnect
|
109
|
+
@nf_last_reconnect += NF_RECONNECT_ADD
|
110
|
+
else
|
111
|
+
@nf_last_reconnect = NF_RECONNECT_START
|
112
|
+
end
|
113
|
+
[@nf_last_reconnect, NF_RECONNECT_MAX].min
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
class HtmlRequest < Hashie::Dash
|
3
|
+
|
4
|
+
property :resource
|
5
|
+
property :method
|
6
|
+
property :headers, :default => {}
|
7
|
+
property :token
|
8
|
+
property :body
|
9
|
+
property :params
|
10
|
+
|
11
|
+
def api_key=(api_key)
|
12
|
+
headers["X-PachubeApiKey"] = api_key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
module RequestMethods
|
3
|
+
|
4
|
+
def get(resource, html_request = {}, token = nil, &block)
|
5
|
+
client.send_request(:get, resource, html_request = {}, token, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def post(resource, html_request = {}, token = nil, &block)
|
9
|
+
client.send_request(:post, resource, html_request = {}, token, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def put(resource, html_request = {}, token = nil, &block)
|
13
|
+
client.send_request(:put, resource, html_request = {}, token, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(resource,html_request = {}, token= nil, &block)
|
17
|
+
client.send_request(:delete, resource, html_request = {}, token, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def subscribe(resource, html_request = {}, token = nil, &block)
|
21
|
+
client.send_request(:subscribe, resource, html_request = {}, token, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def unsubsribe(resource, html_request = {}, token = nil, &block)
|
25
|
+
client.send_request(:unsubsribe, resource, html_request = {}, token, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
class Request
|
3
|
+
|
4
|
+
attr_accessor :on_complete_block, :on_datastream_block, :request, :token, :method, :on_error_block, :on_get_block
|
5
|
+
|
6
|
+
# @param[String] method
|
7
|
+
# @param[String] html this means headers and body and resource and params
|
8
|
+
# @param[String] token if you wanted to send a specific token
|
9
|
+
def initialize(api_key, method, resource, html_request, token = nil)
|
10
|
+
@api_key = api_key
|
11
|
+
@method = method
|
12
|
+
@resource = resource
|
13
|
+
@html_request = html_request
|
14
|
+
@token = token || generate_token(method)
|
15
|
+
generate_html_request(api_key, method, resource, html_request, @token)
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_complete(&block)
|
19
|
+
@on_complete_block = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_datastream(&block)
|
23
|
+
@on_datastream_block = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_error(&block)
|
27
|
+
@on_error_block = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_get(&block)
|
31
|
+
@on_get_block = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_json
|
35
|
+
@request.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
def generate_token(method)
|
40
|
+
"#{method}:#{UUID.generate}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_html_request(api_key, method, resource, html_request, token)
|
44
|
+
@request = HtmlRequest.new(html_request)
|
45
|
+
@request.api_key = api_key
|
46
|
+
@request[:method] = method
|
47
|
+
@request[:resource] = resource
|
48
|
+
@request[:token] = token
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PachubeStream
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'addressable/uri'
|
6
|
+
require "hashie"
|
7
|
+
require 'yajl'
|
8
|
+
require 'json'
|
9
|
+
require 'uuid'
|
10
|
+
require 'pachube-stream/methods'
|
11
|
+
require 'pachube-stream/connection'
|
12
|
+
require 'pachube-stream/client'
|
13
|
+
require 'pachube-stream/request'
|
14
|
+
require 'pachube-stream/html_request'
|
15
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "pachube-stream/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "pachube-stream"
|
7
|
+
s.version = PachubeStream::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Hookercookerman"]
|
10
|
+
s.email = ["hookercookerman@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Pachube TCP streaming API}
|
13
|
+
s.description = %q{Simple Ruby client library for pachube TCP streaming API. Uses EventMachine for connection handling.JSON format only.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "pachube-stream"
|
16
|
+
|
17
|
+
s.add_dependency "eventmachine", ">= 1.0.0.beta.3"
|
18
|
+
s.add_dependency "addressable", ">= 2.2.3"
|
19
|
+
s.add_dependency "hashie", ">= 0.5.1"
|
20
|
+
s.add_dependency 'yajl-ruby', '~> 0.8.2'
|
21
|
+
s.add_dependency 'uuid', '~> 2.3.2'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
{
|
2
|
+
"body":
|
3
|
+
{
|
4
|
+
"location":
|
5
|
+
{
|
6
|
+
"lon":-0.0215005874633789,
|
7
|
+
"domain":"physical",
|
8
|
+
"disposition":"fixed",
|
9
|
+
"exposure":"indoor",
|
10
|
+
"lat":51.4722148395794
|
11
|
+
},
|
12
|
+
"title":"CurrentCost meter",
|
13
|
+
"datastreams":
|
14
|
+
[
|
15
|
+
{
|
16
|
+
"current_value":"32.2",
|
17
|
+
"max_value":"32.3",
|
18
|
+
"min_value":"9.3",
|
19
|
+
"id":"0",
|
20
|
+
"tags":
|
21
|
+
[
|
22
|
+
"celsius",
|
23
|
+
"degrees",
|
24
|
+
"temperature"
|
25
|
+
],
|
26
|
+
"unit":
|
27
|
+
{
|
28
|
+
"label":"Celsius",
|
29
|
+
"symbol":"C",
|
30
|
+
"type":"basicSI"
|
31
|
+
},
|
32
|
+
"at":"2011-04-20T12:35:42.653484Z"
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"current_value":"309.0",
|
36
|
+
"max_value":"12675.0",
|
37
|
+
"min_value":"24.0",
|
38
|
+
"id":"1",
|
39
|
+
"tags":
|
40
|
+
[
|
41
|
+
"electricity",
|
42
|
+
"power",
|
43
|
+
"watts"
|
44
|
+
],
|
45
|
+
"unit":
|
46
|
+
{
|
47
|
+
"label":"Watts",
|
48
|
+
"symbol":"W",
|
49
|
+
"type":"derivedSI"
|
50
|
+
},
|
51
|
+
"at":"2011-04-20T12:35:42.653484Z"
|
52
|
+
}
|
53
|
+
],
|
54
|
+
"creator":"http://www.pachube.com/users/sc84647",
|
55
|
+
"private":"false",
|
56
|
+
"id":6643,
|
57
|
+
"version":"1.0.0",
|
58
|
+
"updated":"2011-04-20T12:35:42.653484Z",
|
59
|
+
"status":"live",
|
60
|
+
"feed":"http://api.pachube.com/v2/feeds/6643.json"
|
61
|
+
},
|
62
|
+
"resource":"/feeds/6643",
|
63
|
+
"token":"subscribe:a06d6f30-4d78-012e-e33d-002332cf7bbe"
|
64
|
+
}
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path("../spec_helper", __FILE__)
|
3
|
+
|
4
|
+
describe "Pachube" do
|
5
|
+
context "on connection" do
|
6
|
+
it "should return stream" do
|
7
|
+
EM.should_receive(:connect).and_return('TESTING CONNECT')
|
8
|
+
stream = PachubeStream::Connection.connect(:api_key => "Testing")
|
9
|
+
stream.should == 'TESTING CONNECT'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should define default properties" do
|
13
|
+
EM.should_receive(:connect).with do |host, port, handler, opts|
|
14
|
+
host.should == 'beta.pachube.com'
|
15
|
+
port.should == 8081
|
16
|
+
end
|
17
|
+
stream = PachubeStream::Connection.connect(:api_key => "Testing")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Connection #subscribe" do
|
22
|
+
attr_reader :stream
|
23
|
+
before(:each) do
|
24
|
+
$data_to_send = read_fixture('pachube/subscribe.json')
|
25
|
+
$recieved_data = ''
|
26
|
+
$close_connection = false
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should send the 'http_request json request' with the method subscribe'" do
|
30
|
+
connect_stream do |connection|
|
31
|
+
connection.subscribe("/feeds/100", {}, "token")
|
32
|
+
end
|
33
|
+
Yajl::Parser.parse($recieved_data).should == Yajl::Parser.parse(read_fixture('pachube/subscribe_request.json'))
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should capture response to the on_compelete as that is not a datastream response" do
|
37
|
+
connect_stream do |connection|
|
38
|
+
subscription = connection.subscribe("/feeds/100", {}, "subscribe:a06d6f30-4d78-012e-e33d-002332cf7bbe")
|
39
|
+
subscription.on_complete do |response|
|
40
|
+
$data_to_send = read_fixture('pachube/subscribe_data_stream.json')
|
41
|
+
response.should eq(Yajl::Parser.parse(read_fixture('pachube/subscribe.json')))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should send capture response on_datastream for datastream" do
|
47
|
+
$data_to_send = read_fixture('pachube/subscribe_data_stream.json')
|
48
|
+
connect_stream do |connection|
|
49
|
+
subscription = connection.subscribe("/feeds/100", {}, "subscribe:a06d6f30-4d78-012e-e33d-002332cf7bbe")
|
50
|
+
subscription.on_datastream do |response|
|
51
|
+
response.should eq(Yajl::Parser.parse(read_fixture('pachube/subscribe_data_stream.json')))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should capture to the on_error for the request when status code other then 200" do
|
57
|
+
$data_to_send = read_fixture('pachube/not_authorized.json')
|
58
|
+
connect_stream do |connection|
|
59
|
+
subscription = connection.subscribe("/feeds/100", {}, "subscribe:a06d6f30-4d78-012e-e33d-002332cf7bbe")
|
60
|
+
subscription.on_error do |response|
|
61
|
+
response.should eq(Yajl::Parser.parse(read_fixture('pachube/not_authorized.json')))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "network failure" do
|
68
|
+
before(:each) do
|
69
|
+
$close_connection = true
|
70
|
+
$data_to_send = ''
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should reconnect with 0.25 at base" do
|
74
|
+
connect_stream do |connection|
|
75
|
+
connection.should_receive(:reconnect_after).with(0.25)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should reconnect with linear timeout" do
|
80
|
+
connect_stream do |connection|
|
81
|
+
connection.nf_last_reconnect = 1
|
82
|
+
connection.should_receive(:reconnect_after).with(1.25)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should stop reconnecting after 100 times" do
|
87
|
+
connect_stream do |connection|
|
88
|
+
connection.reconnect_retries = 100
|
89
|
+
connection.should_not_receive(:reconnect_after)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should notify after reconnect limit is reached" do
|
94
|
+
timeout, retries = nil, nil
|
95
|
+
connect_stream do |connection|
|
96
|
+
connection.on_max_reconnects do |t, r|
|
97
|
+
timeout, retries = t, r
|
98
|
+
end
|
99
|
+
connection.reconnect_retries = 100
|
100
|
+
end
|
101
|
+
timeout.should == 0.25
|
102
|
+
retries.should == 101
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require "pachube-stream"
|
6
|
+
Bundler.require(:test)
|
7
|
+
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
|
11
|
+
def fixture_path(path)
|
12
|
+
File.join(File.dirname(__FILE__), 'fixtures', path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_fixture(path)
|
16
|
+
File.read(fixture_path(path))
|
17
|
+
end
|
18
|
+
|
19
|
+
Host = "127.0.0.1"
|
20
|
+
Port = 9550
|
21
|
+
|
22
|
+
# == What would be awsome to capture the response tcpdump or something and the replay
|
23
|
+
class PachubeServer < EM::Connection
|
24
|
+
attr_accessor :data
|
25
|
+
def receive_data data
|
26
|
+
$recieved_data = data
|
27
|
+
send_data $data_to_send
|
28
|
+
EventMachine.next_tick {
|
29
|
+
close_connection if $close_connection
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def connect_stream(opts={}, &blk)
|
35
|
+
EM.run {
|
36
|
+
opts.merge!(:host => Host, :port => Port)
|
37
|
+
stop_in = opts.delete(:stop_in) || 0.5
|
38
|
+
unless opts[:start_server] == false
|
39
|
+
EM.start_server Host, Port, PachubeServer
|
40
|
+
end
|
41
|
+
@stream = PachubeStream::Connection.connect(:api_key => "testing", :host => Host, :port => Port)
|
42
|
+
blk.call(@stream) if blk
|
43
|
+
EM.add_timer(stop_in){ EM.stop }
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
Rspec.configure do |config|
|
48
|
+
config.mock_with :rspec
|
49
|
+
config.filter_run :focus => true
|
50
|
+
config.run_all_when_everything_filtered = true
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pachube-stream
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Hookercookerman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-04-20 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: eventmachine
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
- beta
|
33
|
+
- 3
|
34
|
+
version: 1.0.0.beta.3
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: addressable
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 2
|
48
|
+
- 3
|
49
|
+
version: 2.2.3
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: hashie
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
- 5
|
63
|
+
- 1
|
64
|
+
version: 0.5.1
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: yajl-ruby
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
- 8
|
78
|
+
- 2
|
79
|
+
version: 0.8.2
|
80
|
+
type: :runtime
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: uuid
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 2
|
92
|
+
- 3
|
93
|
+
- 2
|
94
|
+
version: 2.3.2
|
95
|
+
type: :runtime
|
96
|
+
version_requirements: *id005
|
97
|
+
description: Simple Ruby client library for pachube TCP streaming API. Uses EventMachine for connection handling.JSON format only.
|
98
|
+
email:
|
99
|
+
- hookercookerman@gmail.com
|
100
|
+
executables: []
|
101
|
+
|
102
|
+
extensions: []
|
103
|
+
|
104
|
+
extra_rdoc_files: []
|
105
|
+
|
106
|
+
files:
|
107
|
+
- .gitignore
|
108
|
+
- Gemfile
|
109
|
+
- README.rdoc
|
110
|
+
- Rakefile
|
111
|
+
- autotest/discover.rb
|
112
|
+
- examples/subscribe.rb
|
113
|
+
- lib/pachube-stream.rb
|
114
|
+
- lib/pachube-stream/client.rb
|
115
|
+
- lib/pachube-stream/connection.rb
|
116
|
+
- lib/pachube-stream/html_request.rb
|
117
|
+
- lib/pachube-stream/methods.rb
|
118
|
+
- lib/pachube-stream/request.rb
|
119
|
+
- lib/pachube-stream/version.rb
|
120
|
+
- pachube-stream.gemspec
|
121
|
+
- spec/fixtures/pachube/not_authorized.json
|
122
|
+
- spec/fixtures/pachube/subscribe.json
|
123
|
+
- spec/fixtures/pachube/subscribe_data_stream.json
|
124
|
+
- spec/fixtures/pachube/subscribe_request.json
|
125
|
+
- spec/pachube_spec.rb
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
has_rdoc: true
|
128
|
+
homepage: ""
|
129
|
+
licenses: []
|
130
|
+
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
segments:
|
142
|
+
- 0
|
143
|
+
version: "0"
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
segments:
|
150
|
+
- 0
|
151
|
+
version: "0"
|
152
|
+
requirements: []
|
153
|
+
|
154
|
+
rubyforge_project: pachube-stream
|
155
|
+
rubygems_version: 1.3.7
|
156
|
+
signing_key:
|
157
|
+
specification_version: 3
|
158
|
+
summary: Pachube TCP streaming API
|
159
|
+
test_files:
|
160
|
+
- spec/fixtures/pachube/not_authorized.json
|
161
|
+
- spec/fixtures/pachube/subscribe.json
|
162
|
+
- spec/fixtures/pachube/subscribe_data_stream.json
|
163
|
+
- spec/fixtures/pachube/subscribe_request.json
|
164
|
+
- spec/pachube_spec.rb
|
165
|
+
- spec/spec_helper.rb
|