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 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,8 @@
1
+ require 'bundler'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Generate migrations needed to create RPN tables
3
+
4
+ Example:
5
+ rails generate ror_push_notifications:migrations
6
+
@@ -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,4 @@
1
+ require 'ruby-push-notifications'
2
+ require 'rails-push-notifications/apps'
3
+ require 'rails-push-notifications/notification'
4
+ require 'rails-push-notifications/rails_push_notifications_railtie'
@@ -0,0 +1,4 @@
1
+ require 'rails-push-notifications/apps/base_app'
2
+ require 'rails-push-notifications/apps/apns_app'
3
+ require 'rails-push-notifications/apps/gcm_app'
4
+ require 'rails-push-notifications/apps/mpns_app'
@@ -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,7 @@
1
+ module RailsPushNotifications
2
+ class RailsPushNotificationsRailtie < Rails::Railtie
3
+ generators do
4
+ require 'generators/rails-push-notifications/migrations_generator'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module RailsPushNotifications
2
+ VERSION = '0.2.0'
3
+ 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