zeppelin 0.4.0 → 0.5.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.
@@ -1,9 +1,8 @@
1
1
  require 'faraday'
2
- require 'yajl'
3
2
  require 'time'
4
3
 
5
4
  # A very tiny Urban Airship Push Notification API client.
6
- #
5
+ #
7
6
  # Provides thin wrappers around API calls to the most common API tasks. For more
8
7
  # information on how the requests and responses are formatted, visit the [Urban
9
8
  # Airship Push Notification API docs](http://urbanairship.com/docs/push.html).
@@ -12,140 +11,138 @@ class Zeppelin
12
11
  PUSH_URI = '/api/push/'
13
12
  BATCH_PUSH_URI = '/api/push/batch/'
14
13
  BROADCAST_URI = '/api/push/broadcast/'
15
- SUCCESSFUL_STATUS_CODES = (200..299)
16
14
  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
- #
15
+
23
16
  # @param [String] application_key your Urban Airship Application Key
17
+ #
24
18
  # @param [String] application_master_secret your Urban Airship Application
25
19
  # Master Secret
26
20
  def initialize(application_key, application_master_secret, options = {})
27
- @connection = Faraday::Connection.new(BASE_URI, options) do |builder|
28
- builder.request :json
29
- builder.adapter :net_http
30
- end
31
-
32
- @connection.basic_auth(application_key, application_master_secret)
21
+ @application_key = application_key
22
+ @application_master_secret = application_master_secret
23
+ @options = options
24
+ end
25
+
26
+ # The connection to UrbanAirship
27
+ def connection
28
+ return @connection unless @connection.nil?
29
+ @connection = initialize_connection
33
30
  end
34
-
31
+
35
32
  # Registers a device token.
36
- #
33
+ #
37
34
  # @param [String] device_token
38
35
  # @param [Hash] payload the payload to send during registration
39
36
  # @return [Boolean] whether or not the registration was successful
40
37
  def register_device_token(device_token, payload = {})
41
38
  uri = device_token_uri(device_token)
42
-
39
+
43
40
  if payload.empty?
44
- response = @connection.put(uri)
41
+ response = connection.put(uri)
45
42
  else
46
- response = @connection.put(uri, payload, JSON_HEADERS)
43
+ response = connection.put(uri, payload, JSON_HEADERS)
47
44
  end
48
-
49
- successful?(response)
45
+
46
+ response.success?
50
47
  end
51
-
48
+
52
49
  # Retrieves information on a device token.
53
- #
50
+ #
54
51
  # @param [String] device_token
55
52
  # @return [Hash, nil]
56
53
  def device_token(device_token)
57
- response = @connection.get(device_token_uri(device_token))
58
- successful?(response) ? parse(response.body) : nil
54
+ response = connection.get(device_token_uri(device_token))
55
+ response.success? ? response.body : nil
59
56
  end
60
-
57
+
61
58
  # Deletes a device token.
62
- #
59
+ #
63
60
  # @param [String] device_token
64
61
  # @return [Boolean] whether or not the deletion was successful
65
62
  def delete_device_token(device_token)
66
- response = @connection.delete(device_token_uri(device_token))
67
- successful?(response)
63
+ response = connection.delete(device_token_uri(device_token))
64
+ response.success?
68
65
  end
69
-
66
+
70
67
  # Registers an APID.
71
- #
68
+ #
72
69
  # @param [String] apid
73
70
  # @param [Hash] payload the payload to send during registration
74
71
  # @return [Boolean] whether or not the registration was successful
75
72
  def register_apid(apid, payload = {})
76
73
  uri = apid_uri(apid)
77
-
74
+
78
75
  if payload.empty?
79
- response = @connection.put(uri)
76
+ response = connection.put(uri)
80
77
  else
81
- response = @connection.put(uri, payload, JSON_HEADERS)
78
+ response = connection.put(uri, payload, JSON_HEADERS)
82
79
  end
83
-
84
- successful?(response)
80
+
81
+ response.success?
85
82
  end
86
-
83
+
87
84
  # Retrieves information on an APID.
88
- #
85
+ #
89
86
  # @param [String] apid
90
87
  # @return [Hash, nil]
