rapns 2.0.5.rc1 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
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