zeppelin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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