91
88
  def apid(apid)
92
- response = @connection.get(apid_uri(apid))
93
- successful?(response) ? parse(response.body) : nil
89
+ response = connection.get(apid_uri(apid))
90
+ response.success? ? response.body : nil
94
91
  end
95
-
92
+
96
93
  # Deletes an APID.
97
- #
94
+ #
98
95
  # @param [String] apid
99
96
  # @return [Boolean] whether or not the deletion was successful
100
97
  def delete_apid(apid)
101
- response = @connection.delete(apid_uri(apid))
102
- successful?(response)
98
+ response = connection.delete(apid_uri(apid))
99
+ response.success?
103
100
  end
104
-
101
+
105
102
  # Pushes a message.
106
- #
103
+ #
107
104
  # @param [Hash] payload the payload of the message
108
105
  # @return [Boolean] whether or not pushing the message was successful
109
106
  def push(payload)
110
- response = @connection.post(PUSH_URI, payload, JSON_HEADERS)
111
- successful?(response)
107
+ response = connection.post(PUSH_URI, payload, JSON_HEADERS)
108
+ response.success?
112
109
  end
113
-
110
+
114
111
  # Batch pushes multiple messages.
115
- #
112
+ #
116
113
  # @param [<Hash>] payload the payloads of each message
117
114
  # @return [Boolean] whether or not pushing the messages was successful
118
115
  def batch_push(*payload)
119
- response = @connection.post(BATCH_PUSH_URI, payload, JSON_HEADERS)
120
- successful?(response)
116
+ response = connection.post(BATCH_PUSH_URI, payload, JSON_HEADERS)
117
+ response.success?
121
118
  end
122
-
119
+
123
120
  # Broadcasts a message.
124
- #
121
+ #
125
122
  # @param [Hash] payload the payload of the message
126
123
  # @return [Boolean] whether or not broadcasting the message was successful
127
124
  def broadcast(payload)
128
- response = @connection.post(BROADCAST_URI, payload, JSON_HEADERS)
129
- successful?(response)
125
+ response = connection.post(BROADCAST_URI, payload, JSON_HEADERS)
126
+ response.success?
130
127
  end
131
-
128
+
132
129
  # Retrieves feedback on device tokens.
133
- #
130
+ #
134
131
  # This is useful for removing inactive device tokens for the database.
135
- #
132
+ #
136
133
  # @param [Time] since the time to retrieve inactive tokens from
137
134
  # @return [Hash, nil]
138
135
  def feedback(since)
139
- response = @connection.get(feedback_uri(since))
140
- successful?(response) ? parse(response.body) : nil
136
+ response = connection.get(feedback_uri(since))
137
+ response.success? ? response.body : nil
141
138
  end
142
139
 
143
140
  # Retrieve all tags on the service
144
141
  #
145
142
  # @return [Hash, nil]
146
143
  def tags
147
- response = @connection.get(tag_uri(nil))
148
- successful?(response) ? parse(response.body) : nil
144
+ response = connection.get(tag_uri(nil))
145
+ response.success? ? response.body : nil
149
146
  end
150
147
 
151
148
  # Modifies device tokens associated with a tag.
@@ -156,7 +153,7 @@ class Zeppelin
156
153
  #
157
154
  # @see http://urbanairship.com/docs/tags.html#modifying-device-tokens-on-a-tag
158
155
  def modify_device_tokens_on_tag(tag_name, payload = {})
159
- @connection.post(tag_uri(tag_name), payload, JSON_HEADERS)
156
+ connection.post(tag_uri(tag_name), payload, JSON_HEADERS)
160
157
  end
161
158
 
162
159
  # Creates a tag that is not associated with any device
@@ -165,8 +162,8 @@ class Zeppelin
165
162
  #
166
163
  # @return [Boolean] whether or not the request was successful
167
164
  def add_tag(name)
168
- response = @connection.put(tag_uri(name))
169
- successful?(response)
165
+ response = connection.put(tag_uri(name))
166
+ response.success?
170
167
  end
171
168
 
172
169
  # Removes a tag from the service
@@ -176,16 +173,16 @@ class Zeppelin
176
173
  # @return [Boolean] true when the request was successful. Note that this
