fcm-rails-push-notifications 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +152 -0
  4. data/Rakefile +15 -0
  5. data/lib/generators/rails-push-notifications/USAGE +6 -0
  6. data/lib/generators/rails-push-notifications/migrations_generator.rb +29 -0
  7. data/lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_apps.rb +29 -0
  8. data/lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_notifications.rb +22 -0
  9. data/lib/rails-push-notifications.rb +4 -0
  10. data/lib/rails-push-notifications/apps.rb +5 -0
  11. data/lib/rails-push-notifications/apps/apns_app.rb +36 -0
  12. data/lib/rails-push-notifications/apps/base_app.rb +39 -0
  13. data/lib/rails-push-notifications/apps/fcm_app.rb +27 -0
  14. data/lib/rails-push-notifications/apps/gcm_app.rb +27 -0
  15. data/lib/rails-push-notifications/apps/mpns_app.rb +27 -0
  16. data/lib/rails-push-notifications/notification.rb +42 -0
  17. data/lib/rails-push-notifications/rails_push_notifications_railtie.rb +12 -0
  18. data/lib/rails-push-notifications/version.rb +3 -0
  19. data/spec/apps/apns_app_spec.rb +96 -0
  20. data/spec/apps/gcm_app_spec.rb +69 -0
  21. data/spec/apps/mpns_app_spec.rb +75 -0
  22. data/spec/factories/apps.rb +15 -0
  23. data/spec/factories/notifications.rb +20 -0
  24. data/spec/generators/migrations_generator_spec.rb +25 -0
  25. data/spec/notification_spec.rb +98 -0
  26. data/spec/rails_apps/rails3_2.rb +47 -0
  27. data/spec/rails_apps/rails4.rb +47 -0
  28. data/spec/spec_helper.rb +61 -0
  29. data/spec/support/factory_girl.rb +5 -0
  30. metadata +196 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9598012750f8ccf4873ae6e5985bc978c9f504be
