rapns 3.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/CHANGELOG.md +31 -0
  2. data/LICENSE +7 -0
  3. data/README.md +138 -0
  4. data/bin/rapns +41 -0
  5. data/config/database.yml +39 -0
  6. data/lib/generators/rapns_generator.rb +34 -0
  7. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  8. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  9. data/lib/generators/templates/add_gcm.rb +94 -0
  10. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  11. data/lib/generators/templates/create_rapns_feedback.rb +15 -0
  12. data/lib/generators/templates/create_rapns_notifications.rb +26 -0
  13. data/lib/generators/templates/rapns.rb +39 -0
  14. data/lib/rapns/apns/app.rb +8 -0
  15. data/lib/rapns/apns/binary_notification_validator.rb +12 -0
  16. data/lib/rapns/apns/device_token_format_validator.rb +12 -0
  17. data/lib/rapns/apns/feedback.rb +14 -0
  18. data/lib/rapns/apns/notification.rb +86 -0
  19. data/lib/rapns/apns/required_fields_validator.rb +14 -0
  20. data/lib/rapns/app.rb +29 -0
  21. data/lib/rapns/configuration.rb +46 -0
  22. data/lib/rapns/daemon/apns/app_runner.rb +36 -0
  23. data/lib/rapns/daemon/apns/connection.rb +113 -0
  24. data/lib/rapns/daemon/apns/delivery.rb +63 -0
  25. data/lib/rapns/daemon/apns/delivery_handler.rb +21 -0
  26. data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
  27. data/lib/rapns/daemon/apns/feedback_receiver.rb +74 -0
  28. data/lib/rapns/daemon/app_runner.rb +135 -0
  29. data/lib/rapns/daemon/database_reconnectable.rb +57 -0
  30. data/lib/rapns/daemon/delivery.rb +43 -0
  31. data/lib/rapns/daemon/delivery_error.rb +19 -0
  32. data/lib/rapns/daemon/delivery_handler.rb +46 -0
  33. data/lib/rapns/daemon/delivery_queue.rb +42 -0
  34. data/lib/rapns/daemon/delivery_queue_18.rb +44 -0
  35. data/lib/rapns/daemon/delivery_queue_19.rb +42 -0
  36. data/lib/rapns/daemon/feeder.rb +37 -0
  37. data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
  38. data/lib/rapns/daemon/gcm/delivery.rb +206 -0
  39. data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
  40. data/lib/rapns/daemon/interruptible_sleep.rb +18 -0
  41. data/lib/rapns/daemon/logger.rb +68 -0
  42. data/lib/rapns/daemon.rb +136 -0
  43. data/lib/rapns/gcm/app.rb +7 -0
  44. data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  45. data/lib/rapns/gcm/notification.rb +31 -0
  46. data/lib/rapns/gcm/payload_size_validator.rb +13 -0
  47. data/lib/rapns/multi_json_helper.rb +16 -0
  48. data/lib/rapns/notification.rb +54 -0
  49. data/lib/rapns/patches/rails/3.1.0/postgresql_adapter.rb +12 -0
  50. data/lib/rapns/patches/rails/3.1.1/postgresql_adapter.rb +17 -0
  51. data/lib/rapns/patches.rb +6 -0
  52. data/lib/rapns/version.rb +3 -0
  53. data/lib/rapns.rb +21 -0
  54. data/lib/tasks/cane.rake +18 -0
  55. data/lib/tasks/test.rake +33 -0
  56. data/spec/acceptance/gcm_upgrade_spec.rb +34 -0
  57. data/spec/acceptance_spec_helper.rb +85 -0
  58. data/spec/support/simplecov_helper.rb +13 -0
  59. data/spec/support/simplecov_quality_formatter.rb +8 -0
  60. data/spec/unit/apns/app_spec.rb +15 -0
  61. data/spec/unit/apns/feedback_spec.rb +12 -0
  62. data/spec/unit/apns/notification_spec.rb +198 -0
  63. data/spec/unit/app_spec.rb +18 -0
  64. data/spec/unit/configuration_spec.rb +38 -0
  65. data/spec/unit/daemon/apns/app_runner_spec.rb +39 -0
  66. data/spec/unit/daemon/apns/connection_spec.rb +234 -0
  67. data/spec/unit/daemon/apns/delivery_handler_spec.rb +48 -0
  68. data/spec/unit/daemon/apns/delivery_spec.rb +160 -0
  69. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  70. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +118 -0
  71. data/spec/unit/daemon/app_runner_shared.rb +66 -0
  72. data/spec/unit/daemon/app_runner_spec.rb +129 -0
  73. data/spec/unit/daemon/database_reconnectable_spec.rb +109 -0
  74. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  75. data/spec/unit/daemon/delivery_handler_shared.rb +28 -0
  76. data/spec/unit/daemon/delivery_queue_spec.rb +29 -0
  77. data/spec/unit/daemon/feeder_spec.rb +95 -0
  78. data/spec/unit/daemon/gcm/app_runner_spec.rb +17 -0
  79. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +36 -0
  80. data/spec/unit/daemon/gcm/delivery_spec.rb +236 -0
  81. data/spec/unit/daemon/interruptible_sleep_spec.rb +40 -0
  82. data/spec/unit/daemon/logger_spec.rb +156 -0
  83. data/spec/unit/daemon_spec.rb +139 -0
  84. data/spec/unit/gcm/app_spec.rb +5 -0
  85. data/spec/unit/gcm/notification_spec.rb +55 -0
  86. data/spec/unit/notification_shared.rb +38 -0
  87. data/spec/unit/notification_spec.rb +6 -0
  88. data/spec/unit_spec_helper.rb +145 -0
  89. metadata +240 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
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
+
6
+ ## 2.0.5.rc1 (Oct 5, 2012) ##
7
+ * Release db connections back into the pool after use (#72).
8
+ * Continue to start daemon if a connection cannot be made during startup (#62) (@mattconnolly).
9
+
10
+ ## 2.0.4 (Aug 6, 2012) ##
11
+ * Don't exit when there aren't any Rapns::App instances, just warn (#55).
12
+
13
+ ## 2.0.3 (July 26, 2012) ##
14
+ * JRuby support.
15
+ * Explicitly list all attributes instead of calling column_names (#53).
16
+
17
+ ## 2.0.2 (July 25, 2012) ##
18
+ * Support MultiJson < 1.3.0.
19
+ * Make all model attributes accessible.
20
+
21
+ ## 2.0.1 (July 7, 2012) ##
22
+ * Fix delivery when using Ruby 1.8.
23
+ * MultiJson support.
24
+
25
+ ## 2.0.0 (June 19, 2012) ##
26
+
27
+ * Support for multiple apps.
28
+ * Hot Updates - add/remove apps without restart.
29
+ * MDM support.
30
+ * Removed rapns.yml in favour of command line options.
31
+ * Started the changelog!
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2012 Ian Leitch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,138 @@
1
+ [![Build Status](https://secure.travis-ci.org/ileitch/rapns.png?branch=master)](http://travis-ci.org/ileitch/rapns)
2
+
3
+ ### Rapns - Professional grade APNs and GCM daemon
4
+
5
+ * Supports both APNs (iOS) and GCM (Google Cloud Messaging, Android).
6
+ * Seamless Rails integration.
7
+ * Scalable - choose the number of threads each app spawns.
8
+ * Designed for uptime - signal -HUP to add, update apps.
9
+ * Stable - reconnects database and network connections when lost.
10
+ * Works with MRI, JRuby, Rubinius 1.8 and 1.9.
11
+ * [Airbrake](http://airbrakeapp.com/) integration.
12
+
13
+ ### Who uses Rapns?
14
+
15
+ [GateGuru](http://gateguruapp.com) and [Desk.com](http://desk.com), among others!
16
+
17
+ *I'd love to hear if you use Rapns - @ileitch on twitter.*
18
+
19
+ ## Getting Started
20
+
21
+ Add Rapns to your Gemfile:
22
+
23
+ gem 'rapns'
24
+
25
+ Generate the migration, rapns.yml and migrate:
26
+
27
+ rails g rapns
28
+ rake db:migrate
29
+
30
+ ## Generating Certificates
31
+
32
+ 1. Open up Keychain Access and select the `Certificates` category in the sidebar.
33
+ 2. Expand the disclosure arrow next to the iOS Push Services certificate you want to export.
34
+ 3. Select both the certificate and private key.
35
+ 4. Right click and select `Export 2 items...`.
36
+ 5. Save the file as `cert.p12`, make sure the File Format is `Personal Information Exchange (p12)`.
37
+ 6. If you decide to set a password for your exported certificate, please read the 'Adding Apps' section below.
38
+ 7. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
39
+
40
+ `openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
41
+
42
+ ## Create an App
43
+
44
+ #### APNs
45
+ ```ruby
46
+ app = Rapns::Apns::App.new
47
+ app.name = "ios_app"
48
+ app.certificate = File.read("/path/to/development.pem")
49
+ app.environment = "development"
50
+ app.password = "certificate password"
51
+ app.connections = 1
52
+ app.save!
53
+ ```
54
+
55
+ #### GCM
56
+ ```ruby
57
+ app = Rapns::Gcm::App.new
58
+ app.name = "android_app"
59
+ app.auth_key = "..."
60
+ app.connections = 1
61
+ app.save!
62
+ ```
63
+
64
+ ## Create a Notification
65
+
66
+ #### APNs
67
+ ```ruby
68
+ n = Rapns::Apns::Notification.new
69
+ n.app = Rapns::Apns::App.find_by_name("ios_app")
70
+ n.device_token = "..."
71
+ n.alert = "hi mom!"
72
+ n.attributes_for_device = {:foo => :bar}
73
+ n.save!
74
+ ```
75
+
76
+ #### GCM
77
+ ```ruby
78
+ n = Rapns::Gcm::Notification.new
79
+ n.app = Rapns::Gcm::App.find_by_name("android_app")
80
+ n.registration_ids = ["..."]
81
+ n.data = {:message => "hi mom!"}
82
+ n.save!
83
+ ```
84
+
85
+ ## Starting Rapns
86
+
87
+ cd /path/to/rails/app
88
+ rapns <Rails environment> [options]
89
+
90
+ See [Configuration](wiki/Configuration) for a list of options, or run `rapns --help`.
91
+
92
+ ## Updating Rapns
93
+
94
+ After updating you should run `rails g rapns` to check for any new migrations.
95
+
96
+ ## Wiki
97
+
98
+ ### General
99
+ * [Configuration](wiki/Configuration)
100
+ * [Upgrading from 2.x to 3.0](wiki/Upgrading-from-version-2.x-to-3.0)
101
+ * [Deploying to Heroku](wiki/Heroku)
102
+ * [Hot App Updates](wiki/Hot-App-Updates)
103
+
104
+ ### APNs
105
+ * [Advanced APNs Features](wiki/Advanced-APNs-Features)
106
+ * [APNs Delivery Failure Handling](wiki/APNs-Delivery-Failure-Handling)
107
+ * [Why open multiple connections to the APNs?](wiki/Why-open-multiple-connections-to-the-APNs%3F)
108
+ * [Silent failures might be dropped connections](wiki/Dropped-connections)
109
+
110
+ ### GCM
111
+
112
+ ## Contributing
113
+
114
+ Fork as usual and go crazy!
115
+
116
+ When running specs, please note that the ActiveRecord adapter can be changed by setting the `ADAPTER` environment variable. For example: `ADAPTER=postgresql rake`.
117
+
118
+ Available adapters for testing are `mysql`, `mysql2` and `postgresql`.
119
+
120
+ Note that the database username is changed at runtime to be the currently logged in user's name. So if you're testing
121
+ with mysql and you're using a user named 'bob', you will need to grant a mysql user 'bob' access to the 'rapns_test'
122
+ mysql database.
123
+
124
+ ### Contributors
125
+
126
+ Thank you to the following wonderful people for contributing:
127
+
128
+ * [@blakewatters](https://github.com/blakewatters)
129
+ * [@forresty](https://github.com/forresty)
130
+ * [@sjmadsen](https://github.com/sjmadsen)
131
+ * [@ivanyv](https://github.com/ivanyv)
132
+ * [@taybenlor](https://github.com/taybenlor)
133
+ * [@tompesman](https://github.com/tompesman)
134
+ * [@EpicDraws](https://github.com/EpicDraws)
135
+ * [@dei79](https://github.com/dei79)
136
+ * [@adorr](https://github.com/adorr)
137
+ * [@mattconnolly](https://github.com/mattconnolly)
138
+ * [@emeitch](https://github.com/emeitch)
data/bin/rapns ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'rapns'
5
+
6
+ environment = ARGV[0]
7
+
8
+ banner = 'Usage: rapns <Rails environment> [options]'
9
+ if environment.nil? || environment =~ /^-/
10
+ puts banner
11
+ exit 1
12
+ end
13
+
14
+ # A mock configuration to be used before the Rails environment is loaded.
15
+ class CommandlineConfig < Struct.new(*Rapns::CONFIG_ATTRS); end
16
+ config = CommandlineConfig.new
17
+
18
+ ARGV.options do |opts|
19
+ opts.banner = banner
20
+ opts.on('-f', '--foreground', 'Run in the foreground.') { config.foreground = true }
21
+ opts.on('-P N', '--db-poll N', Integer, "Frequency in seconds to check for new notifications. Default: #{config.push_poll}.") { |n| config.push_poll = n }
22
+ opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback. Default: #{config.feedback_poll}.") { |n| config.feedback_poll = n }
23
+ opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false }
24
+ opts.on('-n', '--no-airbrake-notify', 'Disables error notifications via Airbrake.') { config.airbrake_notify = false }
25
+ opts.on('-p PATH', '--pid-file PATH', String, 'Path to write PID file. Relative to Rails root unless absolute.') { |path| config.pid_file = path }
26
+ opts.on('-b N', '--batch-size N', Integer, 'ActiveRecord notifications batch size.') { |n| config.batch_size = n }
27
+ opts.on('-v', '--version', 'Print this version of rapns.') { puts "rapns #{Rapns::VERSION}"; exit }
28
+ opts.on('-h', '--help', 'You\'re looking at it.') { puts opts; exit }
29
+ opts.parse!
30
+ end
31
+
32
+ ENV['RAILS_ENV'] = environment
33
+ load 'config/environment.rb'
34
+ load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
35
+
36
+ Rapns.config.update(config)
37
+
38
+ require 'rapns/daemon'
39
+ require 'rapns/patches'
40
+
41
+ Rapns::Daemon.start
@@ -0,0 +1,39 @@
1
+ # postgresql is the default if no ADAPTER environment variable is set when running specs.
2
+
3
+ postgresql:
4
+ adapter: postgresql
5
+ database: rapns_test
6
+ host: localhost
7
+ username: postgres
8
+ password: ""
9
+
10
+ jdbcpostgresql:
11
+ adapter: jdbcpostgresql
12
+ database: rapns_test
13
+ host: localhost
14
+ username: postgres
15
+ password: ""
16
+
17
+ mysql:
18
+ adapter: mysql
19
+ database: rapns_test
20
+ host: localhost
21
+ username: rapns_test
22
+ password: ""
23
+ encoding: utf8
24
+
25
+ mysql2:
26
+ adapter: mysql
27
+ database: rapns_test
28
+ host: localhost
29
+ username: rapns_test
30
+ password: ""
31
+ encoding: utf8
32
+
33
+ jdbcmysql:
34
+ adapter: jdbcmysql
35
+ database: rapns_test
36
+ host: localhost
37
+ username: rapns_test
38
+ password: ""
39
+ encoding: utf8
@@ -0,0 +1,34 @@
1
+ class RapnsGenerator < Rails::Generators::Base
2
+ include Rails::Generators::Migration
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ def self.next_migration_number(path)
6
+ @time ||= Time.now.utc
7
+ @calls ||= -1
8
+ @calls += 1
9
+ (@time + @calls.seconds).strftime("%Y%m%d%H%M%S")
10
+ end
11
+
12
+ def copy_migration
13
+ add_rapns_migration('create_rapns_notifications')
14
+ add_rapns_migration('create_rapns_feedback')
15
+ add_rapns_migration('add_alert_is_json_to_rapns_notifications')
16
+ add_rapns_migration('add_app_to_rapns')
17
+ add_rapns_migration('create_rapns_apps')
18
+ add_rapns_migration('add_gcm')
19
+ end
20
+
21
+ def copy_config
22
+ copy_file 'rapns.rb', 'config/initializers/rapns.rb'
23
+ end
24
+
25
+ protected
26
+
27
+ def add_rapns_migration(template)
28
+ migration_dir = File.expand_path('db/migrate')
29
+
30
+ if !self.class.migration_exists?(migration_dir, template)
31
+ migration_template "#{template}.rb", "db/migrate/#{template}.rb"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ class AddAlertIsJsonToRapnsNotifications < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :rapns_notifications, :alert_is_json, :boolean, :null => true, :default => false
4
+ end
5
+
6
+ def self.down
7
+ remove_column :rapns_notifications, :alert_is_json
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ class AddAppToRapns < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :rapns_notifications, :app, :string, :null => true
4
+ add_column :rapns_feedback, :app, :string, :null => true
5
+ end
6
+
7
+ def self.down
8
+ remove_column :rapns_notifications, :app
9
+ remove_column :rapns_feedback, :app
10
+ end
11
+ end
@@ -0,0 +1,94 @@
1
+ class AddGcm < ActiveRecord::Migration
2
+ module Rapns
3
+ class App < ActiveRecord::Base
4
+ self.table_name = 'rapns_apps'
5
+ end
6
+
7
+ class Notification < ActiveRecord::Base
8
+ belongs_to :app
9
+ self.table_name = 'rapns_notifications'
10
+ end
11
+ end
12
+
13
+ def self.up
14
+ add_column :rapns_notifications, :type, :string, :null => true
15
+ add_column :rapns_apps, :type, :string, :null => true
16
+
17
+ AddGcm::Rapns::Notification.update_all :type => 'Rapns::Apns::Notification'
18
+ AddGcm::Rapns::App.update_all :type => 'Rapns::Apns::App'
19
+
20
+ change_column_null :rapns_notifications, :type, false
21
+ change_column_null :rapns_apps, :type, false
22
+ change_column_null :rapns_notifications, :device_token, true
23
+ change_column_null :rapns_notifications, :expiry, true
24
+ change_column_null :rapns_apps, :environment, true
25
+ change_column_null :rapns_apps, :certificate, true
26
+
27
+ change_column :rapns_notifications, :error_description, :text
28
+ change_column :rapns_notifications, :sound, :string, :default => 'default'
29
+
30
+ rename_column :rapns_notifications, :attributes_for_device, :data
31
+ rename_column :rapns_apps, :key, :name
32
+
33
+ add_column :rapns_apps, :auth_key, :string, :null => true
34
+
35
+ add_column :rapns_notifications, :collapse_key, :string, :null => true
36
+ add_column :rapns_notifications, :delay_while_idle, :boolean, :null => false, :default => false
37
+ add_column :rapns_notifications, :registration_ids, :text, :null => true
38
+ add_column :rapns_notifications, :app_id, :integer, :null => true
39
+ add_column :rapns_notifications, :retries, :integer, :null => true, :default => 0
40
+
41
+ Rapns::Notification.reset_column_information
42
+ Rapns::App.reset_column_information
43
+
44
+ Rapns::App.all.each do |app|
45
+ Rapns::Notification.update_all(['app_id = ?', app.id], ['app = ?', app.name])
46
+ end
47
+
48
+ change_column_null :rapns_notifications, :app_id, false
49
+ remove_column :rapns_notifications, :app
50
+
51
+ remove_index :rapns_notifications, :name => "index_rapns_notifications_multi"
52
+ add_index :rapns_notifications, [:app_id, :delivered, :failed, :deliver_after], :name => "index_rapns_notifications_multi"
53
+ end
54
+
55
+ def self.down
56
+ AddGcm::Rapns::Notification.where(:type => 'Rapns::Gcm::Notification').delete_all
57
+
58
+ remove_column :rapns_notifications, :type
59
+ remove_column :rapns_apps, :type
60
+
61
+ change_column_null :rapns_notifications, :device_token, false
62
+ change_column_null :rapns_notifications, :expiry, false
63
+ change_column_null :rapns_apps, :environment, false
64
+ change_column_null :rapns_apps, :certificate, false
65
+
66
+ change_column :rapns_notifications, :error_description, :string
67
+ change_column :rapns_notifications, :sound, :string, :default => '1.aiff'
68
+
69
+ rename_column :rapns_notifications, :data, :attributes_for_device
70
+ rename_column :rapns_apps, :name, :key
71
+
72
+ remove_column :rapns_apps, :auth_key
73
+
74
+ remove_column :rapns_notifications, :collapse_key
75
+ remove_column :rapns_notifications, :delay_while_idle
76
+ remove_column :rapns_notifications, :registration_ids
77
+ remove_column :rapns_notifications, :retries
78
+
79
+ add_column :rapns_notifications, :app, :string, :null => true
80
+
81
+ Rapns::Notification.reset_column_information
82
+ Rapns::App.reset_column_information
83
+
84
+ Rapns::App.all.each do |app|
85
+ Rapns::Notification.update_all(['app = ?', app.key], ['app_id = ?', app.id])
86
+ end
87
+
88
+ change_column_null :rapns_notifications, :key, false
89
+ remove_column :rapns_notifications, :app_id
90
+
91
+ remove_index :rapns_notifications, :name => "index_rapns_notifications_multi"
92
+ add_index :rapns_notifications, [:delivered, :failed, :deliver_after], :name => "index_rapns_notifications_multi"
93
+ end
94
+ end
@@ -0,0 +1,16 @@
1
+ class CreateRapnsApps < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :rapns_apps do |t|
4
+ t.string :key, :null => false
5
+ t.string :environment, :null => false
6
+ t.text :certificate, :null => false
7
+ t.string :password, :null => true
8
+ t.integer :connections, :null => false, :default => 1
9
+ t.timestamps
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :rapns_apps
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ class CreateRapnsFeedback < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :rapns_feedback do |t|
4
+ t.string :device_token, :null => false, :limit => 64
5
+ t.timestamp :failed_at, :null => false
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :rapns_feedback, :device_token
10
+ end
11
+
12
+ def self.down
13
+ drop_table :rapns_feedback
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ class CreateRapnsNotifications < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :rapns_notifications do |t|
4
+ t.integer :badge, :null => true
5
+ t.string :device_token, :null => false, :limit => 64
6
+ t.string :sound, :null => true, :default => "1.aiff"
7
+ t.string :alert, :null => true
8
+ t.text :attributes_for_device, :null => true
9
+ t.integer :expiry, :null => false, :default => 1.day.to_i
10
+ t.boolean :delivered, :null => false, :default => false
11
+ t.timestamp :delivered_at, :null => true
12
+ t.boolean :failed, :null => false, :default => false
13
+ t.timestamp :failed_at, :null => true
14
+ t.integer :error_code, :null => true
15
+ t.string :error_description, :null => true
16
+ t.timestamp :deliver_after, :null => true
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :rapns_notifications, [:delivered, :failed, :deliver_after], :name => "index_rapns_notifications_multi"
21
+ end
22
+
23
+ def self.down
24
+ drop_table :rapns_notifications
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ # Rapns configuration. Options set here are overridden by command-line options.
2
+
3
+ Rapns.configure do |config|
4
+
5
+ # Run in the foreground?
6
+ # config.foreground = false
7
+
8
+ # Frequency in seconds to check for new notifications.
9
+ # config.push_poll = 2
10
+
11
+ # Frequency in seconds to check for feedback
12
+ # config.feedback_poll = 60
13
+
14
+ # Enable/Disable error notifications via Airbrake.
15
+ # config.airbrake_notify = true
16
+
17
+ # Disable APNs error checking after notification delivery.
18
+ # config.check_for_errors = true
19
+
20
+ # ActiveRecord notifications batch size.
21
+ # config.batch_size = 5000
22
+
23
+ # Path to write PID file. Relative to Rails root unless absolute.
24
+ # config.pid_file = '/path/to/rapns.pid'
25
+
26
+ # Define a block that will be called with a Rapns::Apns::Feedback instance
27
+ # when feedback is received from the APNs that a notification has
28
+ # failed to be delivered. Further notifications should not be sent to the device.
29
+ #
30
+ # Example:
31
+ # config.on_apns_feedback do |feedback|
32
+ # device = Device.find_by_device_token(feedback.device_token)
33
+ # if device
34
+ # device.active = false
35
+ # device.save!
36
+ # end
37
+ # end
38
+
39
+ end
@@ -0,0 +1,8 @@
1
+ module Rapns
2
+ module Apns
3
+ class App < Rapns::App
4
+ validates :environment, :presence => true, :inclusion => { :in => %w(development production) }
5
+ validates :certificate, :presence => true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module Rapns
2
+ module Apns
3
+ class BinaryNotificationValidator < ActiveModel::Validator
4
+
5
+ def validate(record)
6
+ if record.payload_size > 256
7
+ record.errors[:base] << "APN notification cannot be larger than 256 bytes. Try condensing your alert and device attributes."
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Rapns
2
+ module Apns
3
+ class DeviceTokenFormatValidator < ActiveModel::Validator
4
+
5
+ def validate(record)
6
+ if record.device_token !~ /^[a-z0-9]{64}$/
7
+ record.errors[:device_token] << "is invalid"
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Rapns
2
+ module Apns
3
+ class Feedback < ActiveRecord::Base
4
+ self.table_name = 'rapns_feedback'
5
+
6
+ attr_accessible :device_token, :failed_at, :app
7
+
8
+ validates :device_token, :presence => true
9
+ validates :failed_at, :presence => true
10
+
11
+ validates_with Rapns::Apns::DeviceTokenFormatValidator
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,86 @@
1
+ module Rapns
2
+ module Apns
3
+ class Notification < Rapns::Notification
4
+ class MultipleAppAssignmentError < StandardError; end
5
+
6
+ validates :device_token, :presence => true
7
+ validates :badge, :numericality => true, :allow_nil => true
8
+
9
+ validates_with Rapns::Apns::DeviceTokenFormatValidator
10
+ validates_with Rapns::Apns::BinaryNotificationValidator
11
+ validates_with Rapns::Apns::RequiredFieldsValidator
12
+
13
+ alias_method :attributes_for_device=, :data=
14
+ alias_method :attributes_for_device, :data
15
+
16
+ def device_token=(token)
17
+ write_attribute(:device_token, token.delete(" <>")) if !token.nil?
18
+ end
19
+
20
+ def alert=(alert)
21
+ if alert.is_a?(Hash)
22
+ write_attribute(:alert, multi_json_dump(alert))
23
+ self.alert_is_json = true if has_attribute?(:alert_is_json)
24
+ else
25
+ write_attribute(:alert, alert)
26
+ self.alert_is_json = false if has_attribute?(:alert_is_json)
27
+ end
28
+ end
29
+
30
+ def alert
31
+ string_or_json = read_attribute(:alert)
32
+
33
+ if has_attribute?(:alert_is_json)
34
+ if alert_is_json?
35
+ multi_json_load(string_or_json)
36
+ else
37
+ string_or_json
38
+ end
39
+ else
40
+ multi_json_load(string_or_json) rescue string_or_json
41
+ end
42
+ end
43
+
44
+ MDM_KEY = '__rapns_mdm__'
45
+ def mdm=(magic)
46
+ self.attributes_for_device = { MDM_KEY => magic }
47
+ end
48
+
49
+ CONTENT_AVAILABLE_KEY = '__rapns_content_available__'
50
+ def content_available=(bool)
51
+ return unless bool
52
+ self.attributes_for_device = { CONTENT_AVAILABLE_KEY => true }
53
+ end
54
+
55
+ def as_json
56
+ json = ActiveSupport::OrderedHash.new
57
+
58
+ if attributes_for_device && attributes_for_device.key?(MDM_KEY)
59
+ json['mdm'] = attributes_for_device[MDM_KEY]
60
+ else
61
+ json['aps'] = ActiveSupport::OrderedHash.new
62
+ json['aps']['alert'] = alert if alert
63
+ json['aps']['badge'] = badge if badge
64
+ json['aps']['sound'] = sound if sound
65
+
66
+ if attributes_for_device && attributes_for_device[CONTENT_AVAILABLE_KEY]
67
+ json['aps']['content-available'] = 1
68
+ end
69
+
70
+ if attributes_for_device
71
+ non_aps_attributes = attributes_for_device.reject { |k, v| k == CONTENT_AVAILABLE_KEY }
72
+ non_aps_attributes.each { |k, v| json[k.to_s] = v.to_s }
73
+ end
74
+ end
75
+
76
+ json
77
+ end
78
+
79
+ def to_binary(options = {})
80
+ id_for_pack = options[:for_validation] ? 0 : id
81
+ [1, id_for_pack, expiry, 0, 32, device_token, payload_size, payload].pack("cNNccH*na*")
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,14 @@
1
+ module Rapns
2
+ module Apns
3
+ class RequiredFieldsValidator < ActiveModel::Validator
4
+
5
+ # Notifications must contain one of alert, badge or sound as per:
6
+ # https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html
7
+ def validate(record)
8
+ if record.alert.nil? && record.badge.nil? && record.sound.nil?
9
+ record.errors[:base] << "APN Notification must contain one of alert, badge, or sound"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end