177
174
  # method will return false if the tag has already been removed.
178
175
  def remove_tag(name)
179
- response = @connection.delete(tag_uri(name))
180
- successful?(response)
176
+ response = connection.delete(tag_uri(name))
177
+ response.success?
181
178
  end
182
179
 
183
180
  # @param [String] device_token
184
181
  #
185
182
  # @return [Hash, nil]
186
183
  def device_tags(device_token)
187
- response = @connection.get(device_tag_uri(device_token, nil))
188
- successful?(response) ? parse(response.body) : nil
184
+ response = connection.get(device_tag_uri(device_token, nil))
185
+ response.success? ? response.body : nil
189
186
  end
190
187
 
191
188
  # @param [String] device_token
@@ -195,8 +192,8 @@ class Zeppelin
195
192
  # @return [Boolean] whether or not a tag was successfully associated with
196
193
  # a device
197
194
  def add_tag_to_device(device_token, tag_name)
198
- response = @connection.put(device_tag_uri(device_token, tag_name))
199
- successful?(response)
195
+ response = connection.put(device_tag_uri(device_token, tag_name))
196
+ response.success?
200
197
  end
201
198
 
202
199
  # @param [String] device_token
@@ -206,20 +203,34 @@ class Zeppelin
206
203
  # @return [Boolean] whether or not a tag was successfully dissociated from
207
204
  # a device
208
205
  def remove_tag_from_device(device_token, tag_name)
209
- response = @connection.delete(device_tag_uri(device_token, tag_name))
210
- successful?(response)
206
+ response = connection.delete(device_tag_uri(device_token, tag_name))
207
+ response.success?
211
208
  end
212
-
209
+
213
210
  private
214
-
211
+
212
+ def initialize_connection
213
+ conn = Faraday::Connection.new(BASE_URI, @options) do |builder|
214
+ builder.request :json
215
+
216
+ builder.use Zeppelin::JsonParserMiddleware
217
+
218
+ builder.adapter :net_http
219
+ end
220
+
221
+ conn.basic_auth(@application_key, @application_master_secret)
222
+
223
+ conn
224
+ end
225
+
215
226
  def device_token_uri(device_token)
216
227
  "/api/device_tokens/#{device_token}"
217
228
  end
218
-
229
+
219
230
  def apid_uri(apid)
220
231
  "/api/apids/#{apid}"
221
232
  end
222
-
233
+
223
234
  def feedback_uri(since)
224
235
  "/api/device_tokens/feedback/?since=#{since.utc.iso8601}"
225
236
  end
@@ -231,14 +242,7 @@ class Zeppelin
231
242
  def device_tag_uri(device_token, tag_name)
232
243
  device_token_uri(device_token) + "/tags/#{tag_name}"
233
244
  end
234
-
235
- def successful?(response)
236
- SUCCESSFUL_STATUS_CODES.include?(response.status)
237
- end
238
-
239
- def parse(json)
240
- Yajl::Parser.parse(json)
241
- end
242
245
  end
243
246
 
247
+ require 'zeppelin/json_parser_middleware'
244
248
  require 'zeppelin/version'
