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 +7 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +16 -7
- data/lib/grocer/error_response.rb +33 -0
- data/lib/grocer/newsstand_notification.rb +4 -4
- data/lib/grocer/notification.rb +43 -15
- data/lib/grocer/version.rb +1 -1
- data/lib/grocer.rb +6 -4
- data/spec/grocer/error_response_spec.rb +26 -0
- data/spec/grocer/notification_spec.rb +10 -0
- metadata +26 -37
    
        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
    
    
    
        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: | 
| 73 | 
            -
              alert: | 
| 74 | 
            -
              badge: | 
| 75 | 
            -
              sound: | 
| 76 | 
            -
              expiry: | 
| 77 | 
            -
              identifier: | 
| 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 | 
| 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
         | 
    
        data/lib/grocer/notification.rb
    CHANGED
    
    | @@ -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 | 
| 9 | 
            -
             | 
| 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 | 
| 15 | 
            -
                #           :alert | 
| 16 | 
            -
                #           :badge | 
| 17 | 
            -
                #           :sound | 
| 18 | 
            -
                #           :expiry | 
| 19 | 
            -
                #           :identifier | 
| 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 | 
            -
                     | 
| 39 | 
            -
                     | 
| 39 | 
            +
                    encoded_payload.bytesize,
         | 
| 40 | 
            +
                    encoded_payload
         | 
| 40 41 | 
             
                  ].pack('CNNnH64nA*')
         | 
| 41 42 | 
             
                end
         | 
| 42 43 |  | 
| 43 | 
            -
                 | 
| 44 | 
            +
                def alert=(alert)
         | 
| 45 | 
            +
                  @alert = alert
         | 
| 46 | 
            +
                  @encoded_payload = nil
         | 
| 47 | 
            +
                end
         | 
| 44 48 |  | 
| 45 | 
            -
                def  | 
| 46 | 
            -
                   | 
| 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
         | 
    
        data/lib/grocer/version.rb
    CHANGED
    
    
    
        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. | 
| 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- | 
| 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:  | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 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:  | 
| 176 | 
            +
            rubygems_version: 2.0.2
         | 
| 189 177 | 
             
            signing_key: 
         | 
| 190 | 
            -
            specification_version:  | 
| 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
         |