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 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
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ # Gem-specific
21
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ script: 'rake test'
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - ree
6
+ - jruby
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --markup-provider maruku
4
+ --default-return ""
5
+ --title "Zeppelin Documentation"
6
+ --hide-void-return
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
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 [![StillMaintained Status](http://stillmaintained.com/CapnKernul/zeppelin.png)](http://stillmaintained.com/CapnKernul/zeppelin) [![Build Status](http://travis-ci.org/CapnKernul/zeppelin.png)](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
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ task :default => 'test'
5
+
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new do |t|
8
+ t.ruby_opts += ['-rubygems']
9
+ t.libs << 'test'
10
+ t.pattern = 'test/**/*_test.rb'
11
+ end
@@ -0,0 +1,3 @@
1
+ class Zeppelin
2
+ VERSION = '0.1.0'
3
+ end
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)
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'mocha'
4
+ require 'test_declarative'
5
+ require 'zeppelin'
6
+
7
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
8
+
9
+ class Zeppelin::TestCase < MiniTest::Unit::TestCase; end
@@ -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