@@ -0,0 +1,35 @@
1
+ require 'yajl'
2
+
3
+ class Zeppelin
4
+ # Middleware for Faraday that parses JSON response bodies. Based on code in
5
+ # the FaradayMiddleware project.
6
+ #
7
+ # @private
8
+ class JsonParserMiddleware < Faraday::Middleware
9
+ CONTENT_TYPE = 'Content-Type'
10
+
11
+ def initialize(app=nil)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ @app.call(env).on_complete do
17
+ parse_response(env) if process_content_type?(env) && parse_response?(env)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def parse_response(env)
24
+ env[:body] = Yajl::Parser.parse(env[:body])
25
+ end
26
+
27
+ def process_content_type?(env)
28
+ env[:response_headers][CONTENT_TYPE].to_s =~ /\bjson$/
29
+ end
30
+
31
+ def parse_response?(env)
32
+ env[:body].respond_to? :to_str
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  class Zeppelin
2
- VERSION = '0.4.0'
3
- end
2
+ VERSION = '0.5.0'
3
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ class JsonParserMiddlewareTest < Zeppelin::TestCase
4
+ def setup
5
+ @json_body = "{\"foo\":\"bar\"}"
6
+ @expected_parsed_body = { 'foo' => 'bar' }
7
+ end
8
+
9
+ test 'parses a standard JSON content type' do
10
+ assert_equal @expected_parsed_body, process(@json_body, 'application/json').body
11
+ end
12
+
13
+ test 'parses vendor JSON content type' do
14
+ assert_equal @expected_parsed_body, process(@json_body, 'application/vnd.urbanairship+json').body
15
+ end
16
+
17
+ test 'does not change nil body' do
18
+ assert process(nil).body.nil?
19
+ end
20
+
21
+ test 'does not parse non-JSON content types' do
22
+ assert_equal '<hello>world</hello>', process('<hello>world</hello>', 'text/xml').body
23
+ end
24
+
25
+ def process(body, content_type=nil, options={})
26
+ env = { :body => body, :response_headers => Faraday::Utils::Headers.new }
27
+ env[:response_headers]['content-type'] = content_type if content_type
28
+
29
+ middleware = Zeppelin::JsonParserMiddleware.new(
30
+ lambda { |env| Faraday::Response.new(env) }
31
+ )
32
+
33
+ middleware.call(env)
34
+ end
35
+ end
@@ -12,6 +12,7 @@ class ZeppelinTest < Zeppelin::TestCase
12
12
  assert_equal 'go.urbanairship.com', @client.connection.host
13
13
  assert_includes @client.connection.builder.handlers, Faraday::Adapter::NetHttp
14
14
  assert_includes @client.connection.builder.handlers, Faraday::Request::JSON
15
+ assert_includes @client.connection.builder.handlers, Zeppelin::JsonParserMiddleware
15
16
  assert_equal 'Basic YXBwIGtleTphcHAgbWFzdGVyIHNlY3JldA==', @client.connection.headers['Authorization']
16
17
  end
17
18
 
@@ -70,7 +71,7 @@ class ZeppelinTest < Zeppelin::TestCase
70
71
  response_body = { 'foo' => 'bar' }
71
72
  stub_requests @client.connection do |stub|
72
73
  stub.get("/api/device_tokens/#{@device_token}") do
73
- [200, {}, Yajl::Encoder.encode(response_body)]
74
+ [200, { 'Content-Type' => 'application/json' }, Yajl::Encoder.encode(response_body)]
74
75
  end
75
76
  end
76
77
 
@@ -160,7 +161,7 @@ class ZeppelinTest < Zeppelin::TestCase
160
161
  response_body = { 'foo' => 'bar' }
161
162
  stub_requests @client.connection do |stub|
162
163
  stub.get("/api/apids/#{@apid}") do
163
- [200, {}, Yajl::Encoder.encode(response_body)]
164
+ [200, { 'Content-Type' => 'application/json' }, Yajl::Encoder.encode(response_body)]
164
165
  end
165
166
  end
166
167
 
@@ -289,7 +290,7 @@ class ZeppelinTest < Zeppelin::TestCase
289
290
 
290
291
  stub_requests @client.connection do |stub|
291
292
  stub.get('/api/device_tokens/feedback/?since=1970-01-01T00%3A00%3A00Z') do
292
- [200, {}, Yajl::Encoder.encode(response_body)]
293
+ [200, { 'Content-Type' => 'application/json' }, Yajl::Encoder.encode(response_body)]
293
294
  end
294
295
  end
295
296
 
@@ -315,7 +316,7 @@ class ZeppelinTest < Zeppelin::TestCase
315
316
 
316
317
  stub_requests @client.connection do |stub|
317
318
  stub.get('/api/tags/') do
318
- [200, {}, Yajl::Encoder.encode(response_body)]
319
+ [200, { 'Content-Type' => 'application/json' }, Yajl::Encoder.encode(response_body)]
319
320
  end
320
321
  end
321
322
 
@@ -382,7 +383,7 @@ class ZeppelinTest < Zeppelin::TestCase
382
383
 
383
384
  stub_requests @client.connection do |stub|
