grocer 0.3.4 → 0.4.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.
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