grocer 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d758bbd299d221fe50d6a3b9d736dce0ae5016fb
4
+ data.tar.gz: f29ab31ea4fd92da51b624c84172578354e96246
5
+ SHA512:
6
+ metadata.gz: 8fea57524b210a9c547f6efcb17f775210cc591e1d04a904828fa9ac4d388aa0d4b31bc2a74f8b025b9d48f29618ec59f6b0deb5ee3ee2a789d1b4e47c68dece
7
+ data.tar.gz: 642f3a3612148d4a721f4bcc554aa9168e89eb0adb5ffbd4a6b9fc35e14187c1143306577a49b223e642caa6be9d1833bbc1bfd355463d97dbc06cbf22ca06ee
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ - 2.0.0
5
6
  - jruby-19mode # JRuby 1.7.0
6
7
  - rbx-19mode
7
8
  # - ruby-head # seems unstable on travis at this time
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.4.0
6
+
7
+ * Add support for `content-available` for background fetches (iOS 7) for all
8
+ types of Notifications ([Zack Parker](https://github.com/somezack))
9
+
3
10
  ## 0.3.4
4
11
 
5
12
  * Add `Grocer::MobileDeviceManagementNotification` to send PushMagic tokens.
data/README.md CHANGED
@@ -69,12 +69,13 @@ pusher = Grocer.pusher(
69
69
  #
70
70
  # Information on obtaining `device_token` is shown later.
71
71
  notification = Grocer::Notification.new(
72
- device_token: "fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2",
73
- alert: "Hello from Grocer!",
74
- badge: 42,
75
- sound: "siren.aiff", # optional
76
- expiry: Time.now + 60*60, # optional; 0 is default, meaning the message is not stored
77
- identifier: 1234 # optional
72
+ device_token: "fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2",
73
+ alert: "Hello from Grocer!",
74
+ badge: 42,
75
+ sound: "siren.aiff", # optional
76
+ expiry: Time.now + 60*60, # optional; 0 is default, meaning the message is not stored
77
+ identifier: 1234, # optional
78
+ content_available: true # optional; any truthy value will set 'content-available' to 1
78
79
  )
79
80
 
80
81
  pusher.push(notification)
@@ -143,7 +144,7 @@ feedback = Grocer.feedback(
143
144
  certificate: "/path/to/cert.pem", # required
144
145
  passphrase: "", # optional
145
146
  gateway: "feedback.push.apple.com", # optional; See note below.
146
- port: 2196 # optional
147
+ port: 2196, # optional
147
148
  retries: 3 # optional
148
149
  )
149
150
 
@@ -252,3 +253,11 @@ openssl pkcs12 -in exported_certificate.p12 -out certificate.pem -nodes -clcerts
252
253
  ```
253
254
 
254
255
  The `certificate.pem` file that is generated can be used with **grocer**.
256
+
257
+ ## Support Channels
258
+
259
+ [GitHub Issues](https://github.com/grocer/grocer/issues) and [Pull
260
+ Requests](https://github.com/grocer/grocer/pulls) are the primary venues for
261
+ communicating issues and discussing possible features. Several of us also
262
+ regularly hang out in the `#grocer` channel on Freenode; feel free to pop in
263
+ and ask questions there as well. Thanks! :heart:
@@ -0,0 +1,33 @@
1
+ module Grocer
2
+ class ErrorResponse
3
+ STATUS_CODE_DESCRIPTIONS = {
4
+ 0 => 'No errors encountered',
5
+ 1 => 'Processing error',
6
+ 2 => 'Missing device token',
7
+ 3 => 'Missing topic',
8
+ 4 => 'Missing payload',
9
+ 5 => 'Invalid token size',
10
+ 6 => 'Invalid topic size',
11
+ 7 => 'Invalid payload size',
12
+ 8 => 'Invalid token',
13
+ 256 => 'None (unknown)',
14
+ }
15
+
16
+ COMMAND = 8
17
+
18
+ attr_accessor :status_code, :identifier
19
+
20
+ def initialize(binary_tuple)
21
+ # C => 1 byte command
22
+ # C => 1 byte status
23
+ # N => 4 byte identifier
24
+ command, @status_code, @identifier = binary_tuple.unpack('CCN')
25
+ raise InvalidFormatError unless @status_code && @identifier
26
+ raise InvalidCommandError unless command == COMMAND
27
+ end
28
+
29
+ def status
30
+ STATUS_CODE_DESCRIPTIONS[status_code]
31
+ end
32
+ end
33
+ end
@@ -11,15 +11,15 @@ module Grocer
11
11
  # #=> { aps: { 'content-available' => 1 } }
12
12
  class NewsstandNotification < Notification
13
13
 
14
+ def initialize(payload = {})
15
+ super(payload.merge(content_available: true))
16
+ end
17
+
14
18
  private
15
19
 
16
20
  def validate_payload
17
21
  true
18
22
  end
19
23
 
20
- def payload_hash
21
- { aps: {'content-available' => 1} }
22
- end
23
-
24
24
  end
25
25
  end
@@ -4,19 +4,21 @@ module Grocer
4
4
  # Public: An object used to send notifications to APNS.
5
5
  class Notification
6
6
  MAX_PAYLOAD_SIZE = 256
7
+ CONTENT_AVAILABLE_INDICATOR = 1
7
8
 
8
- attr_accessor :identifier, :expiry, :device_token, :alert, :badge, :sound,
9
- :custom
9
+ attr_accessor :identifier, :expiry, :device_token
10
+ attr_reader :alert, :badge, :custom, :sound, :content_available
10
11
 
11
12
  # Public: Initialize a new Grocer::Notification. You must specify at least an `alert` or `badge`.
12
13
  #
13
14
  # payload - The Hash of notification parameters and payload to be sent to APNS.:
14
- # :device_token - The String representing to device token sent to APNS.
15
- # :alert - The String or Hash to be sent as the alert portion of the payload. (optional)
16
- # :badge - The Integer to be sent as the badge portion of the payload. (optional)
17
- # :sound - The String representing the sound portion of the payload. (optional)
18
- # :expiry - The Integer representing UNIX epoch date sent to APNS as the notification expiry. (default: 0)
19
- # :identifier - The arbitrary value sent to APNS to uniquely this notification. (default: 0)
15
+ # :device_token - The String representing to device token sent to APNS.
16
+ # :alert - The String or Hash to be sent as the alert portion of the payload. (optional)
17
+ # :badge - The Integer to be sent as the badge portion of the payload. (optional)
18
+ # :sound - The String representing the sound portion of the payload. (optional)
19
+ # :expiry - The Integer representing UNIX epoch date sent to APNS as the notification expiry. (default: 0)
20
+ # :identifier - The arbitrary Integer sent to APNS to uniquely this notification. (default: 0)
21
+ # :content_available - The truthy or falsy value indicating the availability of new content for background fetch. (optional)
20
22
  def initialize(payload = {})
21
23
  @identifier = 0
22
24
 
@@ -27,7 +29,6 @@ module Grocer
27
29
 
28
30
  def to_bytes
29
31
  validate_payload
30
- payload = encoded_payload
31
32
 
32
33
  [
33
34
  1,
@@ -35,24 +36,46 @@ module Grocer
35
36
  expiry_epoch_time,
36
37
  device_token_length,
37
38
  sanitized_device_token,
38
- payload.bytesize,
39
- payload
39
+ encoded_payload.bytesize,
40
+ encoded_payload
40
41
  ].pack('CNNnH64nA*')
41
42
  end
42
43
 
43
- private
44
+ def alert=(alert)
45
+ @alert = alert
46
+ @encoded_payload = nil
47
+ end
44
48
 
45
- def payload_too_large?
46
- encoded_payload.bytesize > MAX_PAYLOAD_SIZE
49
+ def badge=(badge)
50
+ @badge = badge
51
+ @encoded_payload = nil
52
+ end
53
+
54
+ def custom=(custom)
55
+ @custom = custom
56
+ @encoded_payload = nil
57
+ end
58
+
59
+ def sound=(sound)
60
+ @sound = sound
61
+ @encoded_payload = nil
62
+ end
63
+
64
+ def content_available=(content_available)
65
+ @content_available = CONTENT_AVAILABLE_INDICATOR if content_available
66
+ @encoded_payload = nil
47
67
  end
48
68
 
49
69
  def validate_payload
50
70
  fail NoPayloadError unless alert || badge || custom
51
71
  fail PayloadTooLargeError if payload_too_large?
52
72
  end
73
+ alias_method :valid?, :validate_payload
74
+
75
+ private
53
76
 
54
77
  def encoded_payload
55
- JSON.dump(payload_hash)
78
+ @encoded_payload ||= JSON.dump(payload_hash)
56
79
  end
57
80
 
58
81
  def payload_hash
@@ -60,10 +83,15 @@ module Grocer
60
83
  aps_hash[:alert] = alert if alert
61
84
  aps_hash[:badge] = badge if badge
62
85
  aps_hash[:sound] = sound if sound
86
+ aps_hash[:'content-available'] = content_available if content_available
63
87
 
64
88
  { aps: aps_hash }.merge(custom || { })
65
89
  end
66
90
 
91
+ def payload_too_large?
92
+ encoded_payload.bytesize > MAX_PAYLOAD_SIZE
93
+ end
94
+
67
95
  def expiry_epoch_time
68
96
  expiry.to_i
69
97
  end
@@ -1,3 +1,3 @@
1
1
  module Grocer
2
- VERSION = '0.3.4'
2
+ VERSION = '0.4.0'
3
3
  end
data/lib/grocer.rb CHANGED
@@ -1,12 +1,14 @@
1
+ require 'grocer/error_response'
1
2
  require 'grocer/feedback'
3
+ require 'grocer/feedback_connection'
4
+ require 'grocer/mobile_device_management_notification'
5
+ require 'grocer/newsstand_notification'
2
6
  require 'grocer/notification'
3
7
  require 'grocer/passbook_notification'
4
- require 'grocer/newsstand_notification'
5
- require 'grocer/feedback_connection'
6
8
  require 'grocer/push_connection'
7
9
  require 'grocer/pusher'
8
- require 'grocer/version'
9
10
  require 'grocer/server'
11
+ require 'grocer/version'
10
12
 
11
13
  module Grocer
12
14
  Error = Class.new(::StandardError)
@@ -16,6 +18,7 @@ module Grocer
16
18
  NoPortError = Class.new(Error)
17
19
  PayloadTooLargeError = Class.new(Error)
18
20
  CertificateExpiredError = Module.new
21
+ InvalidCommandError = Class.new(Error)
19
22
 
20
23
  def self.env
21
24
  ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
@@ -35,5 +38,4 @@ module Grocer
35
38
  ssl_server = SSLServer.new(options)
36
39
  Server.new(ssl_server)
37
40
  end
38
-
39
41
  end
@@ -0,0 +1,26 @@
1
+ require_relative '../../lib/grocer/error_response'
2
+
3
+ describe Grocer::ErrorResponse do
4
+ let(:status_code) { 1 }
5
+ let(:identifier) { 8342 }
6
+ let(:binary_tuple) { [described_class::COMMAND, status_code, identifier].pack('CCN') }
7
+ let(:invalid_binary_tuple) { 'totally not the right format' }
8
+
9
+ subject(:error_response) { described_class.new(binary_tuple) }
10
+
11
+ describe 'decoding' do
12
+ it 'accepts a binary tuple and sets each attribute' do
13
+ expect(error_response.status_code).to eq(status_code)
14
+ expect(error_response.identifier).to eq(identifier)
15
+ end
16
+
17
+ it 'raises an exception when there are problems decoding' do
18
+ -> { described_class.new(invalid_binary_tuple) }.should
19
+ raise_error(Grocer::InvalidFormatError)
20
+ end
21
+ end
22
+
23
+ it 'finds the status from the status code' do
24
+ expect(error_response.status).to eq('Processing error')
25
+ end
26
+ end
@@ -45,6 +45,16 @@ describe Grocer::Notification do
45
45
  expect(bytes[43...45]).to eq([payload_bytes(notification).bytesize].pack('n'))
46
46
  end
47
47
 
48
+ it 'encodes content-available as part of the payload if a truthy value is passed' do
49
+ notification.content_available = :foo
50
+ expect(payload[:aps][:'content-available']).to eq(1)
51
+ end
52
+
53
+ it 'does not encode content-available as part of the payload if a falsy value is passed' do
54
+ notification.content_available = false
55
+ expect(payload[:aps]).to_not have_key(:'content-available')
56
+ end
57
+
48
58
  context 'missing payload' do
49
59
  let(:payload_options) { Hash.new }
50
60
 
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grocer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
5
- prerelease:
4
+ version: 0.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Andy Lindeman
@@ -11,12 +10,11 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-03-15 00:00:00.000000000 Z
13
+ date: 2013-06-18 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: rspec
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
19
  - - ~>
22
20
  - !ruby/object:Gem::Version
@@ -24,7 +22,6 @@ dependencies:
24
22
  type: :development
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
26
  - - ~>
30
27
  - !ruby/object:Gem::Version
@@ -32,7 +29,6 @@ dependencies:
32
29
  - !ruby/object:Gem::Dependency
33
30
  name: pry
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
33
  - - ~>
38
34
  - !ruby/object:Gem::Version
@@ -40,7 +36,6 @@ dependencies:
40
36
  type: :development
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
40
  - - ~>
46
41
  - !ruby/object:Gem::Version
@@ -48,57 +43,55 @@ dependencies:
48
43
  - !ruby/object:Gem::Dependency
49
44
  name: mocha
50
45
  requirement: !ruby/object:Gem::Requirement
51
- none: false
52
46
  requirements:
53
- - - ! '>='
47
+ - - '>='
54
48
  - !ruby/object:Gem::Version
55
49
  version: '0'
56
50
  type: :development
57
51
  prerelease: false
58
52
  version_requirements: !ruby/object:Gem::Requirement
59
- none: false
60
53
  requirements:
61
- - - ! '>='
54
+ - - '>='
62
55
  - !ruby/object:Gem::Version
63
56
  version: '0'
64
57
  - !ruby/object:Gem::Dependency
65
58
  name: bourne
66
59
  requirement: !ruby/object:Gem::Requirement
67
- none: false
68
60
  requirements:
69
- - - ! '>='
61
+ - - '>='
70
62
  - !ruby/object:Gem::Version
71
63
  version: '0'
72
64
  type: :development
73
65
  prerelease: false
74
66
  version_requirements: !ruby/object:Gem::Requirement
75
- none: false
76
67
  requirements:
77
- - - ! '>='
68
+ - - '>='
78
69
  - !ruby/object:Gem::Version
79
70
  version: '0'
80
71
  - !ruby/object:Gem::Dependency
81
72
  name: rake
82
73
  requirement: !ruby/object:Gem::Requirement
83
- none: false
84
74
  requirements:
85
- - - ! '>='
75
+ - - '>='
86
76
  - !ruby/object:Gem::Version
87
77
  version: '0'
88
78
  type: :development
89
79
  prerelease: false
90
80
  version_requirements: !ruby/object:Gem::Requirement
91
- none: false
92
81
  requirements:
93
- - - ! '>='
82
+ - - '>='
94
83
  - !ruby/object:Gem::Version
95
84
  version: '0'
96
- description: ! " Grocer interfaces with the Apple Push\n
97
- \ Notification Service to send push\n notifications
98
- to iOS devices and collect\n notification feedback via
99
- the Feedback\n Service.\n\n There
100
- are other gems out there to do this,\n but Grocer plans
101
- to be the cleanest, most\n extensible, and friendliest.\n"
85
+ description: |2
86
+ Grocer interfaces with the Apple Push
87
+ Notification Service to send push
88
+ notifications to iOS devices and collect
89
+ notification feedback via the Feedback
90
+ Service.
91
+
92
+ There are other gems out there to do this,
93
+ but Grocer plans to be the cleanest, most
94
+ extensible, and friendliest.
102
95
  email:
103
96
  - alindeman@gmail.com
104
97
  - steveharman@gmail.com
@@ -118,6 +111,7 @@ files:
118
111
  - grocer.gemspec
119
112
  - lib/grocer.rb
120
113
  - lib/grocer/connection.rb
114
+ - lib/grocer/error_response.rb
121
115
  - lib/grocer/extensions/deep_symbolize_keys.rb
122
116
  - lib/grocer/failed_delivery_attempt.rb
123
117
  - lib/grocer/feedback.rb
@@ -140,6 +134,7 @@ files:
140
134
  - spec/fixtures/example.key
141
135
  - spec/fixtures/example.pem
142
136
  - spec/grocer/connection_spec.rb
137
+ - spec/grocer/error_response_spec.rb
143
138
  - spec/grocer/extensions/deep_symbolize_keys_spec.rb
144
139
  - spec/grocer/failed_delivery_attempt_spec.rb
145
140
  - spec/grocer/feedback_connection_spec.rb
@@ -161,39 +156,33 @@ files:
161
156
  homepage: https://github.com/grocer/grocer
162
157
  licenses:
163
158
  - MIT
159
+ metadata: {}
164
160
  post_install_message:
165
161
  rdoc_options: []
166
162
  require_paths:
167
163
  - lib
168
164
  required_ruby_version: !ruby/object:Gem::Requirement
169
- none: false
170
165
  requirements:
171
- - - ! '>='
166
+ - - '>='
172
167
  - !ruby/object:Gem::Version
173
168
  version: '0'
174
- segments:
175
- - 0
176
- hash: -4490716643818473019
177
169
  required_rubygems_version: !ruby/object:Gem::Requirement
178
- none: false
179
170
  requirements:
180
- - - ! '>='
171
+ - - '>='
181
172
  - !ruby/object:Gem::Version
182
173
  version: '0'
183
- segments:
184
- - 0
185
- hash: -4490716643818473019
186
174
  requirements: []
187
175
  rubyforge_project:
188
- rubygems_version: 1.8.23
176
+ rubygems_version: 2.0.2
189
177
  signing_key:
190
- specification_version: 3
178
+ specification_version: 4
191
179
  summary: Pushing Apple notifications since 2012.
192
180
  test_files:
193
181
  - spec/fixtures/example.cer
194
182
  - spec/fixtures/example.key
195
183
  - spec/fixtures/example.pem
196
184
  - spec/grocer/connection_spec.rb
185
+ - spec/grocer/error_response_spec.rb
197
186
  - spec/grocer/extensions/deep_symbolize_keys_spec.rb
198
187
  - spec/grocer/failed_delivery_attempt_spec.rb
199
188
  - spec/grocer/feedback_connection_spec.rb