384
385
  stub.get("/api/device_tokens/#{device_token}/tags/") do
385
- [200, {}, Yajl::Encoder.encode(response_body)]
386
+ [200, { 'Content-Type' => 'application/json' }, Yajl::Encoder.encode(response_body)]
386
387
  end
387
388
  end
388
389
 
@@ -11,19 +11,20 @@ Gem::Specification.new do |s|
11
11
  s.homepage = 'https://github.com/CapnKernul/zeppelin'
12
12
  s.summary = %q{Urban Airship library for Ruby}
13
13
  s.description = %q{Ruby client for the Urban Airship Push Notification API}
14
-
14
+
15
15
  s.rubyforge_project = 'zeppelin'
16
-
16
+
17
17
  s.add_dependency 'faraday'
18
18
  s.add_dependency 'yajl-ruby'
19
-
19
+
20
20
  s.add_development_dependency 'minitest', '~> 2.0'
21
21
  s.add_development_dependency 'journo'
22
22
  s.add_development_dependency 'mocha'
23
23
  s.add_development_dependency 'test_declarative'
24
-
24
+ s.add_development_dependency 'rake'
25
+
25
26
  s.files = `git ls-files`.split("\n")
26
27
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
28
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
29
  s.require_paths = ['lib']
29
- end
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zeppelin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-19 00:00:00.000000000Z
12
+ date: 2012-02-23 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
- requirement: &2165051200 !ruby/object:Gem::Requirement
16
+ requirement: &70115497098880 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2165051200
24
+ version_requirements: *70115497098880
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yajl-ruby
27
- requirement: &2165050780 !ruby/object:Gem::Requirement
27
+ requirement: &70115497097860 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2165050780
35
+ version_requirements: *70115497097860
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: minitest
38
- requirement: &2165050280 !ruby/object:Gem::Requirement
38
+ requirement: &70115497095520 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '2.0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2165050280
46
+ version_requirements: *70115497095520
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: journo
49
- requirement: &2165049860 !ruby/object:Gem::Requirement
49
+ requirement: &70115497094220 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2165049860
57
+ version_requirements: *70115497094220
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: mocha
60
- requirement: &2165049400 !ruby/object:Gem::Requirement
60
+ requirement: &70115497092480 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2165049400
68
+ version_requirements: *70115497092480
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: test_declarative
71
- requirement: &2165048980 !ruby/object:Gem::Requirement
71
+ requirement: &70115497090680 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,18 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2165048980
79
+ version_requirements: *70115497090680
80
+ - !ruby/object:Gem::Dependency
81
+ name: rake
82
+ requirement: &70115497089600 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70115497089600
80
91
  description: Ruby client for the Urban Airship Push Notification API
81
92
  email:
82
93
  - alex@kernul.com
@@ -93,7 +104,9 @@ files:
93
104
  - README.md
94
105
  - Rakefile
95
106
  - lib/zeppelin.rb
107
+ - lib/zeppelin/json_parser_middleware.rb
96
108
  - lib/zeppelin/version.rb
109
+ - test/json_parser_middleware_test.rb
97
110
  - test/test_helper.rb
98
111
  - test/zeppelin_test.rb
99
112
  - zeppelin.gemspec
@@ -109,18 +122,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
122
  - - ! '>='
110
123
  - !ruby/object:Gem::Version
111
124
  version: '0'
125
+ segments:
126
+ - 0
127
+ hash: 437314845617063875
112
128
  required_rubygems_version: !ruby/object:Gem::Requirement
113
129
  none: false
114
130
  requirements:
115
131
  - - ! '>='
116
132
  - !ruby/object:Gem::Version
117
133
  version: '0'
134
+ segments:
135
+ - 0
136
+ hash: 437314845617063875
118
137
  requirements: []
119
138
  rubyforge_project: zeppelin
120
- rubygems_version: 1.8.6
139
+ rubygems_version: 1.8.10
121
140
  signing_key:
122
141
  specification_version: 3
123
142
  summary: Urban Airship library for Ruby
124
143
  test_files:
144
+ - test/json_parser_middleware_test.rb
125
145
  - test/test_helper.rb
126
146
  - test/zeppelin_test.rb