sparks 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -0
- data/lib/sparks.rb +145 -99
- data/lib/sparks/version.rb +2 -2
- data/sparks.gemspec +19 -18
- metadata +82 -66
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Sparks changelog
|
2
2
|
|
3
|
+
## Version 0.4 (20 January 2013)
|
4
|
+
|
5
|
+
- broke everything. sorry.
|
6
|
+
- streaming now works for more than a few minutes!
|
7
|
+
- handles both very small and very large JSON objects
|
8
|
+
- uses Yajl for streaming JSON parsing
|
9
|
+
- uses net-http-persistent to make repeated requests much faster
|
10
|
+
- removed Room and Campfire classes, now super...minimal. or something.
|
11
|
+
|
3
12
|
## Version 0.3 (7 October 2011)
|
4
13
|
|
5
14
|
- added support for play and tweet message types (@imajes)
|
data/Gemfile
ADDED
data/lib/sparks.rb
CHANGED
@@ -1,124 +1,170 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'logger'
|
2
|
+
require 'net/http/persistent'
|
3
|
+
require 'yajl'
|
4
4
|
|
5
5
|
# sparks, a tiny Campfire library
|
6
6
|
|
7
7
|
# Usage:
|
8
|
-
# c = Sparks
|
9
|
-
# r = c.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
# c = Sparks.new('subdomain', 'abc123')
|
9
|
+
# r = c.room "Room Name"
|
10
|
+
# c.say r["id"], "hi there"
|
11
|
+
# c.paste r["id"], "class Foo\nend"
|
12
|
+
class Sparks
|
13
|
+
attr_reader :logger
|
14
|
+
|
15
|
+
def initialize subdomain, token, opts = {}
|
16
|
+
@base = URI("https://#{subdomain}.campfirenow.com")
|
17
|
+
@token = token
|
18
|
+
@logger = opts[:logger] || Logger.new(STDOUT)
|
19
|
+
@http = Net::HTTP::Persistent.new("sparks")
|
20
|
+
@http.ca_file = opts[:ca_file] if opts[:ca_file]
|
21
|
+
@rooms ||= {}
|
22
|
+
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
args.unshift(@id)
|
25
|
-
@api.send method, *args, &block
|
26
|
-
end
|
27
|
-
end
|
24
|
+
def me
|
25
|
+
user("me")
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
28
|
+
def user(id)
|
29
|
+
req("/users/#{id}")[:user]
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@pass = 'x'
|
32
|
+
def room_named(name)
|
33
|
+
req("/rooms")[:rooms].find{|r| r[:name] == name }
|
34
|
+
end
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
campfire_ca_certs = File.expand_path("../rapidssl.crt", __FILE__)
|
42
|
-
@http.ca_file = opts[:ca_file] || campfire_ca_certs
|
36
|
+
def room(id)
|
37
|
+
@rooms[id] ||= begin
|
38
|
+
req("/room/#{id.to_s}")[:room]
|
43
39
|
end
|
40
|
+
end
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
42
|
+
def join(id)
|
43
|
+
req("/room/#{id}/join", :post)
|
44
|
+
end
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
req['Content-Type'] = 'application/json'
|
54
|
-
req.basic_auth token, pass
|
55
|
-
begin
|
56
|
-
JSON.parse(http.request(req).body)["rooms"]
|
57
|
-
rescue JSON::ParserError
|
58
|
-
{}
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
46
|
+
def leave(id)
|
47
|
+
req("/room/#{id}/leave", :post)
|
48
|
+
end
|
62
49
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
50
|
+
def speak(id, message, type = 'TextMessage')
|
51
|
+
data = {'body' => message, 'type' => type}
|
52
|
+
json = Yajl::Encoder.encode('message' => data)
|
53
|
+
req("/room/#{id}/speak", json)
|
54
|
+
end
|
55
|
+
alias_method :say, :speak
|
67
56
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
req.basic_auth token, pass
|
72
|
-
http.request(req, json)
|
73
|
-
end
|
74
|
-
end
|
57
|
+
def paste(id, message)
|
58
|
+
speak id, message, 'PasteMessage'
|
59
|
+
end
|
75
60
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
alias_method :say, :speak
|
61
|
+
def play(id, message)
|
62
|
+
speak id, message, 'SoundMessage'
|
63
|
+
end
|
80
64
|
|
81
|
-
|
82
|
-
|
83
|
-
|
65
|
+
def tweet(id, message)
|
66
|
+
speak id, message, 'TweetMessage'
|
67
|
+
end
|
84
68
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
69
|
+
def watch(id)
|
70
|
+
# campfire won't let you stream until you've joined the room
|
71
|
+
join(id)
|
72
|
+
|
73
|
+
# don't allow retries if we've never connected before.
|
74
|
+
retries ||= nil
|
75
|
+
|
76
|
+
uri = URI("https://streaming.campfirenow.com") + "/room/#{id}/live.json"
|
77
|
+
logger.debug "Ready to stream from #{uri}"
|
78
|
+
|
79
|
+
request = Net::HTTP::Get.new(uri.path)
|
80
|
+
request.basic_auth @token, "x"
|
81
|
+
|
82
|
+
@http.request(uri, request) do |response|
|
83
|
+
logger.debug "Connected and streaming from room #{id}"
|
84
|
+
# connected! allow retries.
|
85
|
+
retries = 0
|
92
86
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
87
|
+
# Set up a Yajl stream parser
|
88
|
+
parser = Yajl::Parser.new(:symbolize_keys => true)
|
89
|
+
parser.on_parse_complete = -> hash { yield hash }
|
90
|
+
|
91
|
+
# Feed chunks into the stream parser
|
92
|
+
response.read_body do |chunk|
|
93
|
+
# Campfire keepalive pings
|
94
|
+
next if chunk == " "
|
95
|
+
parser << chunk
|
98
96
|
end
|
99
97
|
end
|
98
|
+
rescue Yajl::ParseError, # Bad JSON in the response
|
99
|
+
SystemCallError, # All Errno errors
|
100
|
+
SocketError, # Errors from socket operations
|
101
|
+
Net::HTTP::Persistent::Error, # Timeout, SSL, or connection error
|
102
|
+
Net::HTTPBadResponse, # response wasn't 2xx
|
103
|
+
Net::HTTPHeaderSyntaxError, # response header issue
|
104
|
+
Net::ProtocolError => e # not http
|
105
|
+
# pass through errors if we haven't ever connected
|
106
|
+
raise e unless retries
|
107
|
+
# if we connected at least once, try, try, again
|
108
|
+
retries += 1
|
109
|
+
logger.error "#{e.class}: #{e.message}"
|
110
|
+
logger.error "Trying to stream again in #{retries * 2}s"
|
111
|
+
sleep retries * 2
|
112
|
+
retry
|
113
|
+
end
|
100
114
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
req.basic_auth token, pass
|
111
|
-
http.request(req) do |res|
|
112
|
-
res.read_body do |chunk|
|
113
|
-
unless chunk.strip.empty?
|
114
|
-
chunk.split("\r").each do |message|
|
115
|
-
yield JSON.parse(message)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
115
|
+
def req(uri, body = nil)
|
116
|
+
uri = @base + (uri + ".json") unless uri.is_a?(URI)
|
117
|
+
logger.debug "#{body ? 'POST' : 'GET'} #{uri}"
|
118
|
+
|
119
|
+
if body
|
120
|
+
request = Net::HTTP::Post.new(uri.path)
|
121
|
+
request.body = body unless body == :post
|
122
|
+
else
|
123
|
+
request = Net::HTTP::Get.new(uri.path)
|
121
124
|
end
|
125
|
+
request.content_type = "application/json"
|
126
|
+
request.basic_auth @token, "x"
|
127
|
+
|
128
|
+
retries ||= 0
|
129
|
+
response = @http.request(uri, request)
|
130
|
+
response.value # raises if response is not 2xx
|
131
|
+
parse_response(response)
|
132
|
+
|
133
|
+
rescue Net::HTTPRetriableError => e # response was 3xx
|
134
|
+
location = URI(response['location'])
|
135
|
+
logger.info "Request redirected to #{location}"
|
136
|
+
sleep 2
|
137
|
+
req(location, body)
|
138
|
+
|
139
|
+
rescue Net::HTTPServerException => e # response was 4xx
|
140
|
+
msg = "Authorization failed: HTTP #{response.code}"
|
141
|
+
msg << ": " << request.body if request.body && !request.body.empty?
|
142
|
+
raise msg
|
143
|
+
|
144
|
+
rescue SystemCallError, # All Errno errors
|
145
|
+
Net::HTTP::Persistent::Error, # Timeout, SSL, or connection error
|
146
|
+
Net::HTTPBadResponse, # response wasn't 2xx
|
147
|
+
Net::HTTPHeaderSyntaxError, # response header issue
|
148
|
+
Net::ProtocolError => e # not http
|
149
|
+
# Retry if something goes wrong
|
150
|
+
retries += 1
|
151
|
+
logger.info "Request failed: #{e.class}: #{e.message}"
|
152
|
+
logger.info "Going to retry request in #{retries * 2}s"
|
153
|
+
sleep retries * 2
|
154
|
+
retry
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
122
158
|
|
159
|
+
def parse_response(response)
|
160
|
+
if response.body.strip.empty?
|
161
|
+
true
|
162
|
+
else
|
163
|
+
Yajl::Parser.parse(response.body, :symbolize_keys => true)
|
164
|
+
end
|
165
|
+
rescue Yajl::ParseError
|
166
|
+
logger.debug "Couldn't parse #{res.inspect}: #{res.body.inspect}"
|
167
|
+
{}
|
123
168
|
end
|
169
|
+
|
124
170
|
end
|
data/lib/sparks/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.
|
1
|
+
class Sparks
|
2
|
+
VERSION = "0.4"
|
3
3
|
end
|
data/sparks.gemspec
CHANGED
@@ -1,23 +1,24 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require "sparks/version"
|
4
4
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
s.description = %q{A tiny Campfire client API that only uses the standard library}
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "sparks"
|
7
|
+
gem.version = Sparks::VERSION
|
8
|
+
gem.summary = %q{A tiny Campfire client API}
|
9
|
+
gem.description = %q{Yet another Campfire client. Because oh my god so many dependencies.}
|
10
|
+
gem.authors = ["André Arko"]
|
11
|
+
gem.email = ["andre@arko.net"]
|
12
|
+
gem.homepage = "http://github.com/indirect/sparks"
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
gem.add_dependency "yajl-ruby", "~> 1.1"
|
20
|
+
gem.add_dependency "net-http-persistent", "~> 2.8"
|
21
|
+
|
22
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
23
|
+
gem.add_development_dependency "bundler", "~> 1.2"
|
23
24
|
end
|
metadata
CHANGED
@@ -1,102 +1,118 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: sparks
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
version: "0.3"
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
12
|
-
-
|
7
|
+
authors:
|
8
|
+
- André Arko
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
date: 2013-04-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yajl-ruby
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.1'
|
22
|
+
type: :runtime
|
22
23
|
prerelease: false
|
23
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.1'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: net-http-persistent
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
24
33
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.8'
|
32
38
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: bundler
|
36
39
|
prerelease: false
|
37
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.8'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
38
65
|
none: false
|
39
|
-
requirements:
|
66
|
+
requirements:
|
40
67
|
- - ~>
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
segments:
|
44
|
-
- 1
|
45
|
-
- 0
|
46
|
-
version: "1.0"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.2'
|
47
70
|
type: :development
|
48
|
-
|
49
|
-
|
50
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.2'
|
78
|
+
description: Yet another Campfire client. Because oh my god so many dependencies.
|
79
|
+
email:
|
51
80
|
- andre@arko.net
|
52
81
|
executables: []
|
53
|
-
|
54
82
|
extensions: []
|
55
|
-
|
56
83
|
extra_rdoc_files: []
|
57
|
-
|
58
|
-
files:
|
84
|
+
files:
|
59
85
|
- .gitignore
|
60
86
|
- CHANGELOG.md
|
87
|
+
- Gemfile
|
61
88
|
- README.md
|
62
89
|
- Rakefile
|
63
90
|
- lib/rapidssl.crt
|
64
91
|
- lib/sparks.rb
|
65
92
|
- lib/sparks/version.rb
|
66
93
|
- sparks.gemspec
|
67
|
-
has_rdoc: true
|
68
94
|
homepage: http://github.com/indirect/sparks
|
69
95
|
licenses: []
|
70
|
-
|
71
96
|
post_install_message:
|
72
97
|
rdoc_options: []
|
73
|
-
|
74
|
-
require_paths:
|
98
|
+
require_paths:
|
75
99
|
- lib
|
76
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
101
|
none: false
|
78
|
-
requirements:
|
79
|
-
- -
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
82
|
-
|
83
|
-
- 0
|
84
|
-
version: "0"
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
107
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
94
112
|
requirements: []
|
95
|
-
|
96
|
-
|
97
|
-
rubygems_version: 1.3.7
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.23
|
98
115
|
signing_key:
|
99
116
|
specification_version: 3
|
100
117
|
summary: A tiny Campfire client API
|
101
118
|
test_files: []
|
102
|
-
|