zeppelin 0.1.0
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/.autotest +15 -0
- data/.gitignore +21 -0
- data/.travis.yml +6 -0
- data/.yardopts +6 -0
- data/Gemfile +2 -0
- data/LICENSE +19 -0
- data/README.md +47 -0
- data/Rakefile +11 -0
- data/lib/zeppelin/version.rb +3 -0
- data/lib/zeppelin.rb +123 -0
- data/test/support/spec_reporter.rb +222 -0
- data/test/test_helper.rb +9 -0
- data/test/zeppelin_test.rb +232 -0
- data/zeppelin.gemspec +29 -0
- metadata +137 -0
data/.autotest
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Autotest.add_hook :initialize do |at|
|
2
|
+
# Requires bundler before minitest since the minitest bundled with Ruby does
|
3
|
+
# not support runners.
|
4
|
+
at.testlib = 'bundler/setup minitest/unit'
|
5
|
+
|
6
|
+
# Mappings from http://opensoul.org/blog/archives/2008/08/22/autotest-mapping-for-rails-test-conventions/.
|
7
|
+
at.clear_mappings
|
8
|
+
|
9
|
+
at.add_mapping %r%/^lib/(.*)\.rb$% do |_, m|
|
10
|
+
possible = File.basename(m[1])
|
11
|
+
files_matching %r%^test/.*(#{possible}_test|test_#{possible})\.rb$%
|
12
|
+
end
|
13
|
+
|
14
|
+
at.add_mapping(%r%^test/.*\.rb$%) { |filename, _| filename }
|
15
|
+
end
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Alexander Kern
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# zeppelin - Urban Airship library for Ruby [](http://stillmaintained.com/CapnKernul/zeppelin) [](http://travis-ci.org/CapnKernul/zeppelin) #
|
2
|
+
|
3
|
+
Ruby client for the [Urban Airship](http://urbanairship.com) Push Notification
|
4
|
+
API.
|
5
|
+
|
6
|
+
## Installation ##
|
7
|
+
|
8
|
+
Without bundler:
|
9
|
+
|
10
|
+
gem install zeppelin
|
11
|
+
|
12
|
+
With bundler:
|
13
|
+
|
14
|
+
gem 'zeppelin'
|
15
|
+
|
16
|
+
## Usage ##
|
17
|
+
|
18
|
+
# First, create a client.
|
19
|
+
client = Zeppelin.new('your app key', 'your app master secret')
|
20
|
+
|
21
|
+
# You can then use the client to push messages to Urban Airship. The options
|
22
|
+
# for push are converted to JSON and sent as the payload.
|
23
|
+
client.push(:device_tokens => ['devtoken'], :aps => { :badge => 10 })
|
24
|
+
|
25
|
+
Check out the docs for more ways of querying the API.
|
26
|
+
|
27
|
+
## TODO ##
|
28
|
+
|
29
|
+
* Add support for the statistics API.
|
30
|
+
* Add support for the device token list API.
|
31
|
+
|
32
|
+
## Note on Patches/Pull Requests ##
|
33
|
+
|
34
|
+
* Fork the project.
|
35
|
+
* Make your feature addition or bug fix.
|
36
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
37
|
+
* Commit, but do not mess with the `Rakefile`. If you want to have your own version, that is fine but bump the version in a commit by itself in another branch so I can ignore it when I pull.
|
38
|
+
* Send me a pull request. Bonus points for git flow feature branches.
|
39
|
+
|
40
|
+
## Resources ##
|
41
|
+
|
42
|
+
* [GitHub Repository](https://github.com/CapnKernul/zeppelin)
|
43
|
+
* [Documentation](http://rubydoc.info/github/CapnKernul/zeppelin)
|
44
|
+
|
45
|
+
## License ##
|
46
|
+
|
47
|
+
Zeppelin is licensed under the MIT License. See `LICENSE` for details.
|
data/Rakefile
ADDED
data/lib/zeppelin.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'yajl'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
# A very tiny Urban Airship Push Notification API client.
|
6
|
+
#
|
7
|
+
# Provides thin wrappers around API calls to the most common API tasks. For more
|
8
|
+
# information on how the requests and responses are formatted, visit the [Urban
|
9
|
+
# Airship Push Notification API docs](http://urbanairship.com/docs/push.html).
|
10
|
+
class Zeppelin
|
11
|
+
BASE_URI = 'https://go.urbanairship.com'
|
12
|
+
PUSH_URI = '/api/push/'
|
13
|
+
BATCH_PUSH_URI = '/api/push/batch/'
|
14
|
+
BROADCAST_URI = '/api/push/broadcast/'
|
15
|
+
SUCCESSFUL_STATUS_CODES = (200..299)
|
16
|
+
JSON_HEADERS = { 'Content-Type' => 'application/json' }
|
17
|
+
|
18
|
+
# The connection to `https://go.urbanairship.com`.
|
19
|
+
attr_reader :connection
|
20
|
+
|
21
|
+
# Creates a new client.
|
22
|
+
#
|
23
|
+
# @param [String] application_key your Urban Airship Application Key
|
24
|
+
# @param [String] application_master_secret your Urban Airship Application
|
25
|
+
# Master Secret
|
26
|
+
def initialize(application_key, application_master_secret)
|
27
|
+
@connection = Faraday::Connection.new(BASE_URI) do |builder|
|
28
|
+
builder.request :json
|
29
|
+
builder.adapter :net_http
|
30
|
+
end
|
31
|
+
|
32
|
+
@connection.basic_auth(application_key, application_master_secret)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Registers a device token.
|
36
|
+
#
|
37
|
+
# @param [String] device_token
|
38
|
+
# @param [Hash] payload the payload to send during registration
|
39
|
+
# @return [Boolean] whether or not the registration was successful
|
40
|
+
def register_device_token(device_token, payload = {})
|
41
|
+
uri = device_token_uri(device_token)
|
42
|
+
|
43
|
+
if payload.empty?
|
44
|
+
response = @connection.put(uri)
|
45
|
+
else
|
46
|
+
response = @connection.put(uri, payload, JSON_HEADERS)
|
47
|
+
end
|
48
|
+
|
49
|
+
successful?(response)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieves information on a device token.
|
53
|
+
#
|
54
|
+
# @param [String] device_token
|
55
|
+
# @return [Hash, nil]
|
56
|
+
def device_token(device_token)
|
57
|
+
response = @connection.get(device_token_uri(device_token))
|
58
|
+
successful?(response) ? Yajl::Parser.parse(response.body) : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Deletes a device token.
|
62
|
+
#
|
63
|
+
# @param [String] device_token
|
64
|
+
# @return [Boolean] whether or not the deletion was successful
|
65
|
+
def delete_device_token(device_token)
|
66
|
+
response = @connection.delete(device_token_uri(device_token))
|
67
|
+
successful?(response)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Pushes a message.
|
71
|
+
#
|
72
|
+
# @param [Hash] payload the payload of the message
|
73
|
+
# @return [Boolean] whether or not pushing the message was successful
|
74
|
+
def push(payload)
|
75
|
+
response = @connection.post(PUSH_URI, payload, JSON_HEADERS)
|
76
|
+
successful?(response)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Batch pushes multiple messages.
|
80
|
+
#
|
81
|
+
# @param [<Hash>] payload the payloads of each message
|
82
|
+
# @return [Boolean] whether or not pushing the messages was successful
|
83
|
+
def batch_push(*payload)
|
84
|
+
response = @connection.post(BATCH_PUSH_URI, payload, JSON_HEADERS)
|
85
|
+
successful?(response)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Broadcasts a message.
|
89
|
+
#
|
90
|
+
# @param [Hash] payload the payload of the message
|
91
|
+
# @return [Boolean] whether or not broadcasting the message was successful
|
92
|
+
def broadcast(payload)
|
93
|
+
response = @connection.post(BROADCAST_URI, payload, JSON_HEADERS)
|
94
|
+
successful?(response)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Retrieves feedback on device tokens.
|
98
|
+
#
|
99
|
+
# This is useful for removing inactive device tokens for the database.
|
100
|
+
#
|
101
|
+
# @param [Time] since the time to retrieve inactive tokens from
|
102
|
+
# @return [Hash, nil]
|
103
|
+
def feedback(since)
|
104
|
+
response = @connection.get(feedback_uri(since))
|
105
|
+
successful?(response) ? Yajl::Parser.parse(response.body) : nil
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def device_token_uri(device_token)
|
111
|
+
"/api/device_tokens/#{device_token}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def feedback_uri(since)
|
115
|
+
"/api/device_tokens/feedback/?since=#{since.utc.iso8601}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def successful?(response)
|
119
|
+
SUCCESSFUL_STATUS_CODES.include?(response.status)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
require 'zeppelin/version'
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'minitest/unit'
|
2
|
+
require 'ansi'
|
3
|
+
|
4
|
+
# Code for this runner has been borrowed and modified from MiniTest, written by
|
5
|
+
# Ryan Davis of Seattle.rb. MiniTest is licensed under the MIT License, and can
|
6
|
+
# be found on GitHub at https://github.com/seattlerb/minitest.
|
7
|
+
#
|
8
|
+
# This code is also heavily based upon these gists as well, which don't appear
|
9
|
+
# to have a license:
|
10
|
+
# * https://gist.github.com/356945
|
11
|
+
# * https://gist.github.com/960669
|
12
|
+
#
|
13
|
+
# @abstract
|
14
|
+
# @todo Add documentation to everything.
|
15
|
+
module MiniTest
|
16
|
+
class Reporter
|
17
|
+
attr_accessor :runner
|
18
|
+
|
19
|
+
def print(*args)
|
20
|
+
runner.output.print(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def puts(*args)
|
24
|
+
runner.output.puts(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_suites(suites); end
|
28
|
+
def after_suites(suites); end
|
29
|
+
def before_suite(suite); end
|
30
|
+
def after_suite(suite); end
|
31
|
+
def before_test(suite, test); end
|
32
|
+
def pass(suite, test); end
|
33
|
+
def skip(suite, test, e); end
|
34
|
+
def failure(suite, test, e); end
|
35
|
+
def error(suite, test, e); end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module MiniTest
|
40
|
+
class SpecReporter < Reporter
|
41
|
+
include ANSI::Code
|
42
|
+
|
43
|
+
TEST_PADDING = 2
|
44
|
+
INFO_PADDING = 8
|
45
|
+
MARK_SIZE = 5
|
46
|
+
|
47
|
+
def before_suites(suites)
|
48
|
+
@suites_start_time = Time.now
|
49
|
+
puts 'Started'
|
50
|
+
puts
|
51
|
+
end
|
52
|
+
|
53
|
+
def after_suites(suites)
|
54
|
+
total_time = Time.now - @suites_start_time
|
55
|
+
|
56
|
+
puts('Finished in %.5fs' % total_time)
|
57
|
+
print('%d tests, %d assertions, ' % [runner.test_count, runner.assertion_count])
|
58
|
+
print(red { '%d failures, %d errors, ' } % [runner.failures, runner.errors])
|
59
|
+
print(yellow { '%d skips' } % runner.skips)
|
60
|
+
puts
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_suite(suite)
|
64
|
+
puts suite
|
65
|
+
end
|
66
|
+
|
67
|
+
def after_suite(suite)
|
68
|
+
puts
|
69
|
+
end
|
70
|
+
|
71
|
+
def before_test(suite, test)
|
72
|
+
@test_start_time = Time.now
|
73
|
+
end
|
74
|
+
|
75
|
+
def pass(suite, test)
|
76
|
+
print(green { pad_mark('PASS') })
|
77
|
+
print_test_with_time(test)
|
78
|
+
puts
|
79
|
+
end
|
80
|
+
|
81
|
+
def skip(suite, test, e)
|
82
|
+
print(yellow { pad_mark('SKIP') })
|
83
|
+
print_test_with_time(test)
|
84
|
+
puts
|
85
|
+
end
|
86
|
+
|
87
|
+
def failure(suite, test, e)
|
88
|
+
print(red { pad_mark('FAIL') })
|
89
|
+
print_test_with_time(test)
|
90
|
+
puts
|
91
|
+
print_info(e)
|
92
|
+
puts
|
93
|
+
end
|
94
|
+
|
95
|
+
def error(suite, test, e)
|
96
|
+
print(red { pad_mark('ERROR') })
|
97
|
+
print_test_with_time(test)
|
98
|
+
puts
|
99
|
+
print_info(e)
|
100
|
+
puts
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def print_test_with_time(test)
|
106
|
+
total_time = Time.now - @test_start_time
|
107
|
+
print(" #{test} (%.2fs)" % total_time)
|
108
|
+
end
|
109
|
+
|
110
|
+
def print_info(e)
|
111
|
+
e.message.each_line { |line| puts pad(line, INFO_PADDING) }
|
112
|
+
|
113
|
+
trace = MiniTest.filter_backtrace(e.backtrace)
|
114
|
+
trace.each { |line| puts pad(line, INFO_PADDING) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def pad(str, size)
|
118
|
+
' ' * size + str
|
119
|
+
end
|
120
|
+
|
121
|
+
def pad_mark(str)
|
122
|
+
pad("%#{MARK_SIZE}s" % str, TEST_PADDING)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module MiniTest
|
128
|
+
class RunnerWithReporter < Unit
|
129
|
+
def initialize(new_reporter)
|
130
|
+
super()
|
131
|
+
@reporter = new_reporter
|
132
|
+
@reporter.runner = self
|
133
|
+
end
|
134
|
+
|
135
|
+
def puke(suite, method, e)
|
136
|
+
case e
|
137
|
+
when MiniTest::Skip then
|
138
|
+
@skips += 1
|
139
|
+
[:skip, e]
|
140
|
+
when MiniTest::Assertion then
|
141
|
+
@failures += 1
|
142
|
+
[:failure, e]
|
143
|
+
else
|
144
|
+
@errors += 1
|
145
|
+
[:error, e]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def _run_anything(type)
|
150
|
+
@test_count = @assertion_count = 0
|
151
|
+
suites = TestCase.send("#{type}_suites")
|
152
|
+
return if suites.empty?
|
153
|
+
|
154
|
+
@reporter.before_suites(suites)
|
155
|
+
|
156
|
+
sync = output.respond_to?(:'sync=') # stupid emacs
|
157
|
+
old_sync, output.sync = output.sync, true if sync
|
158
|
+
_run_suites(suites, type)
|
159
|
+
output.sync = old_sync if sync
|
160
|
+
|
161
|
+
@reporter.after_suites(suites)
|
162
|
+
end
|
163
|
+
|
164
|
+
def _run_suites(suites, type)
|
165
|
+
suites.map { |suite| _run_suite(suite, type) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def _run_suite(suite, type)
|
169
|
+
run_suite_header(suite, type)
|
170
|
+
|
171
|
+
filter = options[:filter] || '/./'
|
172
|
+
filter = Regexp.new($1) if filter =~ /\/(.*)\//
|
173
|
+
|
174
|
+
tests = suite.send("#{type}_methods").grep(filter)
|
175
|
+
|
176
|
+
unless tests.empty?
|
177
|
+
@reporter.before_suite(suite)
|
178
|
+
run_suite_tests(suite, tests)
|
179
|
+
@reporter.after_suite(suite)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def run_suite_header(suite, type)
|
186
|
+
header_method = "#{type}_suite_header"
|
187
|
+
send(header_method, suite) if respond_to?(header_method)
|
188
|
+
end
|
189
|
+
|
190
|
+
def run_suite_tests(suite, tests)
|
191
|
+
suite.startup if suite.respond_to?(:startup)
|
192
|
+
|
193
|
+
tests.each do |test|
|
194
|
+
@reporter.before_test(suite, test)
|
195
|
+
response, e = run_suite_test(suite, test)
|
196
|
+
|
197
|
+
case response
|
198
|
+
when :pass then @reporter.pass(suite, test)
|
199
|
+
when :skip then @reporter.skip(suite, test, e)
|
200
|
+
when :failure then @reporter.failure(suite, test, e)
|
201
|
+
else @reporter.error(suite, test, e)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
ensure
|
205
|
+
suite.shutdown if suite.respond_to?(:shutdown)
|
206
|
+
end
|
207
|
+
|
208
|
+
def run_suite_test(suite, test)
|
209
|
+
suite_instance = suite.new(test)
|
210
|
+
suite_instance._assertions = 0
|
211
|
+
|
212
|
+
result = suite_instance.run(self)
|
213
|
+
|
214
|
+
@test_count += 1
|
215
|
+
@assertion_count += suite_instance._assertions
|
216
|
+
|
217
|
+
result == '.' ? :pass : result
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
MiniTest::Unit.runner = MiniTest::RunnerWithReporter.new(MiniTest::SpecReporter.new)
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ZeppelinTest < Zeppelin::TestCase
|
4
|
+
def setup
|
5
|
+
@client = Zeppelin.new('app key', 'app master secret')
|
6
|
+
@device_token = '1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF'
|
7
|
+
end
|
8
|
+
|
9
|
+
test '#connection' do
|
10
|
+
assert_instance_of Faraday::Connection, @client.connection
|
11
|
+
assert_equal 'https', @client.connection.scheme
|
12
|
+
assert_equal 'go.urbanairship.com', @client.connection.host
|
13
|
+
assert_includes @client.connection.builder.handlers, Faraday::Adapter::NetHttp
|
14
|
+
assert_includes @client.connection.builder.handlers, Faraday::Request::JSON
|
15
|
+
assert_equal 'Basic YXBwIGtleTphcHAgbWFzdGVyIHNlY3JldA==', @client.connection.headers['Authorization']
|
16
|
+
end
|
17
|
+
|
18
|
+
test '#register_device_token without a payload' do
|
19
|
+
stub_requests @client.connection do |stub|
|
20
|
+
stub.put("/api/device_tokens/#{@device_token}") do [201, {}, '']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
response = @client.register_device_token(@device_token)
|
25
|
+
assert response
|
26
|
+
end
|
27
|
+
|
28
|
+
test '#register_device_token an already registered device token' do
|
29
|
+
stub_requests @client.connection do |stub|
|
30
|
+
stub.put("/api/device_tokens/#{@device_token}") do
|
31
|
+
[200, {}, '']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
response = @client.register_device_token(@device_token)
|
36
|
+
assert response
|
37
|
+
end
|
38
|
+
|
39
|
+
test '#register_device_token with payload' do
|
40
|
+
payload = { :alias => 'CapnKernul' }
|
41
|
+
|
42
|
+
stub_requests @client.connection do |stub|
|
43
|
+
stub.put("/api/device_tokens/#{@device_token}", Yajl::Encoder.encode(payload)) do
|
44
|
+
[200, {}, '']
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
response = @client.register_device_token(@device_token, payload)
|
49
|
+
assert response
|
50
|
+
end
|
51
|
+
|
52
|
+
test '#register_device_token with an error' do
|
53
|
+
stub_requests @client.connection do |stub|
|
54
|
+
stub.put("/api/device_tokens/#{@device_token}", nil) do
|
55
|
+
[500, {}, '']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
response = @client.register_device_token(@device_token)
|
60
|
+
refute response
|
61
|
+
end
|
62
|
+
|
63
|
+
test '#device_token with valid device token' do
|
64
|
+
response_body = { 'foo' => 'bar' }
|
65
|
+
stub_requests @client.connection do |stub|
|
66
|
+
stub.get("/api/device_tokens/#{@device_token}") do
|
67
|
+
[200, {}, Yajl::Encoder.encode(response_body)]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
response = @client.device_token(@device_token)
|
72
|
+
assert_equal response_body, response
|
73
|
+
end
|
74
|
+
|
75
|
+
test '#device_token with an unknown device token' do
|
76
|
+
stub_requests @client.connection do |stub|
|
77
|
+
stub.get("/api/device_tokens/#{@device_token}") do
|
78
|
+
[404, {}, '']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
response = @client.device_token(@device_token)
|
83
|
+
assert_nil response
|
84
|
+
end
|
85
|
+
|
86
|
+
test '#delete_device_token with a valid device token' do
|
87
|
+
stub_requests @client.connection do |stub|
|
88
|
+
stub.delete("/api/device_tokens/#{@device_token}") do
|
89
|
+
[204, {}, '']
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
response = @client.delete_device_token(@device_token)
|
94
|
+
assert response
|
95
|
+
end
|
96
|
+
|
97
|
+
test '#delete_device_token with an unknown device token' do
|
98
|
+
stub_requests @client.connection do |stub|
|
99
|
+
stub.delete("/api/device_tokens/#{@device_token}") do
|
100
|
+
[404, {}, '']
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
response = @client.delete_device_token(@device_token)
|
105
|
+
refute response
|
106
|
+
end
|
107
|
+
|
108
|
+
test '#push with a valid payload' do
|
109
|
+
payload = {
|
110
|
+
:device_tokens => [@device_token],
|
111
|
+
:aps => { :alert => 'Hello from Urban Airship!' }
|
112
|
+
}
|
113
|
+
|
114
|
+
stub_requests @client.connection do |stub|
|
115
|
+
stub.post('/api/push/', Yajl::Encoder.encode(payload)) do
|
116
|
+
[200, {}, '']
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
response = @client.push(payload)
|
121
|
+
assert response
|
122
|
+
end
|
123
|
+
|
124
|
+
# Although the Urban Airship documentation states that a 400 Status Code
|
125
|
+
# will be sent when there's an invalid payload, it doesn't state what
|
126
|
+
# constitutes an invalid payload. Hence, I'm just mocking out the request so
|
127
|
+
# that it returns a 400.
|
128
|
+
test '#push with an invalid payload' do
|
129
|
+
stub_requests @client.connection do |stub|
|
130
|
+
stub.post('/api/push/', '{}') do
|
131
|
+
[400, {}, '']
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
response = @client.push({})
|
136
|
+
refute response
|
137
|
+
end
|
138
|
+
|
139
|
+
test '#batch_push with a valid payload' do
|
140
|
+
message1 = {
|
141
|
+
:device_tokens => [@device_token],
|
142
|
+
:aps => { :alert => 'Hello from Urban Airship!' }
|
143
|
+
}
|
144
|
+
|
145
|
+
message2 = {
|
146
|
+
:device_tokens => [],
|
147
|
+
:aps => { :alert => 'Yet another hello from Urban Airship!' }
|
148
|
+
}
|
149
|
+
|
150
|
+
payload = [message1, message2]
|
151
|
+
|
152
|
+
stub_requests @client.connection do |stub|
|
153
|
+
stub.post('/api/push/batch/', Yajl::Encoder.encode(payload)) do
|
154
|
+
[200, {}, '']
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
response = @client.batch_push(message1, message2)
|
159
|
+
assert response
|
160
|
+
end
|
161
|
+
|
162
|
+
# See the note above for why this test exists.
|
163
|
+
test '#batch_push with an invalid payload' do
|
164
|
+
stub_requests @client.connection do |stub|
|
165
|
+
stub.post('/api/push/batch/', '[{},{}]') do
|
166
|
+
[400, {}, '']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
response = @client.batch_push({}, {})
|
171
|
+
refute response
|
172
|
+
end
|
173
|
+
|
174
|
+
test '#broadcast with a valid payload' do
|
175
|
+
payload = {
|
176
|
+
:aps => { :alert => 'Hello from Urban Airship!' }
|
177
|
+
}
|
178
|
+
|
179
|
+
stub_requests @client.connection do |stub|
|
180
|
+
stub.post('/api/push/broadcast/', Yajl::Encoder.encode(payload)) do
|
181
|
+
[200, {}, '']
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
response = @client.broadcast(payload)
|
186
|
+
assert response
|
187
|
+
end
|
188
|
+
|
189
|
+
# See the note above for why this test exists.
|
190
|
+
test '#broadcast with an invalid payload' do
|
191
|
+
stub_requests @client.connection do |stub|
|
192
|
+
stub.post('/api/push/broadcast/', '{}') do
|
193
|
+
[400, {}, '']
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
response = @client.broadcast({})
|
198
|
+
refute response
|
199
|
+
end
|
200
|
+
|
201
|
+
test '#feedback with a valid since' do
|
202
|
+
response_body = { 'foo' => 'bar' }
|
203
|
+
since = Time.at(0)
|
204
|
+
|
205
|
+
stub_requests @client.connection do |stub|
|
206
|
+
stub.get('/api/device_tokens/feedback/?since=1970-01-01T00%3A00%3A00Z') do
|
207
|
+
[200, {}, Yajl::Encoder.encode(response_body)]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
response = @client.feedback(since)
|
212
|
+
assert_equal response_body, response
|
213
|
+
end
|
214
|
+
|
215
|
+
test '#feedback with an error' do
|
216
|
+
since = Time.at(0)
|
217
|
+
|
218
|
+
stub_requests @client.connection do |stub|
|
219
|
+
stub.get('/api/device_tokens/feedback/?since=1970-01-01T00%3A00%3A00Z') do
|
220
|
+
[400, {}, '']
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
response = @client.feedback(since)
|
225
|
+
assert_nil response
|
226
|
+
end
|
227
|
+
|
228
|
+
def stub_requests(connection, &block)
|
229
|
+
connection.builder.handlers.delete(Faraday::Adapter::NetHttp)
|
230
|
+
connection.adapter(:test, &block)
|
231
|
+
end
|
232
|
+
end
|
data/zeppelin.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'zeppelin/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'zeppelin'
|
7
|
+
s.version = Zeppelin::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Alexander Kern']
|
10
|
+
s.email = ['alex@kernul.com']
|
11
|
+
s.homepage = 'https://github.com/CapnKernul/zeppelin'
|
12
|
+
s.summary = %q{Urban Airship library for Ruby}
|
13
|
+
s.description = %q{Ruby client for the Urban Airship Push Notification API}
|
14
|
+
|
15
|
+
s.rubyforge_project = 'zeppelin'
|
16
|
+
|
17
|
+
s.add_dependency 'faraday'
|
18
|
+
s.add_dependency 'yajl-ruby'
|
19
|
+
|
20
|
+
s.add_development_dependency 'ansi'
|
21
|
+
s.add_development_dependency 'minitest', '~> 2.0'
|
22
|
+
s.add_development_dependency 'mocha'
|
23
|
+
s.add_development_dependency 'test_declarative'
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.require_paths = ['lib']
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zeppelin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alexander Kern
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-15 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: faraday
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: yajl-ruby
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: ansi
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: minitest
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "2.0"
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: mocha
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: test_declarative
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
82
|
+
description: Ruby client for the Urban Airship Push Notification API
|
83
|
+
email:
|
84
|
+
- alex@kernul.com
|
85
|
+
executables: []
|
86
|
+
|
87
|
+
extensions: []
|
88
|
+
|
89
|
+
extra_rdoc_files: []
|
90
|
+
|
91
|
+
files:
|
92
|
+
- .autotest
|
93
|
+
- .gitignore
|
94
|
+
- .travis.yml
|
95
|
+
- .yardopts
|
96
|
+
- Gemfile
|
97
|
+
- LICENSE
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- lib/zeppelin.rb
|
101
|
+
- lib/zeppelin/version.rb
|
102
|
+
- test/support/spec_reporter.rb
|
103
|
+
- test/test_helper.rb
|
104
|
+
- test/zeppelin_test.rb
|
105
|
+
- zeppelin.gemspec
|
106
|
+
has_rdoc: true
|
107
|
+
homepage: https://github.com/CapnKernul/zeppelin
|
108
|
+
licenses: []
|
109
|
+
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: "0"
|
127
|
+
requirements: []
|
128
|
+
|
129
|
+
rubyforge_project: zeppelin
|
130
|
+
rubygems_version: 1.6.2
|
131
|
+
signing_key:
|
132
|
+
specification_version: 3
|
133
|
+
summary: Urban Airship library for Ruby
|
134
|
+
test_files:
|
135
|
+
- test/support/spec_reporter.rb
|
136
|
+
- test/test_helper.rb
|
137
|
+
- test/zeppelin_test.rb
|