4
+ data.tar.gz: 7e12d994ea80a6ff257fd2c945ab6a4d51006cfe
5
+ SHA512:
6
+ metadata.gz: 9c8fa43b070c14df34d558812f2a7e2ce7c0232ae6e6bffee160dc058a518f62a879865ec54f651243143001908eb36dd731d8d3e8230ef43837708fa8ccd01b
7
+ data.tar.gz: 06953b99a1168dc985ee990c545bb185336f2e3d1e1c27b608240fb970bde432157292d11bb8bcbef5077109bf182d50f40e0bec8d212620d6e122e04f6331e2
@@ -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.
@@ -0,0 +1,152 @@
1
+ # 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/coverage)
3
+ ## Professional iOS, Android and Windows Phone push notifications for Ruby on Rails
4
+
5
+ RailsPushNotifications is an intuitive and easy to use gem that will allow you to easily add Push Notifications to your project.
6
+
7
+ RailsPushNotifications key features:
8
+
9
+ * Multiple Apple/Android/WinPhone applications/configurations
10
+ * Single and Bulk notifications
11
+ * Fully customizable notification's contents
12
+ * Detailed feedback on each individual notification's push results.
13
+
14
+ ## Live example
15
+
16
+ Want to try this gem live?
17
+
18
+ Browse [https://rails-push-notifications-test.herokuapp.com](https://rails-push-notifications-test.herokuapp.com) and play with it!
19
+ The source code for that project is here: [https://github.com/calonso/rails_push_notifications_test](https://github.com/calonso/rails_push_notifications_test)
20
+
21
+ ## Installation
22
+ To install the gem simply add this line to your Gemfile
23
+
24
+ gem 'rails-push-notifications', '~> 0.2.0'
25
+
26
+ and then install it by running
27
+
28
+ $ bundle install
29
+
30
+ 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:
31
+
32
+ $ rails generate rails_push_notifications:migrations
33
+ $ rake db:migrate
34
+
35
+ ## Applications setup
36
+
37
+ 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.
38
+
39
+ ### Apple Applications setup
40
+
41
+ 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.
42
+
43
+ To convert the certificates the first thing you need to do is to export them as .p12 files. To do this:
44
+
45
+ 1. Open Keychain Application of your Macintosh computer
46
+ 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
47
+ 3. Right click and select 'Export 2 items...'. Then select the desired .p12 format and save it to your disk
48
+
49
+ Once exported, to convert it to the readable .pem format simply run:
50
+
51
+ $ openssl pkcs12 -in <your .p12 filename> -out <your output .pem filename>.pem -nodes -clcerts
52
+
53
+ Now it's time to create our applications
54
+
55
+ app = RailsPushNotifications::APNSApp.new
56
+ app.apns_dev_cert = File.read('path/to/your/development/certificate.pem')
57
+ app.apns_prod_cert = File.read('path/to/your/production/certificate.pem')
58
+ app.sandbox_mode = true
59
+ app.save
60
+
61
+ And that's it!! You can create as many different applications as you want. RailsPushNotifications gem allows it.
62
+
63
+ ### Android Applications setup
64
+
65
+ The only thing you need is the API_KEY that you will get from the Google APIS Console > API Access
66
+
67
+ app = RailsPushNotifications::GCMApp.new
68
+ app.gcm_key = '<your app api key>'
69
+ app.save
70
+
71
+ That's all. You have your applications' configurations stored in RailsPushNotification.
72
+
73
+ ### WindowsPhone Applications setup
74
+
75
+ This case is similar but even simpler than APNS Apps
76
+
77
+ app = RailsPushNotifications::MPNSApp.new
78
+ app.cert = File.read('path/to/your/certificate.pem')
79
+ app.save
80
+
81
+ ## Creating notifications
82
+
83
+ The notifications interface is common to all kind of apps, but depending on the type of underlying app
84
+ the destinations and the data format will vary, so for example
85
+
86
+ ### Apple notifications
87
+
88
+ app = <Your Apple app>
89
+ notification = app.notifications.create(
90
+ destinations: [
91
+ 'Your first destination token',
92
+ 'Your second destination token'
93
+ ],
94
+ data: { aps: { alert: 'Hello APNS World!', sound: 'true', badge: 1 } }
95
+ )
96
+
97
+ ### GCM Notifications
98
+
99
+ app = <Your GCM app>
100
+ notification = app.notifications.create(
101
+ destinations: [
102
+ 'Your first destination token',
103
+ 'Your second destination token'
104
+ ],
105
+ data: { message: 'Hello GCM World!' }
106
+ )
107
+
108
+ ### WindowsPhone Notifications
109
+
110
+ app = <Your WindowsPhone app>
111
+ notification = app.notifications.create(
112
+ destinations: [
113
+ 'Your first destination url',
114
+ 'Your second destination url'
115
+ ],
116
+ data: { title: 'Title', message: 'Hello MPNS World!', type: :toast }
117
+ )
118
+
119
+ ## Pushing notifications
120
+
121
+ Eventually we want to actually push our notifications. For that task all the different apps
122
+ have a `push_notifications` method which will find the associated and not yet pushed notifications
123
+ and actually push them.
124
+
125
+ app = <Your app>
126
+ # Create several notifications for your app
127
+ app.push_notifications
128
+
129
+ As this task can be potentially very time consuming you will probably want to invoke it in
130
+ a background worker
131
+
132
+ And that's all!! I hope you find this gem useful and please, feel free to contribute with
133
+ any improvement/bug you may find.
134
+
135
+ ## Pending tasks
136
+
137
+ Please, feel free to contribute in building any of those or adding more stuff to this list!
138
+
139
+ * Better generation of the data for each kind of notification. Hiding the particulars
140
+ to avoid bugs.
141
+ * Rake task to push all notifications from all applications.
142
+
143
+ ## Contributing
144
+
145
+ 1. Fork it ( https://github.com/calonso/rails-push-notifications/fork )
146
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
147
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
148
+ 4. Push to the branch (`git push origin my-new-feature`)
149
+ 5. Create a new Pull Request
150
+
151
+ Released under the MIT-LICENSE.
152
+
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'bundler'
4
+ require 'appraisal'
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task :default => :spec
11
+
12
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
13
+ task :default => :appraisal
14
+ end
15
+
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Generate migrations needed to create RailsPushNotifications tables
3
+
4
+ Example:
5
+ rails generate rails_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,29 @@
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
+
23
+ create_table :rails_push_notifications_fcm_apps do |t|
24
+ t.string :fcm_key
25
+
26
+ t.timestamps null: false
27
+ end
28
+ end
29
+ 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,5 @@
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/fcm_app'
5
+ require 'rails-push-notifications/apps/mpns_app'
@@ -0,0 +1,36 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class represents an Apple iOS application.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class APNSApp < BaseApp
8
+ self.table_name = 'rails_push_notifications_apns_apps'
9
+
10
+ # Requires a development certificate
11
+ validates :apns_dev_cert, presence: true
12
+ # Requires a production certificate
13
+ validates :apns_prod_cert, presence: true
14
+
15
+ before_validation { |app| app.sandbox_mode = true if app.sandbox_mode.nil? }
16
+
17
+ private
18
+
19
+ # @return [String] the certificate(dev or prod) to use
20
+ def cert
21
+ sandbox_mode ? apns_dev_cert : apns_prod_cert
22
+ end
23
+
24
+ # @return [RubyPushNotifications::APNS::APNSPusher] configured and
25
+ # ready to push
26
+ def build_pusher
27
+ RubyPushNotifications::APNS::APNSPusher.new cert, sandbox_mode
28
+ end
29
+
30
+ # @return [RubyPushNotifications::APNS::APNSNotification]. The type of
31
+ # notifications this app manages
32
+ def notification_type
33
+ RubyPushNotifications::APNS::APNSNotification
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # Abstract. This class is the base of all application entity type.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class BaseApp < ActiveRecord::Base
8
+
9
+ self.abstract_class = true
10
+
11
+ # has_many notifications
12
+ has_many :notifications, as: :app
13
+
14
+ #
15
+ # This method will find all notifications owned by this app and
16
+ # push them.
17
+ #
18
+ def push_notifications
19
+ pending = find_pending
20
+ to_send = pending.map do |notification|
21
+ notification_type.new notification.destinations, notification.data
22
+ end
23
+ pusher = build_pusher
24
+ pusher.push to_send
25
+ pending.each_with_index do |p, i|
26
+ p.update_attributes! results: to_send[i].results
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ #
33
+ # Method that searches the owned notifications for those not yet sent
34
+ #
35
+ def find_pending
36
+ notifications.where sent: false
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class represents an Android FCM application.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class FCMApp < BaseApp
8
+ self.table_name = 'rails_push_notifications_fcm_apps'
9
+
10
+ # Requires a fcm_key
11
+ validates :fcm_key, presence: true
12
+
13
+ private
14
+
15
+ # @return [RubyPushNotifications::FCM::FCMPusher] configured and
16
+ # ready to push
17
+ def build_pusher
18
+ RubyPushNotifications::FCM::FCMPusher.new fcm_key
19
+ end
20
+
21
+ # @return [RubyPushNotifications::FCM::FCMNotification]. The type of
22
+ # notifications this app manages
23
+ def notification_type
24
+ RubyPushNotifications::FCM::FCMNotification
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class represents an Android GCM application.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class GCMApp < BaseApp
8
+ self.table_name = 'rails_push_notifications_gcm_apps'
9
+
10
+ # Requires a gcm_key
11
+ validates :gcm_key, presence: true
12
+
13
+ private
14
+
15
+ # @return [RubyPushNotifications::GCM::GCMPusher] configured and
16
+ # ready to push
17
+ def build_pusher
18
+ RubyPushNotifications::GCM::GCMPusher.new gcm_key
19
+ end
20
+
21
+ # @return [RubyPushNotifications::GCM::GCMNotification]. The type of
22
+ # notifications this app manages
23
+ def notification_type
24
+ RubyPushNotifications::GCM::GCMNotification
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class represents an Windows Phone application.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class MPNSApp < BaseApp
8
+ self.table_name = 'rails_push_notifications_mpns_apps'
9
+
10
+ # Requires the certificate
11
+ validates :cert, presence: true
12
+
13
+ private
14
+
15
+ # @return [RubyPushNotifications::MPNS::MPNSPusher] configured and
16
+ # ready to push
17
+ def build_pusher
18
+ RubyPushNotifications::MPNS::MPNSPusher.new cert
19
+ end
20
+
21
+ # @return [RubyPushNotifications::MPNS::MPNSNotification]. The type of
22
+ # notifications this app manages
23
+ def notification_type
24
+ RubyPushNotifications::MPNS::MPNSNotification
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class represents the Notification Entity.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class Notification < ActiveRecord::Base
8
+
9
+ self.table_name = 'rails_push_notifications_notifications'
10
+
11
+ # belongs_to an app
12
+ belongs_to :app, polymorphic: true
13
+
14
+ # Requires an app
15
+ validates :app, presence: true
16
+ # Requires data
17
+ validates :data, presence: true
18
+ # Requires at least one destination
19
+ validates :destinations, presence: true, length: { minimum: 1 }
20
+
21
+ serialize :data, Hash
22
+ serialize :destinations, Array
23
+ serialize :results
24
+
25
+ # Automatically manages the sent flag, setting it to true as soon as
26
+ # it is assigned results.
27
+ before_save do |record|
28
+ record.sent = !record.results.nil?
29
+ true
30
+ end
31
+
32
+ # Automatically manages the success and failed fields, setting them to the
33
+ # value specified by the results assigned.
34
+ before_save do |record|
35
+ if record.results_changed? && record.results
36
+ record.success = record.results.success
37
+ record.failed = record.results.failed
38
+ end
39
+ true
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ module RailsPushNotifications
2
+ #
3
+ # This class exports the migrations generator to Rails
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class RailsPushNotificationsRailtie < Rails::Railtie
8
+ generators do
9
+ require 'generators/rails-push-notifications/migrations_generator'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module RailsPushNotifications
2
+ VERSION = '0.2.4'
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
@@ -0,0 +1,69 @@
1
+ module RailsPushNotifications
2
+ describe GCMApp, type: :model do
3
+
4
+ it 'creates an instance given valid attributes' do
5
+ GCMApp.create! attributes_for :gcm_app
6
+ end
7
+
8
+ describe 'validations' do
9
+ let(:app) { build :gcm_app }
10
+
11
+ it 'requires a gcm key' do
12
+ app.gcm_key = nil
13
+ expect(app).to_not be_valid
14
+ end
15
+ end
16
+
17
+ describe 'notifications relationship' do
18
+ let(:app) { create :gcm_app }
19
+
20
+ it 'can create new notifications' do
21
+ expect do
22
+ app.notifications.create attributes_for :gcm_notification
23
+ end.to change { app.reload.notifications.count }.from(0).to 1
24
+ end
25
+ end
26
+
27
+ describe '#push_notifications' do
28
+ let(:app) { create :gcm_app }
29
+ let(:notifications) do
30
+ (1..10).map { create :gcm_notification, app: app }
31
+ end
32
+ let(:single_notification) { create :gcm_notification, app: app }
33
+
34
+ let(:response) { JSON.dump a: 1 }
35
+
36
+ before do
37
+ stub_request(:post, 'https://android.googleapis.com/gcm/send').
38
+ to_return status: [200, 'OK'], body: response
39
+ end
40
+
41
+ it 'assigns results' do
42
+ expect do
43
+ app.push_notifications
44
+ end.to change {
45
+ notifications.map { |n| n.reload.results }
46
+ }.from([nil] * 10).
47
+ to([RubyPushNotifications::GCM::GCMResponse.new(200, response)] * 10)
48
+ end
49
+
50
+ it 'marks as sent' do
51
+ expect do
52
+ app.push_notifications
53
+ end.to change { single_notification.reload.sent }.from(false).to true
54
+ end
55
+
56
+ context 'with an already sent notification' do
57
+ let!(:sent_notification) { create :apns_notification, app: app }
58
+
59
+ before { app.push_notifications }
60
+
61
+ it "doesn't send already sent notifications" do
62
+ expect(RubyPushNotifications::GCM::GCMConnection).
63
+ to_not receive(:post)
64
+ app.push_notifications
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,75 @@
1
+ module RailsPushNotifications
2
+ describe MPNSApp, type: :model do
3
+
4
+ it 'creates an instance given valid attributes' do
5
+ MPNSApp.create! attributes_for :mpns_app
6
+ end
7
+
8
+ describe 'validations' do
9
+
10
+ let(:app) { build :mpns_app }
11
+
12
+ it 'requires the certificate' do
13
+ app.cert = nil
14
+ expect(app).to_not be_valid
15
+ end
16
+ end
17
+
18
+ describe 'notifications relationship' do
19
+
20
+ let(:app) { create :mpns_app }
21
+
22
+ it 'can create new notifications' do
23
+ expect do
24
+ app.notifications.create attributes_for :mpns_notification
25
+ end.to change { app.reload.notifications.count }.from(0).to 1
26
+ end
27
+ end
28
+
29
+ describe '#push_notifications' do
30
+
31
+ let(:app) { create :mpns_app }
32
+ let(:notifications) {
33
+ (1..10).map { create :mpns_notification, app: app }
34
+ }
35
+ let(:single_notification) { create :mpns_notification, app: app }
36
+
37
+ let(:headers) {
38
+ {
39
+ 'x-notificationstatus' => 'Received',
40
+ 'x-deviceconnectionstatus' => 'Connected',
41
+ 'x-subscriptionstatus' => 'Active'
42
+ }
43
+ }
44
+ let(:response) { [ { device_url: 'http://s.notify.live.net/1', headers: headers, code: 200 } ]}
45
+
46
+ before do
47
+ stub_request(:post, %r{s.notify.live.net}).
48
+ to_return status: [200, 'OK'], headers: headers
49
+ end
50
+
51
+ it 'assigns results' do
52
+ expect do
53
+ app.push_notifications
54
+ end.to change { notifications.map { |n| n.reload.results } }.from([nil] * 10).to([RubyPushNotifications::MPNS::MPNSResponse.new(response)] * 10)
55
+ end
56
+
57
+ it 'marks as sent' do
58
+ expect do
59
+ app.push_notifications
60
+ end.to change { single_notification.reload.sent }.from(false).to true
61
+ end
62
+
63
+ context 'with an already sent notification' do
64
+ let!(:sent_notification) { create :mpns_notification, app: app }
65
+
66
+ before { app.push_notifications }
67
+
68
+ it "doesn't send already sent notifications" do
69
+ expect(RubyPushNotifications::MPNS::MPNSConnection).to_not receive(:post)
70
+ app.push_notifications
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,15 @@
1
+ FactoryGirl.define do
2
+ factory :apns_app, class: 'RailsPushNotifications::APNSApp' do
3
+ apns_dev_cert 'abc'
4
+ apns_prod_cert 'def'
5
+ sandbox_mode true
6
+ end
7
+
8
+ factory :gcm_app, class: 'RailsPushNotifications::GCMApp' do
9
+ gcm_key 'abc123def456'
10
+ end
11
+
12
+ factory :mpns_app, class: 'RailsPushNotifications::MPNSApp' do
13
+ cert 'abc'
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+
2
+ FactoryGirl.define do
3
+ factory :apns_notification, class: 'RailsPushNotifications::Notification' do
4
+ association :app, factory: :apns_app, strategy: :create
5
+ data a: 1
6
+ destinations ['1']
7
+ end
8
+
9
+ factory :gcm_notification, class: 'RailsPushNotifications::Notification' do
10
+ association :app, factory: :gcm_app, strategy: :create
11
+ data a: 1
12
+ destinations ['1']
13
+ end
14
+
15
+ factory :mpns_notification, class: 'RailsPushNotifications::Notification' do
16
+ association :app, factory: :mpns_app, strategy: :create
17
+ data message: { value1: 'hello' }
18
+ destinations ['http://s.notify.live.net/1']
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'generators/rails-push-notifications/migrations_generator'
2
+
3
+ describe RailsPushNotifications::Generators::MigrationsGenerator, type: :generator do
4
+ destination File.expand_path("../tmp", __FILE__)
5
+
6
+ before do
7
+ prepare_destination
8
+ run_generator
9
+ end
10
+
11
+ after do
12
+ rm_rf destination_root
13
+ end
14
+
15
+ it 'creates the migrations' do
16
+ expect(destination_root).to have_structure do
17
+ directory 'db' do
18
+ directory 'migrate' do
19
+ migration 'create_rails_push_notifications_apps'
20
+ migration 'create_rails_push_notifications_notifications'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,98 @@
1
+
2
+ module RailsPushNotifications
3
+ describe Notification, type: :model do
4
+
5
+ class FakeResults
6
+ attr_reader :value1, :success, :failed
7
+
8
+ def initialize(value1, success, failed)
9
+ @value1 = value1
10
+ @success = success
11
+ @failed = failed
12
+ end
13
+
14
+ def ==(other)
15
+ other != nil && other.is_a?(FakeResults) &&
16
+ @value1 == other.value1 && @success == other.success &&
17
+ @failed == failed || super(other)
18
+ end
19
+ end
20
+
21
+ let(:notification) { build :apns_notification }
22
+
23
+ describe 'app relationship' do
24
+ it 'requires an app' do
25
+ notification.app = nil
26
+ expect(notification).to_not be_valid
27
+ end
28
+
29
+ it 'belongs to an app' do
30
+ expect(notification.app).to be_a APNSApp
31
+ end
32
+ end
33
+
34
+ describe 'data' do
35
+ it 'requires data' do
36
+ notification.data = nil
37
+ expect(notification).to_not be_valid
38
+ end
39
+
40
+ it 'has to be a Hash' do
41
+ expect do
42
+ notification.data = 'dummy text'
43
+ notification.save
44
+ end.to raise_error ActiveRecord::SerializationTypeMismatch
45
+ end
46
+ end
47
+
48
+ describe 'destinations' do
49
+ it 'requires destinations' do
50
+ notification.destinations = nil
51
+ expect(notification).to_not be_valid
52
+ end
53
+
54
+ it 'has to be an array' do
55
+ expect do
56
+ notification.destinations = 'dummy destinations'
57
+ notification.save
58
+ end.to raise_error ActiveRecord::SerializationTypeMismatch
59
+ end
60
+
61
+ it 'has to contain at least one destination' do
62
+ notification.destinations = []
63
+ expect(notification).to_not be_valid
64
+ end
65
+ end
66
+
67
+ describe 'sent' do
68
+ it 'is false by default' do
69
+ notification.save
70
+ expect(notification.sent).to eq false
71
+ end
72
+
73
+ it 'is sent if results assigned' do
74
+ notification.save
75
+ expect do
76
+ notification.results = FakeResults.new(1, 2, 3)
77
+ notification.save
78
+ end.to change { notification.reload.sent }.from(false).to true
79
+ end
80
+ end
81
+
82
+ describe 'results' do
83
+
84
+ let(:success) { 1 }
85
+ let(:failed) { 3 }
86
+ let(:results) { FakeResults.new 'abc', success, failed }
87
+
88
+ it 'serializes any object' do
89
+ notification.results = results
90
+ notification.save
91
+ notification.reload
92
+ expect(notification.results).to eq results
93
+ expect(notification.success).to eq success
94
+ expect(notification.failed).to eq failed
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,47 @@
1
+ require 'rails'
2
+ require 'rails/all'
3
+ require 'action_view/testing/resolvers'
4
+ require 'rails/test_help'
5
+
6
+ require 'rails-push-notifications'
7
+
8
+ class RailsPushNotificationsApp < Rails::Application
9
+ config.root = File.expand_path("../../..", __FILE__)
10
+ config.cache_classes = true
11
+
12
+ config.eager_load = false
13
+ config.serve_static_assets = true
14
+ config.static_cache_control = "public, max-age=3600"
15
+
16
+ config.consider_all_requests_local = true
17
+ config.action_controller.perform_caching = false
18
+
19
+ config.action_dispatch.show_exceptions = false
20
+
21
+ config.action_controller.allow_forgery_protection = false
22
+
23
+ config.active_support.deprecation = :stderr
24
+
25
+ config.middleware.delete "Rack::Lock"
26
+ config.middleware.delete "ActionDispatch::Flash"
27
+ config.middleware.delete "ActionDispatch::BestStandardsSupport"
28
+ config.secret_token = "49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk"
29
+ routes.append do
30
+ get "/" => "welcome#index"
31
+ end
32
+ end
33
+
34
+ class ApplicationController < ActionController::Base
35
+ include Rails.application.routes.url_helpers
36
+ layout 'application'
37
+ self.view_paths = [ActionView::FixtureResolver.new(
38
+ "layouts/application.html.erb" => '<%= yield %>',
39
+ "welcome/index.html.erb"=> 'Hello from index.html.erb',
40
+ )]
41
+
42
+ def index
43
+ end
44
+
45
+ end
46
+
47
+ RailsPushNotificationsApp.initialize!
@@ -0,0 +1,47 @@
1
+ require 'rails'
2
+ require 'rails/all'
3
+ require 'action_view/testing/resolvers'
4
+
5
+ require 'rails-push-notifications'
6
+
7
+ module RailsPushNotificationsApp
8
+ class Application < Rails::Application
9
+ config.root = File.expand_path("../../..", __FILE__)
10
+ config.cache_classes = true
11
+
12
+ config.eager_load = false
13
+ config.static_cache_control = "public, max-age=3600"
14
+
15
+ config.consider_all_requests_local = true
16
+ config.action_controller.perform_caching = false
17
+
18
+ config.action_dispatch.show_exceptions = false
19
+
20
+ config.action_controller.allow_forgery_protection = false
21
+
22
+ config.active_support.deprecation = :stderr
23
+
24
+ config.middleware.delete "Rack::Lock"
25
+ config.middleware.delete "ActionDispatch::Flash"
26
+ config.middleware.delete "ActionDispatch::BestStandardsSupport"
27
+ config.secret_key_base = '49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk'
28
+ routes.append do
29
+ get "/" => "application#index"
30
+ end
31
+ end
32
+ end
33
+
34
+ class ApplicationController < ActionController::Base
35
+ include Rails.application.routes.url_helpers
36
+ layout 'application'
37
+ self.view_paths = [ActionView::FixtureResolver.new(
38
+ "layouts/application.html.erb" => '<%= yield %>',
39
+ "welcome/index.html.erb"=> 'Hello from index.html.erb',
40
+ )]
41
+
42
+ def index
43
+ end
44
+
45
+ end
46
+
47
+ RailsPushNotificationsApp::Application.initialize!
@@ -0,0 +1,61 @@
1
+
2
+ require 'codeclimate-test-reporter'
3
+ CodeClimate::TestReporter.start
4
+
5
+ Bundler.setup
6
+
7
+ require 'rails'
8
+
9
+ ENV['RAILS_ENV'] = 'test'
10
+
11
+ case "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}"
12
+ when '3.2'
13
+ ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'
14
+ require 'rails_apps/rails3_2'
15
+ require 'active_record/migration'
16
+ when '4.0'
17
+ ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'
18
+ require 'rails_apps/rails4'
19
+ when '4.1', '4.2'
20
+ ENV['DATABASE_URL'] = 'sqlite3::memory:'
21
+ require 'rails_apps/rails4'
22
+ else
23
+ raise NotImplementedError.new "Rails Push Notifications gem doesn't support Rails #{Rails.version}"
24
+ end
25
+
26
+ Bundler.require :default
27
+ Bundler.require :development
28
+
29
+ require 'webmock/rspec'
30
+ require 'rspec/rails'
31
+ require 'ruby-push-notifications'
32
+
33
+ Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
34
+
35
+ files = Dir.glob(File.join(File.dirname(__FILE__), '..', 'lib', 'generators', 'rails-push-notifications', 'templates', 'migrations', '*.rb'))
36
+
37
+ migrations = []
38
+ files.each_with_index do |file, version|
39
+ name, scope = file.scan(/([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
40
+
41
+ name = name.camelize
42
+
43
+ migrations << ActiveRecord::MigrationProxy.new(name, version, file, scope)
44
+ end
45
+
46
+ migrations.sort_by(&:version)
47
+
48
+ migrator = ActiveRecord::Migrator.new(:up, migrations)
49
+
50
+ if Rails::VERSION::MAJOR == 3
51
+ migrator.instance_variable_set(:@migrations, migrations)
52
+ end
53
+
54
+ migrator.migrate
55
+
56
+ RSpec.configure do |config|
57
+ config.use_transactional_fixtures = true
58
+ config.order = :random
59
+ end
60
+
61
+ WebMock.disable_net_connect!(:allow => "codeclimate.com")
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |config|
2
+ config.include FactoryGirl::Syntax::Methods
3
+ end
4
+
5
+ FactoryGirl.find_definitions
metadata ADDED
@@ -0,0 +1,196 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fcm-rails-push-notifications
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.4
5
+ platform: ruby
6
+ authors:
7
+ - Santhu MS
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fcm-ruby-push-notifications
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: factory_girl
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: generator_spec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.20'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.20'
125
+ description: Rails gem to push notifications for iOS, Android and Windows Phone devices
126
+ email:
127
+ - santhu.ms83@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - Rakefile
135
+ - lib/generators/rails-push-notifications/USAGE
136
+ - lib/generators/rails-push-notifications/migrations_generator.rb
137
+ - lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_apps.rb
138
+ - lib/generators/rails-push-notifications/templates/migrations/create_rails_push_notifications_notifications.rb
139
+ - lib/rails-push-notifications.rb
140
+ - lib/rails-push-notifications/apps.rb
141
+ - lib/rails-push-notifications/apps/apns_app.rb
142
+ - lib/rails-push-notifications/apps/base_app.rb
143
+ - lib/rails-push-notifications/apps/fcm_app.rb
144
+ - lib/rails-push-notifications/apps/gcm_app.rb
145
+ - lib/rails-push-notifications/apps/mpns_app.rb
146
+ - lib/rails-push-notifications/notification.rb
147
+ - lib/rails-push-notifications/rails_push_notifications_railtie.rb
148
+ - lib/rails-push-notifications/version.rb
149
+ - spec/apps/apns_app_spec.rb
150
+ - spec/apps/gcm_app_spec.rb
151
+ - spec/apps/mpns_app_spec.rb
152
+ - spec/factories/apps.rb
153
+ - spec/factories/notifications.rb
154
+ - spec/generators/migrations_generator_spec.rb
155
+ - spec/notification_spec.rb
156
+ - spec/rails_apps/rails3_2.rb
157
+ - spec/rails_apps/rails4.rb
158
+ - spec/spec_helper.rb
159
+ - spec/support/factory_girl.rb
160
+ homepage: https://github.com/santhums/rails-push-notifications
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 2.3.1
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.5.1
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Professional iOS, Android and Windows Phone push notifications for Ruby on
184
+ Rails
185
+ test_files:
186
+ - spec/apps/apns_app_spec.rb
187
+ - spec/apps/mpns_app_spec.rb
188
+ - spec/apps/gcm_app_spec.rb
189
+ - spec/support/factory_girl.rb
190
+ - spec/notification_spec.rb
191
+ - spec/spec_helper.rb
192
+ - spec/generators/migrations_generator_spec.rb
193
+ - spec/rails_apps/rails3_2.rb
194
+ - spec/rails_apps/rails4.rb
195
+ - spec/factories/apps.rb
196
+ - spec/factories/notifications.rb