rails-push-notifications 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +150 -0
- data/Rakefile +8 -0
- data/lib/generators/rails-push-notifications/USAGE +6 -0
- data/lib/generators/rails-push-notifications/migrations_generator.rb +29 -0
- data/lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_apps.rb +23 -0
- data/lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_notifications.rb +22 -0
- data/lib/rails-push-notifications.rb +4 -0
- data/lib/rails-push-notifications/apps.rb +4 -0
- data/lib/rails-push-notifications/apps/apns_app.rb +24 -0
- data/lib/rails-push-notifications/apps/base_app.rb +26 -0
- data/lib/rails-push-notifications/apps/gcm_app.rb +17 -0
- data/lib/rails-push-notifications/apps/mpns_app.rb +17 -0
- data/lib/rails-push-notifications/notification.rb +29 -0
- data/lib/rails-push-notifications/rails_push_notifications_railtie.rb +7 -0
- data/lib/rails-push-notifications/version.rb +3 -0
- data/spec/apps/apns_app_spec.rb +96 -0
- data/spec/apps/gcm_app_spec.rb +69 -0
- data/spec/apps/mpns_app_spec.rb +75 -0
- data/spec/factories/apps.rb +15 -0
- data/spec/factories/notifications.rb +20 -0
- data/spec/generators/migrations_generator_spec.rb +25 -0
- data/spec/log/test.log +262 -0
- data/spec/notification_spec.rb +96 -0
- data/spec/rails_apps/rails4.rb +47 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/factory_girl.rb +5 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 28b29841df4ec35af174ea50b11d4d7a913e7e49
|
4
|
+
data.tar.gz: 7e22916e1d96da39482adb1f174a0981aa797863
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b52a811881b2b7e3644da03f037bf5a45a114ff9a5e8f8c8630f57caa3c45decd4ae1770f5820a3c084a6e4fc9c5311c6e3cbad621583c64bc0ac313d4c79c5
|
7
|
+
data.tar.gz: a4be8fb817019db20905072f539e28dad0f8bd950144e0f4ca08071fe65c0d18002757d4faf31099649ffeb946fafe0a4d51143a99549fdd7319dd427073719f
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# Rpn (Ruby on Rails Push Notifications)
|
2
|
+
[![Build Status](https://travis-ci.org/calonso/rails-push-notifications.svg)](https://travis-ci.org/calonso/rails-push-notifications) [![Code Climate](https://codeclimate.com/github/calonso/rails-push-notifications/badges/gpa.svg)](https://codeclimate.com/github/calonso/rails-push-notifications) [![Test Coverage](https://codeclimate.com/github/calonso/rails-push-notifications/badges/coverage.svg)](https://codeclimate.com/github/calonso/rails-push-notifications)
|
3
|
+
## Professional iOS, Android and Windows Phone push notifications for Ruby on Rails
|
4
|
+
|
5
|
+
Rpn is an intuitive and easy to use gem that will allow you to easily add Push Notifications to your project.
|
6
|
+
|
7
|
+
Rpn supports:
|
8
|
+
|
9
|
+
* Multiple Apple/Android/WinPhone applications/configurations
|
10
|
+
* Single and Bulk notifications
|
11
|
+
* Fully customizable notification's contents
|
12
|
+
|
13
|
+
## Live example
|
14
|
+
|
15
|
+
Want to try this gem live?
|
16
|
+
|
17
|
+
Browse [https://rails-push-notifications-test.herokuapp.com](https://rails-push-notifications-test.herokuapp.com) and play with it!
|
18
|
+
The source code for that project is here: [https://github.com/calonso/rails_push_notifications_test](https://github.com/calonso/rails_push_notifications_test)
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
To install the gem simply add this line to your Gemfile
|
22
|
+
|
23
|
+
gem 'rails-push-notifications', '~> 0.2'
|
24
|
+
|
25
|
+
and then install it by running
|
26
|
+
|
27
|
+
$ bundle install
|
28
|
+
|
29
|
+
Once you have it installed, you need to generate the database migrations in order to build the required database structure and then run the newly generated migrations. So do it by executing:
|
30
|
+
|
31
|
+
$ rails generate rails_push_notifications:migrations
|
32
|
+
$ rake db:migrate
|
33
|
+
|
34
|
+
## Applications setup
|
35
|
+
|
36
|
+
First step when using RailsPushNotifications is to setup Apple/Android/WinPhone applications to which the notifications will be associated to. Each platform has it's own details and process.
|
37
|
+
|
38
|
+
### Apple Applications setup
|
39
|
+
|
40
|
+
Once you have configured your app as Apple Push Notifications Service enabled and you have requested and downloaded both the development and production certificates, it's time to convert them to a readable format (.pem) to import them into Rpn afterwards.
|
41
|
+
|
42
|
+
To convert the certificates the first you need is to export them as .p12 files. To do this:
|
43
|
+
|
44
|
+
1. Open Keychain Application of your Macintosh computer
|
45
|
+
2. Find the recently generated certificates. Click the disclosure button on the left of your certificate and select both the certificate and the contained key
|
46
|
+
3. Right click and select 'Export 2 items...'. Then select the desired .p12 format and save it to your disk
|
47
|
+
|
48
|
+
Once exported, to convert it to the readable .pem format simply run:
|
49
|
+
|
50
|
+
$ openssl pkcs12 -in <your .p12 filename> -out <your output .pem filename>.pem -nodes -clcerts
|
51
|
+
|
52
|
+
Now it's time to create our applications
|
53
|
+
|
54
|
+
app = RailsPushNotifications::APNSApp.new
|
55
|
+
app.apns_dev_cert = File.read('path/to/your/development/certificate.pem')
|
56
|
+
app.apns_prod_cert = File.read('path/to/your/production/certificate.pem')
|
57
|
+
app.sandbox_mode = true
|
58
|
+
app.save
|
59
|
+
|
60
|
+
And that's it!! You can create as many different applications as you want. RailsPushNotifications gem allows it.
|
61
|
+
|
62
|
+
### Android Applications setup
|
63
|
+
|
64
|
+
The only thing you need is the API_KEY that you will get from the Google APIS Console > API Access
|
65
|
+
|
66
|
+
app = RailsPushNotifications::GCMApp.new
|
67
|
+
app.gcm_key = '<your app api key>'
|
68
|
+
app.save
|
69
|
+
|
70
|
+
That's all. You have your applications' configurations stored in RailsPushNotification.
|
71
|
+
|
72
|
+
### WindowsPhone Applications setup
|
73
|
+
|
74
|
+
This case is similar but even simpler than APNS Apps
|
75
|
+
|
76
|
+
app = RailsPushNotifications::MPNSApp.new
|
77
|
+
app.cert = File.read('path/to/your/certificate.pem')
|
78
|
+
app.save
|
79
|
+
|
80
|
+
## Creating notifications
|
81
|
+
|
82
|
+
The notifications interface is common to all kind of apps, but depending on the type of underlying app
|
83
|
+
the destinations and the data format will vary, so for example
|
84
|
+
|
85
|
+
### Apple notifications
|
86
|
+
|
87
|
+
app = <Your Apple app>
|
88
|
+
notification = app.notifications.create(
|
89
|
+
destinations: [
|
90
|
+
'Your first destination token',
|
91
|
+
'Your second destination token'
|
92
|
+
],
|
93
|
+
data: { aps: { alert: 'Hello APNS World!', sound: 'true', badge: 1 } }
|
94
|
+
)
|
95
|
+
|
96
|
+
### GCM Notifications
|
97
|
+
|
98
|
+
app = <Your GCM app>
|
99
|
+
notification = app.notifications.create(
|
100
|
+
destinations: [
|
101
|
+
'Your first destination token',
|
102
|
+
'Your second destination token'
|
103
|
+
],
|
104
|
+
data: { text: 'Hello GCM World!' }
|
105
|
+
)
|
106
|
+
|
107
|
+
### WindowsPhone Notifications
|
108
|
+
|
109
|
+
app = <Your WindowsPhone app>
|
110
|
+
notification = app.notifications.create(
|
111
|
+
destinations: [
|
112
|
+
'Your first destination url',
|
113
|
+
'Your second destination url'
|
114
|
+
],
|
115
|
+
data: { title: 'Title', message: 'Hello MPNS World!', type: :toast }
|
116
|
+
)
|
117
|
+
|
118
|
+
## Pushing notifications
|
119
|
+
|
120
|
+
Eventually we want to actually push our notifications. For that task all the different apps
|
121
|
+
have a `push_notifications` method which will find the associated and not yet pushed notifications
|
122
|
+
and actually push them.
|
123
|
+
|
124
|
+
app = <Your app>
|
125
|
+
# Create several notifications for your app
|
126
|
+
app.push_notifications
|
127
|
+
|
128
|
+
As this task can be potentially very time consuming you will probably want to invoke it in
|
129
|
+
a background worker
|
130
|
+
|
131
|
+
And that's all!! I hope you find this gem useful and please, feel free to contribute with
|
132
|
+
any improvement/bug you may find.
|
133
|
+
|
134
|
+
## Pending tasks
|
135
|
+
|
136
|
+
Please, feel free to contribute in building any of those or adding more stuff to this list!
|
137
|
+
|
138
|
+
* Better generation of the data for each kind of notification. Hiding the particulars
|
139
|
+
to avoid bugs
|
140
|
+
|
141
|
+
## Contributing
|
142
|
+
|
143
|
+
1. Fork it ( https://github.com/calonso/rails-push-notifications/fork )
|
144
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
145
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
146
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
147
|
+
5. Create a new Pull Request
|
148
|
+
|
149
|
+
Released under the MIT-LICENSE.
|
150
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module RailsPushNotifications
|
5
|
+
module Generators
|
6
|
+
class MigrationsGenerator < Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
source_root File.expand_path('../templates/migrations', __FILE__)
|
10
|
+
|
11
|
+
def create_migrations
|
12
|
+
templates = [
|
13
|
+
'create_rails_push_notifications_apps',
|
14
|
+
'create_rails_push_notifications_notifications'
|
15
|
+
]
|
16
|
+
|
17
|
+
templates.each do |file|
|
18
|
+
migration_template("#{file}.rb", "db/migrate/#{file}.rb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.next_migration_number(path)
|
23
|
+
@count ||= 0
|
24
|
+
@count += 1
|
25
|
+
(Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + @count).to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CreateRailsPushNotificationsApps < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :rails_push_notifications_apns_apps do |t|
|
4
|
+
t.text :apns_dev_cert
|
5
|
+
t.text :apns_prod_cert
|
6
|
+
t.boolean :sandbox_mode, deafult: true
|
7
|
+
|
8
|
+
t.timestamps null: false
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :rails_push_notifications_gcm_apps do |t|
|
12
|
+
t.string :gcm_key
|
13
|
+
|
14
|
+
t.timestamps null: false
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :rails_push_notifications_mpns_apps do |t|
|
18
|
+
t.text :cert
|
19
|
+
|
20
|
+
t.timestamps null: false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateRailsPushNotificationsNotifications < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :rails_push_notifications_notifications do |t|
|
4
|
+
t.text :destinations
|
5
|
+
t.integer :app_id
|
6
|
+
t.string :app_type
|
7
|
+
t.text :data
|
8
|
+
t.text :results
|
9
|
+
t.integer :success
|
10
|
+
t.integer :failed
|
11
|
+
t.boolean :sent, default: false
|
12
|
+
|
13
|
+
t.timestamps null: false
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index(
|
17
|
+
:rails_push_notifications_notifications,
|
18
|
+
[:app_id, :app_type, :sent],
|
19
|
+
name: 'app_and_sent_index_on_rails_push_notifications'
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
class APNSApp < BaseApp
|
3
|
+
self.table_name = 'rails_push_notifications_apns_apps'
|
4
|
+
|
5
|
+
validates :apns_dev_cert, presence: true
|
6
|
+
validates :apns_prod_cert, presence: true
|
7
|
+
|
8
|
+
before_validation { |app| app.sandbox_mode = true if app.sandbox_mode.nil? }
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def cert
|
13
|
+
sandbox_mode ? apns_dev_cert : apns_prod_cert
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_pusher
|
17
|
+
RubyPushNotifications::APNS::APNSPusher.new cert, sandbox_mode
|
18
|
+
end
|
19
|
+
|
20
|
+
def notification_type
|
21
|
+
RubyPushNotifications::APNS::APNSNotification
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
class BaseApp < ActiveRecord::Base
|
3
|
+
|
4
|
+
self.abstract_class = true
|
5
|
+
|
6
|
+
has_many :notifications, as: :app
|
7
|
+
|
8
|
+
def push_notifications
|
9
|
+
pending = find_pending
|
10
|
+
to_send = pending.map do |notification|
|
11
|
+
notification_type.new notification.destinations, notification.data
|
12
|
+
end
|
13
|
+
pusher = build_pusher
|
14
|
+
pusher.push to_send
|
15
|
+
pending.each_with_index do |p, i|
|
16
|
+
p.update_attributes! results: to_send[i].results
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def find_pending
|
23
|
+
notifications.where sent: false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
class GCMApp < BaseApp
|
3
|
+
self.table_name = 'rails_push_notifications_gcm_apps'
|
4
|
+
|
5
|
+
validates :gcm_key, presence: true
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def build_pusher
|
10
|
+
RubyPushNotifications::GCM::GCMPusher.new gcm_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def notification_type
|
14
|
+
RubyPushNotifications::GCM::GCMNotification
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
class MPNSApp < BaseApp
|
3
|
+
self.table_name = 'rails_push_notifications_mpns_apps'
|
4
|
+
|
5
|
+
validates :cert, presence: true
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def build_pusher
|
10
|
+
RubyPushNotifications::MPNS::MPNSPusher.new cert
|
11
|
+
end
|
12
|
+
|
13
|
+
def notification_type
|
14
|
+
RubyPushNotifications::MPNS::MPNSNotification
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
class Notification < ActiveRecord::Base
|
3
|
+
|
4
|
+
self.table_name = 'rails_push_notifications_notifications'
|
5
|
+
|
6
|
+
belongs_to :app, polymorphic: true
|
7
|
+
|
8
|
+
validates :app, presence: true
|
9
|
+
validates :data, presence: true
|
10
|
+
validates :destinations, presence: true, length: { minimum: 1 }
|
11
|
+
|
12
|
+
serialize :data, Hash
|
13
|
+
serialize :destinations, Array
|
14
|
+
serialize :results
|
15
|
+
|
16
|
+
before_save do |record|
|
17
|
+
record.sent = !record.results.nil?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
before_save do |record|
|
22
|
+
if record.results_changed? && record.results
|
23
|
+
record.success = record.results.success
|
24
|
+
record.failed = record.results.failed
|
25
|
+
end
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module RailsPushNotifications
|
2
|
+
describe APNSApp, type: :model do
|
3
|
+
it 'creates an instance given valid attributes' do
|
4
|
+
APNSApp.create! attributes_for :apns_app
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'validations' do
|
8
|
+
|
9
|
+
let(:app) { build :apns_app }
|
10
|
+
|
11
|
+
it 'requires an apns dev cert' do
|
12
|
+
app.apns_dev_cert = nil
|
13
|
+
expect(app).to_not be_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'requires an apns prod cert' do
|
17
|
+
app.apns_prod_cert = nil
|
18
|
+
expect(app).to_not be_valid
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'sandbox mode' do
|
22
|
+
it 'automatically sets sandbox mode if missing' do
|
23
|
+
app.sandbox_mode = nil
|
24
|
+
expect do
|
25
|
+
app.valid?
|
26
|
+
end.to change { app.sandbox_mode }.from(nil).to true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'uses the value if given' do
|
30
|
+
app.sandbox_mode = false
|
31
|
+
expect do
|
32
|
+
app.valid?
|
33
|
+
end.to_not change { app.sandbox_mode }.from false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'notifications relationship' do
|
39
|
+
let(:app) { create :apns_app }
|
40
|
+
|
41
|
+
it 'can create new notifications' do
|
42
|
+
expect do
|
43
|
+
app.notifications.create attributes_for :apns_notification
|
44
|
+
end.to change { app.reload.notifications.count }.from(0).to 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#push_notifications' do
|
49
|
+
let(:app) { create :apns_app }
|
50
|
+
let(:notifications) do
|
51
|
+
(1..10).map { create :apns_notification, app: app }
|
52
|
+
end
|
53
|
+
let(:single_notification) { create :apns_notification, app: app }
|
54
|
+
|
55
|
+
let(:connection) do
|
56
|
+
instance_double(RubyPushNotifications::APNS::APNSConnection).
|
57
|
+
as_null_object
|
58
|
+
end
|
59
|
+
|
60
|
+
before do
|
61
|
+
allow(RubyPushNotifications::APNS::APNSConnection).
|
62
|
+
to receive(:open).with(app.apns_dev_cert, app.sandbox_mode).
|
63
|
+
and_return connection
|
64
|
+
allow(IO).to receive(:select).and_return []
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'assigns results' do
|
68
|
+
expect do
|
69
|
+
app.push_notifications
|
70
|
+
end.to change {
|
71
|
+
notifications.map do |n|
|
72
|
+
n.reload.results && n.results.individual_results
|
73
|
+
end
|
74
|
+
}.from([nil] * 10).
|
75
|
+
to([[RubyPushNotifications::APNS::NO_ERROR_STATUS_CODE]] * 10)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'marks as sent' do
|
79
|
+
expect do
|
80
|
+
app.push_notifications
|
81
|
+
end.to change { single_notification.reload.sent }.from(false).to true
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with an already sent notification' do
|
85
|
+
let!(:sent_notification) { create :apns_notification, app: app }
|
86
|
+
|
87
|
+
before { app.push_notifications }
|
88
|
+
|
89
|
+
it "doesn't send already sent notifications" do
|
90
|
+
expect(connection).to_not receive(:write)
|
91
|
+
app.push_notifications
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|