rapns 2.0.5.rc1 → 2.0.5

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 2.0.5 (Nov 4, 2012) ##
2
+ * Support content-available (#68).
3
+ * Append to log files.
4
+ * Fire a callback when Feedback is received.
5
+
1
6
  ## 2.0.5.rc1 (Oct 5, 2012) ##
2
7
  * Release db connections back into the pool after use (#72).
3
8
  * Continue to start daemon if a connection cannot be made during startup (#62) (@mattconnolly).
@@ -23,4 +28,4 @@
23
28
  * Hot Updates - add/remove apps without restart.
24
29
  * MDM support.
25
30
  * Removed rapns.yml in favour of command line options.
26
- * Started the changelog!
31
+ * Started the changelog!
data/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ### Who uses rapns?
17
17
 
18
- [GateGuru](http://gateguruapp.com), among others!
18
+ [GateGuru](http://gateguruapp.com) and [Desk.com](http://desk.com), among others!
19
19
 
20
20
  *I'd love to hear if you use rapns - @ileitch on twitter.*
21
21
 
@@ -166,3 +166,4 @@ Thank you to the following wonderful people for contributing to rapns:
166
166
  * [@dei79](https://github.com/dei79)
167
167
  * [@adorr](https://github.com/adorr)
168
168
  * [@mattconnolly](https://github.com/mattconnolly)
169
+ * [@emeitch](https://github.com/emeitch)
data/bin/rapns CHANGED
@@ -5,13 +5,7 @@ require 'rapns'
5
5
 
6
6
  environment = ARGV[0]
7
7
 
8
- config = Struct.new(:foreground, :push_poll, :feedback_poll, :airbrake_notify, :check_for_errors, :pid_file, :batch_size).new
9
- config.foreground = false
10
- config.push_poll = 2
11
- config.feedback_poll = 60
12
- config.airbrake_notify = true
13
- config.check_for_errors = true
14
- config.batch_size = 5000
8
+ config = Rapns.configuration
15
9
 
16
10
  banner = 'Usage: rapns <Rails environment> [options]'
17
11
  ARGV.options do |opts|
@@ -35,6 +29,7 @@ end
35
29
 
36
30
  ENV['RAILS_ENV'] = environment
37
31
  load 'config/environment.rb'
32
+ load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
38
33
 
39
34
  require 'rapns/daemon'
40
35
  require 'rapns/patches'
data/lib/rapns/app.rb CHANGED
@@ -8,5 +8,23 @@ module Rapns
8
8
  validates :environment, :presence => true, :inclusion => { :in => %w(development production) }
9
9
  validates :certificate, :presence => true
10
10
  validates_numericality_of :connections, :greater_than => 0, :only_integer => true
11
+
12
+ validate :certificate_has_matching_private_key
13
+
14
+ private
15
+
16
+ def certificate_has_matching_private_key
17
+ result = false
18
+ if certificate.present?
19
+ x509 = OpenSSL::X509::Certificate.new certificate rescue nil
20
+ pkey = OpenSSL::PKey::RSA.new certificate rescue nil
21
+ result = !x509.nil? && !pkey.nil?
22
+ unless result
23
+ errors.add :certificate, "Certificate value must contain a certificate and a private key"
24
+ end
25
+ end
26
+ result
27
+ end
11
28
  end
12
- end
29
+ end
30
+
@@ -0,0 +1,55 @@
1
+ module Rapns
2
+
3
+ # A globally accessible instance of Rapns::Config
4
+ def self.configuration
5
+ @configuration ||= Rapns::Config.new
6
+ end
7
+
8
+ # Call the given block yielding to it the global Rapns::Config instance for setting
9
+ # configuration values / callbacks.
10
+ #
11
+ # Typically this would be used in your Rails application's config/initializers/rapns.rb file
12
+ def self.configure
13
+ yield configuration if block_given?
14
+ end
15
+
16
+ # A class to hold Rapns configuration settings and callbacks.
17
+ class Config < Struct.new(:foreground, :push_poll, :feedback_poll, :airbrake_notify, :check_for_errors, :pid_file, :batch_size)
18
+
19
+ attr_accessor :feedback_callback
20
+
21
+ # Initialize the Config with default values
22
+ def initialize
23
+ super
24
+
25
+ # defaults:
26
+ self.foreground = false
27
+ self.push_poll = 2
28
+ self.feedback_poll = 60
29
+ self.airbrake_notify = true
30
+ self.check_for_errors = true
31
+ self.batch_size = 5000
32
+ end
33
+
34
+ # Define a block that will be executed with a Rapns::Feedback instance when feedback has been received from the
35
+ # push notification servers that a notification has failed to be delivered. Further notifications should not
36
+ # be sent to this device token.
37
+ #
38
+ # Example usage (in config/initializers/rapns.rb):
39
+ #
40
+ # Rapns.configure do |config|
41
+ # config.on_feedback do |feedback|
42
+ # device = Device.find_by_device_token feedback.device_token
43
+ # if device
44
+ # device.active = false
45
+ # device.save
46
+ # end
47
+ # end
48
+ # end
49
+ #
50
+ # Where `Device` is a model specific to your Rails app that has a `device_token` field.
51
+ def on_feedback(&block)
52
+ self.feedback_callback = block
53
+ end
54
+ end
55
+ end
@@ -69,7 +69,12 @@ module Rapns
69
69
  formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
70
70
  with_database_reconnect_and_retry do
71
71
  Rapns::Daemon.logger.info("[FeedbackReceiver:#{@name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
72
- Rapns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @name)
72
+ feedback = Rapns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @name)
73
+ begin
74
+ Rapns.configuration.feedback_callback.call(feedback) if Rapns.configuration.feedback_callback
75
+ rescue Exception => e
76
+ Rapns::Daemon.logger.error(e)
77
+ end
73
78
  end
74
79
  end
75
80
  end
@@ -3,7 +3,7 @@ module Rapns
3
3
  class Logger
4
4
  def initialize(options)
5
5
  @options = options
6
- log = File.open(File.join(Rails.root, 'log', 'rapns.log'), 'w')
6
+ log = File.open(File.join(Rails.root, 'log', 'rapns.log'), 'a')
7
7
  log.sync = true
8
8
  @logger = ActiveSupport::BufferedLogger.new(log, Rails.logger.level)
9
9
  @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
@@ -52,4 +52,4 @@ module Rapns
52
52
  end
53
53
  end
54
54
  end
55
- end
55
+ end
@@ -52,22 +52,36 @@ module Rapns
52
52
  multi_json_load(read_attribute(:attributes_for_device)) if read_attribute(:attributes_for_device)
53
53
  end
54
54
 
55
- MDM_OVERIDE_KEY = '__rapns_mdm__'
55
+ MDM_KEY = '__rapns_mdm__'
56
56
  def mdm=(magic)
57
- self.attributes_for_device = {MDM_OVERIDE_KEY => magic}
57
+ self.attributes_for_device = { MDM_KEY => magic }
58
+ end
59
+
60
+ CONTENT_AVAILABLE_KEY = '__rapns_content_available__'
61
+ def content_available=(bool)
62
+ return unless bool
63
+ self.attributes_for_device = { CONTENT_AVAILABLE_KEY => true }
58
64
  end
59
65
 
60
66
  def as_json
61
67
  json = ActiveSupport::OrderedHash.new
62
68
 
63
- if attributes_for_device && attributes_for_device.key?(MDM_OVERIDE_KEY)
64
- json['mdm'] = attributes_for_device[MDM_OVERIDE_KEY]
69
+ if attributes_for_device && attributes_for_device.key?(MDM_KEY)
70
+ json['mdm'] = attributes_for_device[MDM_KEY]
65
71
  else
66
72
  json['aps'] = ActiveSupport::OrderedHash.new
67
73
  json['aps']['alert'] = alert if alert
68
74
  json['aps']['badge'] = badge if badge
69
75
  json['aps']['sound'] = sound if sound
70
- attributes_for_device.each { |k, v| json[k.to_s] = v.to_s } if attributes_for_device
76
+
77
+ if attributes_for_device && attributes_for_device[CONTENT_AVAILABLE_KEY]
78
+ json['aps']['content-available'] = 1
79
+ end
80
+
81
+ if attributes_for_device
82
+ non_aps_attributes = attributes_for_device.reject { |k, v| k == CONTENT_AVAILABLE_KEY }
83
+ non_aps_attributes.each { |k, v| json[k.to_s] = v.to_s }
84
+ end
71
85
  end
72
86
 
73
87
  json
data/lib/rapns/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rapns
2
- VERSION = '2.0.5.rc1'
2
+ VERSION = '2.0.5'
3
3
  end
data/lib/rapns.rb CHANGED
@@ -7,3 +7,4 @@ require 'rapns/device_token_format_validator'
7
7
  require 'rapns/notification'
8
8
  require 'rapns/feedback'
9
9
  require 'rapns/app'
10
+ require 'rapns/config'
@@ -0,0 +1,89 @@
1
+ require "spec_helper"
2
+
3
+
4
+ # a test certificate that contains both an X509 certificate and
5
+ # a private key, similar to those used for connecting to Apple
6
+ # push notification servers.
7
+ #
8
+ # Note that we cannot validate the certificate and private key
9
+ # because we are missing the certificate chain used to validate
10
+ # the certificate, and this is private to Apple. So if the app
11
+ # has a certificate and a private key in it, the only way to find
12
+ # out if it really is valid is to connect to Apple's servers.
13
+ #
14
+ TEST_CERT = <<EOF
15
+ Bag Attributes
16
+ friendlyName: test certificate
17
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
18
+ subject=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
19
+ issuer=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
20
+ -----BEGIN CERTIFICATE-----
21
+ MIID5jCCAs6gAwIBAgIBATALBgkqhkiG9w0BAQswgY0xGTAXBgNVBAMMEHRlc3Qg
22
+ Y2VydGlmaWNhdGUxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAsMB0V4YW1wbGUx
23
+ DDAKBgNVBAgMA1FMRDELMAkGA1UEBhMCQVUxEDAOBgNVBAcMB0V4YW1wbGUxHzAd
24
+ BgkqhkiG9w0BCQEWEHVzZXJAZXhhbXBsZS5jb20wHhcNMTIwOTA5MDMxODMyWhcN
25
+ MjIwOTA3MDMxODMyWjCBjTEZMBcGA1UEAwwQdGVzdCBjZXJ0aWZpY2F0ZTEQMA4G
26
+ A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEMMAoGA1UECAwDUUxEMQsw
27
+ CQYDVQQGEwJBVTEQMA4GA1UEBwwHRXhhbXBsZTEfMB0GCSqGSIb3DQEJARYQdXNl
28
+ ckBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKF+
29
+ UDsN1sLen8g+97PNTiWju9+wkSv+H5rQlvb6YFLPx11YvqpK8ms6kFU1OmWeLfmh
30
+ cpsT+bZtKupC7aGPoSG3RXzzf/YUMgs/ZSXA0idZHA6tkReAEzIX6jL5otfPWbaP
31
+ luCTUoVMeP4u9ywk628zlqh9IQHC1Agl0R1xGCpULDk8kn1gPyEisl38wI5aDbzy
32
+ 6lYQGNUKOqt1xfVjtIFe/jyY/v0sxFjIJlRLcAFBuJx4sRV+PwRBkusOQtYwcwpI
33
+ loMxJj+GQe66ueATW81aC4iOU66DAFFEuGzwIwm3bOilimGGQbGb92F339RfmSOo
34
+ TPAvVhsakI3mzESb4lkCAwEAAaNRME8wDgYDVR0PAQH/BAQDAgeAMCAGA1UdJQEB
35
+ /wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAbBgNVHREEFDASgRB1c2VyQGV4YW1w
36
+ bGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA5UbNR+83ZdI2DiaB4dRmy0V5RDAqJ
37
+ k9+QskcTV4gBTjsOBS46Dw1tI6iTrfTyjYJdnyH0Y2Y2YVWBnvtON41UCZak+4ed
38
+ /IqyzU0dtfZ+frWa0RY4reyl80TwqnzyJfni0nDo4zGGvz70cxyaz2u1BWqwLjqb
39
+ dh8Dxvt+aHW2MQi0iGKh/HNbgwVanR4+ubNwziK9sR1Rnq9MkHWtwBw16SXQG6ao
40
+ SZKASWNaH8VL08Zz0E98cwd137UJkPsldCwJ8kHR5OzkcjPdXvnGD3d64yy2TC1Z
41
+ Gy1Aazt98wPcTYBytlhK8Rvzg9OoY9QmsdpmWxz1ZCXECJNqCa3IKsqO
42
+ -----END CERTIFICATE-----
43
+ Bag Attributes
44
+ friendlyName: test certificate
45
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
46
+ Key Attributes: <No Attributes>
47
+ -----BEGIN RSA PRIVATE KEY-----
48
+ MIIEpQIBAAKCAQEAoX5QOw3Wwt6fyD73s81OJaO737CRK/4fmtCW9vpgUs/HXVi+
49
+ qkryazqQVTU6ZZ4t+aFymxP5tm0q6kLtoY+hIbdFfPN/9hQyCz9lJcDSJ1kcDq2R
50
+ F4ATMhfqMvmi189Zto+W4JNShUx4/i73LCTrbzOWqH0hAcLUCCXRHXEYKlQsOTyS
51
+ fWA/ISKyXfzAjloNvPLqVhAY1Qo6q3XF9WO0gV7+PJj+/SzEWMgmVEtwAUG4nHix
52
+ FX4/BEGS6w5C1jBzCkiWgzEmP4ZB7rq54BNbzVoLiI5TroMAUUS4bPAjCbds6KWK
53
+ YYZBsZv3YXff1F+ZI6hM8C9WGxqQjebMRJviWQIDAQABAoIBAQCTiLIDQUFSBdAz
54
+ QFNLD+S0vkCEuunlJuP4q1c/ir006l1YChsluBJ/o6D4NwiCjV+zDquEwVsALftm
55
+ yH4PewfZpXT2Ef508T5GyEO/mchj6iSXxDkpHvhqay6qIyWBwwxSnBtaTzy0Soi+
56
+ rmlhCtmLXbXld2sQEM1kJChGnWtWPtvSyrn+mapNPZviGRtgRNK+YsrAti1nUext
57
+ 2syO5mTdHf1D8GR7I98OaX6odREuSocEV9PzfapWZx2GK5tvRiS1skiug5ciieTd
58
+ Am5/C+bb31h4drFslihLb5BRGO5SFQJvMJL2Sx1f19BCC4XikS01P4/zZbxQNq79
59
+ kxEQuDGBAoGBANP4pIYZ5xshCkx7cTYqmxzWLClGKE2S7Oa8N89mtOwfmqT9AFun
60
+ t9Us9Ukbi8BaKlKhGpQ1HlLf/KVcpyW0x2qLou6AyIWYH+/5VaR3graNgUnzpK9f
61
+ 1F5HoaNHbhlAoebqhzhASFlJI2aqUdQjdOv73z+s9szJU4gpILNwGDFnAoGBAMMJ
62
+ j+vIxtG9J2jldyoXzpg5mbMXSj9u/wFLBVdjXWyOoiqVMMBto53RnoqAom7Ifr9D
63
+ 49LxRAT1Q3l4vs/YnM3ziMsIg2vQK1EbrLsY9OnD/kvPaLXOlNIOdfLM8UeVWZMc
64
+ I4LPbbZrhv/7CC8RjbRhMoWWdGYPvxmvD6V4ZDY/AoGBALoI6OxA45Htx4okdNHj
65
+ RstiNNPsnQaoQn6nBhxiubraafEPkzbd1fukP4pwQJELEUX/2sHkdL6rkqLW1GPF
66
+ a5dZAiBsqpCFWNJWdBGqSfBJ9QSgbxLz+gDcwUH6OOi0zuNJRm/aCyVBiW5bYQHc
67
+ NIvAPMk31ksZDtTbs7WIVdNVAoGBALZ1+KWNxKqs+fSBT5UahpUUtfy8miJz9a7A
68
+ /3M8q0cGvSF3Rw+OwpW/aEGMi+l2OlU27ykFuyukRAac9m296RwnbF79TO2M5ylO
69
+ 6a5zb5ROXlWP6RbE96b4DlIidssQJqegmHwlEC+rsrVBpOtb0aThlYEyOxzMOGyP
70
+ wOR9l8rDAoGADZ4TUHFM6VrvPlUZBkGbqiyXH9IM/y9JWk+22JQCEGnM6RFZemSs
71
+ jxWqQiPAdJtb3xKryJSCMtFPH9azedoCrSgaMflJ1QgoXgpiKZyoEXWraVUggh/0
72
+ CEavgZcTZ6SvMuayqJdGGB+zb1V8XwXMtCjApR/kTm47DjxO4DmpOPs=
73
+ -----END RSA PRIVATE KEY-----
74
+ EOF
75
+
76
+
77
+ describe Rapns::App do
78
+
79
+ it "does not validate an app with an invalid certificate" do
80
+ @app = Rapns::App.new :key => 'test', :environment => 'development', :certificate => 'foo'
81
+ @app.should_not be_valid
82
+ @app.errors.should include(:certificate)
83
+ end
84
+
85
+ it "does validate a real certificate" do
86
+ @app = Rapns::App.new :key => 'test', :environment => 'development', :certificate => TEST_CERT
87
+ @app.should be_valid
88
+ end
89
+ end
@@ -9,14 +9,14 @@ describe Rapns::Daemon::FeedbackReceiver, 'check_for_feedback' do
9
9
  let(:app) { 'my_app' }
10
10
  let(:connection) { stub(:connect => nil, :read => nil, :close => nil) }
11
11
  let(:logger) { stub(:error => nil, :info => nil) }
12
- let(:receiever) { Rapns::Daemon::FeedbackReceiver.new(app, host, port, poll, certificate, password) }
12
+ let(:receiver) { Rapns::Daemon::FeedbackReceiver.new(app, host, port, poll, certificate, password) }
13
13
 
14
14
  before do
15
- receiever.stub(:interruptible_sleep)
15
+ receiver.stub(:interruptible_sleep)
16
16
  Rapns::Daemon.logger = logger
17
17
  Rapns::Daemon::Connection.stub(:new => connection)
18
18
  Rapns::Feedback.stub(:create!)
19
- receiever.instance_variable_set("@stop", false)
19
+ receiver.instance_variable_set("@stop", false)
20
20
  end
21
21
 
22
22
  def stub_connection_read_with_tuple
@@ -32,61 +32,87 @@ describe Rapns::Daemon::FeedbackReceiver, 'check_for_feedback' do
32
32
 
33
33
  it 'instantiates a new connection' do
34
34
  Rapns::Daemon::Connection.should_receive(:new).with("FeedbackReceiver:#{app}", host, port, certificate, password)
35
- receiever.check_for_feedback
35
+ receiver.check_for_feedback
36
36
  end
37
37
 
38
38
  it 'connects to the feeback service' do
39
39
  connection.should_receive(:connect)
40
- receiever.check_for_feedback
40
+ receiver.check_for_feedback
41
41
  end
42
42
 
43
43
  it 'closes the connection' do
44
44
  connection.should_receive(:close)
45
- receiever.check_for_feedback
45
+ receiver.check_for_feedback
46
46
  end
47
47
 
48
48
  it 'reads from the connection' do
49
49
  connection.should_receive(:read).with(38)
50
- receiever.check_for_feedback
50
+ receiver.check_for_feedback
51
51
  end
52
52
 
53
53
  it 'logs the feedback' do
54
54
  stub_connection_read_with_tuple
55
55
  Rapns::Daemon.logger.should_receive(:info).with("[FeedbackReceiver:my_app] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17")
56
- receiever.check_for_feedback
56
+ receiver.check_for_feedback
57
57
  end
58
58
 
59
59
  it 'creates the feedback' do
60
60
  stub_connection_read_with_tuple
61
61
  Rapns::Feedback.should_receive(:create!).with(:failed_at => Time.at(1323533325), :device_token => '834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17', :app => 'my_app')
62
- receiever.check_for_feedback
62
+ receiver.check_for_feedback
63
63
  end
64
64
 
65
65
  it 'logs errors' do
66
66
  error = StandardError.new('bork!')
67
67
  connection.stub(:read).and_raise(error)
68
68
  Rapns::Daemon.logger.should_receive(:error).with(error)
69
- lambda { receiever.check_for_feedback }.should raise_error
69
+ lambda { receiver.check_for_feedback }.should raise_error
70
70
  end
71
71
 
72
72
  it 'sleeps for the feedback poll period' do
73
- receiever.stub(:check_for_feedback)
74
- receiever.should_receive(:interruptible_sleep).with(60).at_least(:once)
73
+ receiver.stub(:check_for_feedback)
74
+ receiver.should_receive(:interruptible_sleep).with(60).at_least(:once)
75
75
  Thread.stub(:new).and_yield
76
- receiever.stub(:loop).and_yield
77
- receiever.start
76
+ receiver.stub(:loop).and_yield
77
+ receiver.start
78
78
  end
79
79
 
80
80
  it 'checks for feedback when started' do
81
- receiever.should_receive(:check_for_feedback).at_least(:once)
81
+ receiver.should_receive(:check_for_feedback).at_least(:once)
82
82
  Thread.stub(:new).and_yield
83
- receiever.stub(:loop).and_yield
84
- receiever.start
83
+ receiver.stub(:loop).and_yield
84
+ receiver.start
85
85
  end
86
86
 
87
87
  it 'interrupts sleep when stopped' do
88
- receiever.stub(:check_for_feedback)
89
- receiever.should_receive(:interrupt_sleep)
90
- receiever.stop
88
+ receiver.stub(:check_for_feedback)
89
+ receiver.should_receive(:interrupt_sleep)
90
+ receiver.stop
91
+ end
92
+
93
+ it 'calls the configuration feedback_callback when feedback is received and the callback is set' do
94
+ stub_connection_read_with_tuple
95
+ Rapns::configuration.feedback_callback = Proc.new {}
96
+ feedback = Object.new
97
+ Rapns::Feedback.stub(:create! => feedback)
98
+ Rapns::configuration.feedback_callback.should_receive(:call).with(feedback)
99
+ receiver.check_for_feedback
100
+ end
101
+
102
+ it 'catches exceptions in the feedback_callback' do
103
+ error = StandardError.new('bork!')
104
+ stub_connection_read_with_tuple
105
+ callback = Proc.new { raise error }
106
+ Rapns::configuration.feedback_callback = callback
107
+ expect { receiver.check_for_feedback }.not_to raise_error
108
+ end
109
+
110
+ it 'logs an exception from the feedback_callback' do
111
+ error = StandardError.new('bork!')
112
+ stub_connection_read_with_tuple
113
+ callback = Proc.new { raise error }
114
+ Rapns::Daemon.logger.should_receive(:error).with(error)
115
+ Rapns::configuration.feedback_callback = callback
116
+ receiver.check_for_feedback
91
117
  end
92
118
  end
@@ -34,7 +34,7 @@ describe Rapns::Daemon::Logger do
34
34
  end
35
35
 
36
36
  it "should open the a log file in the Rails log directory" do
37
- File.should_receive(:open).with('/rails_root/log/rapns.log', 'w')
37
+ File.should_receive(:open).with('/rails_root/log/rapns.log', 'a')
38
38
  Rapns::Daemon::Logger.new(:foreground => true)
39
39
  end
40
40
 
@@ -145,4 +145,4 @@ describe Rapns::Daemon::Logger do
145
145
  logger = Rapns::Daemon::Logger.new({})
146
146
  @buffered_logger.auto_flushing.should be_true
147
147
  end
148
- end
148
+ end
@@ -5,8 +5,8 @@ describe Rapns::Feedback do
5
5
  it { should validate_presence_of(:failed_at) }
6
6
 
7
7
  it "should validate the format of the device_token" do
8
- notification = Rapns::Feedback.new(:device_token => "{$%^&*()}")
9
- notification.valid?.should be_false
10
- notification.errors[:device_token].include?("is invalid").should be_true
8
+ feedback = Rapns::Feedback.new(:device_token => "{$%^&*()}")
9
+ feedback.valid?.should be_false
10
+ feedback.errors[:device_token].include?("is invalid").should be_true
11
11
  end
12
12
  end
@@ -118,6 +118,24 @@ describe Rapns::Notification, 'MDM' do
118
118
  end
119
119
  end
120
120
 
121
+ describe Rapns::Notification, 'content-available' do
122
+ let(:notification) { Rapns::Notification.new }
123
+
124
+ it 'includes content-available in the payload' do
125
+ notification.content_available = true
126
+ notification.as_json['aps']['content-available'].should == 1
127
+ end
128
+
129
+ it 'does not include content-available in the payload if not set' do
130
+ notification.as_json['aps'].key?('content-available').should be_false
131
+ end
132
+
133
+ it 'does not include convert-available as a non-aps attribute' do
134
+ notification.content_available = true
135
+ notification.as_json.key?('content-available').should be_false
136
+ end
137
+ end
138
+
121
139
  describe Rapns::Notification, "to_binary" do
122
140
  it "should correctly convert the notification to binary" do
123
141
  notification = Rapns::Notification.new
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapns
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5.rc1
5
- prerelease: 6
4
+ version: 2.0.5
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ian Leitch
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-06 00:00:00.000000000 Z
12
+ date: 2012-11-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -47,6 +47,7 @@ files:
47
47
  - lib/rapns.rb
48
48
  - lib/rapns/app.rb
49
49
  - lib/rapns/binary_notification_validator.rb
50
+ - lib/rapns/config.rb
50
51
  - lib/rapns/daemon.rb
51
52
  - lib/rapns/daemon/app_runner.rb
52
53
  - lib/rapns/daemon/connection.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/rapns/patches/rails/3.1.1/postgresql_adapter.rb
71
72
  - lib/rapns/version.rb
72
73
  - config/database.yml
74
+ - spec/rapns/app_spec.rb
73
75
  - spec/rapns/daemon/app_runner_spec.rb
74
76
  - spec/rapns/daemon/connection_spec.rb
75
77
  - spec/rapns/daemon/database_reconnectable_spec.rb
@@ -101,9 +103,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
103
  required_rubygems_version: !ruby/object:Gem::Requirement
102
104
  none: false
103
105
  requirements:
104
- - - ! '>'
106
+ - - ! '>='
105
107
  - !ruby/object:Gem::Version
106
- version: 1.3.1
108
+ version: '0'
107
109
  requirements: []
108
110
  rubyforge_project:
109
111
  rubygems_version: 1.8.23
@@ -112,6 +114,7 @@ specification_version: 3
112
114
  summary: Easy to use, full featured APNs daemon for Rails 3
113
115
  test_files:
114
116
  - config/database.yml
117
+ - spec/rapns/app_spec.rb
115
118
  - spec/rapns/daemon/app_runner_spec.rb
116
119
  - spec/rapns/daemon/connection_spec.rb
117
120
  - spec/rapns/daemon/database_reconnectable_spec.rb