notify_user 0.1.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/Rakefile +7 -1
- data/app/controllers/notify_user/base_notifications_controller.rb +36 -35
- data/app/mailers/notify_user/notification_mailer.rb +1 -1
- data/app/models/notify_user/apn_connection.rb +35 -17
- data/app/models/notify_user/apns.rb +124 -14
- data/app/models/notify_user/base_notification.rb +172 -35
- data/app/models/notify_user/gcm.rb +58 -0
- data/app/models/notify_user/push.rb +37 -0
- data/app/models/notify_user/unsubscribe.rb +26 -9
- data/app/models/notify_user/urban_airship.rb +1 -1
- data/app/serializers/notify_user/notification_serializer.rb +1 -4
- data/config/routes.rb +2 -2
- data/lib/generators/notify_user/aggr_interval_update/USAGE +5 -0
- data/lib/generators/notify_user/aggr_interval_update/aggr_interval_update_generator.rb +36 -0
- data/lib/generators/notify_user/aggr_interval_update/templates/add_sent_time_to_notifications.rb +10 -0
- data/lib/generators/notify_user/aggr_interval_update/templates/update_unsubscribe.rb +6 -0
- data/lib/generators/notify_user/install/install_generator.rb +2 -5
- data/lib/generators/notify_user/install/templates/create_notify_user_notifications.rb +8 -0
- data/lib/generators/notify_user/install/templates/create_notify_user_unsubscribes.rb +5 -0
- data/lib/generators/notify_user/notification/notification_generator.rb +1 -0
- data/lib/notify_user/channels/action_mailer/action_mailer_channel.rb +1 -1
- data/lib/notify_user/channels/apns/apns_channel.rb +23 -23
- data/lib/notify_user/engine.rb +0 -1
- data/lib/notify_user/version.rb +1 -1
- data/spec/controllers/notify_user/notifications_controller_spec.rb +130 -108
- data/spec/dummy/rails-4.1.0/Gemfile +1 -1
- data/spec/dummy/rails-4.1.0/app/notifications/new_post_notification.rb +2 -6
- data/spec/dummy/{rails-4.0.4/app/views/notify_user/new_post_notification/mobile_sdk/notification.html.erb → rails-4.1.0/app/views/notify_user/new_post_notification/mobile_sdk/aggregate_notifications.html.erb} +0 -0
- data/spec/dummy/rails-4.1.0/app/views/notify_user/new_post_notification/mobile_sdk/notification.html.erb +1 -0
- data/spec/dummy/rails-4.1.0/config/environments/production.rb +1 -6
- data/spec/dummy/rails-4.1.0/config/initializers/assets.rb +8 -0
- data/spec/dummy/rails-4.1.0/config/initializers/notify_user.rb +10 -0
- data/spec/dummy/rails-4.1.0/config/secrets.yml +2 -2
- data/spec/dummy/{rails-3.2.17/db/migrate/20141102231350_create_users.rb → rails-4.1.0/db/migrate/20150907004705_create_users.rb} +0 -0
- data/spec/dummy/{rails-4.0.4/db/migrate/20141102231413669669843000_create_notify_user_notifications.rb → rails-4.1.0/db/migrate/20150907004707_create_notify_user_notifications.rb} +8 -0
- data/spec/dummy/rails-4.1.0/db/migrate/{20141102231434013013078000_create_notify_user_unsubscribes.rb → 20150907004708_create_notify_user_unsubscribes.rb} +5 -0
- data/spec/dummy/{rails-3.2.17/db/migrate/20141102231353651651843000_create_notify_user_user_hashes.rb → rails-4.1.0/db/migrate/20150907004709_create_notify_user_user_hashes.rb} +0 -0
- data/spec/dummy/rails-4.1.0/db/schema.rb +12 -1
- data/spec/dummy/rails-4.1.0/log/test.log +55083 -8099
- data/spec/dummy/rails-4.1.0/test/test_helper.rb +0 -3
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/2iFf6DF21eHswaXamThRpysQhZa4HHhfXSwW0I26I_Q.cache +3 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/56gcPUZsFk_z-NC4BqxyzJ2fHeG-wa2DVNUK5iFQico.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/8DepzOh5SIwaxC825zft8xeBhAi84AFjIkQLBafpfcI.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/DFBSCnNmEyoM8roYdJ4CwzgAxnyRtTG3S2qD1ryqXxw.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/DcdyyraxoBKSYh1m6Spmfn5Qtn0V2X2t8aO3dmAi0Po.cache +3 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/IO46kchRzb_wUQ8H3D25_DHv7lZK4CxthkOj6ZKp8mQ.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/JTg9Khb4iQ4MkGt2OoYdfz6rYD9ccxMNxCOnVK92fJA.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/O8akbUwAbmIKY-AhITimq-JDQ6IgsO3Hvq2v5etufFw.cache +2 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/RJrFPSmoXCFaapGXw1bP4Lg0NLmr9qG2RMuN56UrR7U.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/TWJMA4bg5d25falxiw_TLK0GtSXWpfl0vFaFLRpkYKI.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/XjnNasrgRd0l5xx4UR4IeE-Tz1EtjiPKwmizcf59P9U.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/Y829B5qbE0t7idbBscHZCJxlb-D9S6G_8P6HwwMe7J4.cache +2 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/aXGU_8_bWOQw87gl5m_8fgEUtethSIj95P57d5YuW5k.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/cjXxcrFHyMpHUOqA38Yaft85pmxR7gZ4__TFcgJQyKA.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/cpoXvFR-s_2VM6kFm4ixeZKjW6en5HiD8B-ttj-iUSI.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/hAsOx0KPt1yL2Nh2sDOmOIWrAfoEZo1RxZQkNop9FOU.cache +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/j52KuMJNJLJcKPVLf8rk8USLswZAmy6Hi1OW35JjVTo.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/mwsAf4xggfO0hGKNns79uKcXG7B5rk8a4Zav3vXyr6M.cache +2 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/tPF6H9NRpGO3whu2y5_e_R_1EACKdCwu5437D83qJT0.cache +3 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/x5V5ICXz753PW9A8l6gpNAh_utfwyXyixuUxtMgJ2Pg.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/y8AwtETeMIB25XCoxQDwn3DmMpUFLblIFK4yb18zN4c.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/yWa5z3IIFR71QlDOGDwO5IpC9vsVUGFNfficVy0q3zo.cache +1 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/v3.0/zDCAil6tD1zhO-gqs7OG9_zEEdMVEZo_aUkZjLHP53M.cache +0 -0
- data/spec/factories/notify_user_notifications.rb +4 -7
- data/spec/factories/users.rb +7 -0
- data/spec/lib/notify_user/channels/apns_channel_spec.rb +65 -0
- data/spec/mailers/notify_user/notification_mailer_spec.rb +24 -24
- data/spec/models/notify_user/apn_connection_spec.rb +29 -0
- data/spec/models/notify_user/apns_spec.rb +96 -0
- data/spec/models/notify_user/gcm_spec.rb +70 -0
- data/spec/models/notify_user/notification_spec.rb +456 -64
- data/spec/models/notify_user/unsubscribe_spec.rb +59 -16
- data/spec/models/notify_user/user_hash_spec.rb +11 -14
- data/spec/serializers/notify_user/notification_serializer_spec.rb +24 -6
- data/spec/setup_spec.rb +3 -4
- data/spec/spec_helper.rb +20 -12
- data/spec/support/test_apn_connection.rb +8 -0
- data/spec/support/test_gcm_connection.rb +5 -0
- metadata +131 -332
- data/app/models/notify_user/houston.rb +0 -101
- data/spec/dummy/rails-3.2.17/Gemfile +0 -38
- data/spec/dummy/rails-3.2.17/README.rdoc +0 -261
- data/spec/dummy/rails-3.2.17/Rakefile +0 -7
- data/spec/dummy/rails-3.2.17/app/assets/images/rails.png +0 -0
- data/spec/dummy/rails-3.2.17/app/assets/javascripts/application.js +0 -15
- data/spec/dummy/rails-3.2.17/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/rails-3.2.17/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/rails-3.2.17/app/controllers/notify_user/notifications_controller.rb +0 -9
- data/spec/dummy/rails-3.2.17/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/rails-3.2.17/app/models/user.rb +0 -3
- data/spec/dummy/rails-3.2.17/app/notifications/new_post_notification.rb +0 -15
- data/spec/dummy/rails-3.2.17/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/rails-3.2.17/app/views/notify_user/layouts/action_mailer.html.erb +0 -39
- data/spec/dummy/rails-3.2.17/app/views/notify_user/new_post_notification/action_mailer/notification.html.erb +0 -1
- data/spec/dummy/rails-3.2.17/config.ru +0 -4
- data/spec/dummy/rails-3.2.17/config/application.rb +0 -62
- data/spec/dummy/rails-3.2.17/config/boot.rb +0 -6
- data/spec/dummy/rails-3.2.17/config/database.yml +0 -24
- data/spec/dummy/rails-3.2.17/config/environment.rb +0 -5
- data/spec/dummy/rails-3.2.17/config/environments/development.rb +0 -37
- data/spec/dummy/rails-3.2.17/config/environments/production.rb +0 -67
- data/spec/dummy/rails-3.2.17/config/environments/test.rb +0 -37
- data/spec/dummy/rails-3.2.17/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/rails-3.2.17/config/initializers/inflections.rb +0 -15
- data/spec/dummy/rails-3.2.17/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/rails-3.2.17/config/initializers/notify_user.rb +0 -14
- data/spec/dummy/rails-3.2.17/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/rails-3.2.17/config/initializers/session_store.rb +0 -8
- data/spec/dummy/rails-3.2.17/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/rails-3.2.17/config/keys/development_push.pem +0 -0
- data/spec/dummy/rails-3.2.17/config/locales/en.yml +0 -5
- data/spec/dummy/rails-3.2.17/config/routes.rb +0 -58
- data/spec/dummy/rails-3.2.17/db/migrate/20141102231353649649586000_create_notify_user_notifications.rb +0 -13
- data/spec/dummy/rails-3.2.17/db/migrate/20141102231353650650817000_create_notify_user_unsubscribes.rb +0 -10
- data/spec/dummy/rails-3.2.17/db/schema.rb +0 -50
- data/spec/dummy/rails-3.2.17/db/seeds.rb +0 -7
- data/spec/dummy/rails-3.2.17/doc/README_FOR_APP +0 -2
- data/spec/dummy/rails-3.2.17/log/test.log +0 -9574
- data/spec/dummy/rails-3.2.17/public/404.html +0 -26
- data/spec/dummy/rails-3.2.17/public/422.html +0 -26
- data/spec/dummy/rails-3.2.17/public/500.html +0 -25
- data/spec/dummy/rails-3.2.17/public/favicon.ico +0 -0
- data/spec/dummy/rails-3.2.17/public/index.html +0 -241
- data/spec/dummy/rails-3.2.17/public/robots.txt +0 -5
- data/spec/dummy/rails-3.2.17/script/rails +0 -6
- data/spec/dummy/rails-3.2.17/test/fixtures/users.yml +0 -7
- data/spec/dummy/rails-3.2.17/test/performance/browsing_test.rb +0 -12
- data/spec/dummy/rails-3.2.17/test/test_helper.rb +0 -13
- data/spec/dummy/rails-3.2.17/test/unit/user_test.rb +0 -7
- data/spec/dummy/rails-4.0.4/Gemfile +0 -45
- data/spec/dummy/rails-4.0.4/README.rdoc +0 -28
- data/spec/dummy/rails-4.0.4/Rakefile +0 -6
- data/spec/dummy/rails-4.0.4/app/assets/javascripts/application.js +0 -16
- data/spec/dummy/rails-4.0.4/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/rails-4.0.4/app/controllers/application_controller.rb +0 -5
- data/spec/dummy/rails-4.0.4/app/controllers/notify_user/notifications_controller.rb +0 -9
- data/spec/dummy/rails-4.0.4/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/rails-4.0.4/app/models/user.rb +0 -2
- data/spec/dummy/rails-4.0.4/app/notifications/new_post_notification.rb +0 -15
- data/spec/dummy/rails-4.0.4/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/rails-4.0.4/app/views/notify_user/layouts/action_mailer.html.erb +0 -39
- data/spec/dummy/rails-4.0.4/app/views/notify_user/new_post_notification/action_mailer/notification.html.erb +0 -1
- data/spec/dummy/rails-4.0.4/bin/bundle +0 -3
- data/spec/dummy/rails-4.0.4/bin/rails +0 -4
- data/spec/dummy/rails-4.0.4/bin/rake +0 -4
- data/spec/dummy/rails-4.0.4/config.ru +0 -4
- data/spec/dummy/rails-4.0.4/config/application.rb +0 -23
- data/spec/dummy/rails-4.0.4/config/boot.rb +0 -4
- data/spec/dummy/rails-4.0.4/config/database.yml +0 -24
- data/spec/dummy/rails-4.0.4/config/environment.rb +0 -5
- data/spec/dummy/rails-4.0.4/config/environments/development.rb +0 -29
- data/spec/dummy/rails-4.0.4/config/environments/production.rb +0 -80
- data/spec/dummy/rails-4.0.4/config/environments/test.rb +0 -36
- data/spec/dummy/rails-4.0.4/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/rails-4.0.4/config/initializers/filter_parameter_logging.rb +0 -4
- data/spec/dummy/rails-4.0.4/config/initializers/inflections.rb +0 -16
- data/spec/dummy/rails-4.0.4/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/rails-4.0.4/config/initializers/notify_user.rb +0 -14
- data/spec/dummy/rails-4.0.4/config/initializers/secret_token.rb +0 -12
- data/spec/dummy/rails-4.0.4/config/initializers/session_store.rb +0 -3
- data/spec/dummy/rails-4.0.4/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/rails-4.0.4/config/keys/development_push.pem +0 -136
- data/spec/dummy/rails-4.0.4/config/locales/en.yml +0 -23
- data/spec/dummy/rails-4.0.4/config/routes.rb +0 -56
- data/spec/dummy/rails-4.0.4/db/migrate/20141102231412_create_users.rb +0 -9
- data/spec/dummy/rails-4.0.4/db/migrate/20141102231413670670945000_create_notify_user_unsubscribes.rb +0 -10
- data/spec/dummy/rails-4.0.4/db/migrate/20141102231413671671735000_create_notify_user_user_hashes.rb +0 -12
- data/spec/dummy/rails-4.0.4/db/schema.rb +0 -53
- data/spec/dummy/rails-4.0.4/db/seeds.rb +0 -7
- data/spec/dummy/rails-4.0.4/log/test.log +0 -45689
- data/spec/dummy/rails-4.0.4/public/404.html +0 -58
- data/spec/dummy/rails-4.0.4/public/422.html +0 -58
- data/spec/dummy/rails-4.0.4/public/500.html +0 -57
- data/spec/dummy/rails-4.0.4/public/favicon.ico +0 -0
- data/spec/dummy/rails-4.0.4/public/robots.txt +0 -5
- data/spec/dummy/rails-4.0.4/test/fixtures/users.yml +0 -7
- data/spec/dummy/rails-4.0.4/test/models/user_test.rb +0 -7
- data/spec/dummy/rails-4.0.4/test/test_helper.rb +0 -15
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/0e8918f38b9bf3fc19fca4efda1600a1 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/1bded108b40ef13c7e07be129e2cd83c +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/1ebdd9dfe7e88e90bee37951e2da1ea2 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/23fe609acece1521082ad6b8249f96b1 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/40fd179449501b801f80c852d1635a16 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/4499e16a29d89bdf30304de41c456b43 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/49088e1598df49c63ff7d874c97e958f +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/7398a3a743326d3576cbb05a000df04b +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/8eedfb1d9aee665c9f5a77df67b64a81 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/91d718eeae4552c702b8eafd9e8bbe76 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/9e2c26ef339b5827a5c296acb284ab99 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/a4f2f445b4f8578493957e4f7b0c76e3 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/af361e697648848dbf0282eba1d15c2f +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/af95fe3d35c9fe417700e8c3625329fe +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/b89f385c2540cb58f04dbfed561d3902 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/bc1864fd5bfa875243b05071735cbb82 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/bede0f0813e15081ea4ee32b640c92c4 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/c1ba4e65491e8bb9fcdc38b22cb64aa9 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/cb741827a22668ecf975f78242076b6b +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/d3516b65bb025bc333e0f91647a7b119 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/df7c1d81da90fb0287da1e283291dc81 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/f78ea05f6019d54c3572513ebae03556 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/f7c2f64864ec1995aee8dcead45d7f24 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/rails-4.0.4/tmp/cache/assets/test/sprockets/fbbb5b21a4144f5e3efee9eda4fc38e5 +0 -0
- data/spec/dummy/rails-4.1.0/db/migrate/20141102231432_create_users.rb +0 -9
- data/spec/dummy/rails-4.1.0/db/migrate/20141102231434012012157000_create_notify_user_notifications.rb +0 -13
- data/spec/dummy/rails-4.1.0/db/migrate/20141102231434013013847000_create_notify_user_user_hashes.rb +0 -12
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/1bded108b40ef13c7e07be129e2cd83c +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/1ebdd9dfe7e88e90bee37951e2da1ea2 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/23fe609acece1521082ad6b8249f96b1 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/3fa5cc1bc06bfb81887b971eb8d36258 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/796287194e2490ae173cabd0f8e0fce7 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/8d2563dcd2d59f9e020fc2623eda3999 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/8ebc0d0c963e18c7c16e6e7f55fd379f +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/91d718eeae4552c702b8eafd9e8bbe76 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/9e2c26ef339b5827a5c296acb284ab99 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/a098882f88a8b3ca91d2efaada23dba3 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/af361e697648848dbf0282eba1d15c2f +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/b89f385c2540cb58f04dbfed561d3902 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/c1ba4e65491e8bb9fcdc38b22cb64aa9 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/cb741827a22668ecf975f78242076b6b +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/df7c1d81da90fb0287da1e283291dc81 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/f031b3342a7246183ffa1bb7819f293e +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/f78ea05f6019d54c3572513ebae03556 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/rails-4.1.0/tmp/cache/assets/test/sprockets/fbbb5b21a4144f5e3efee9eda4fc38e5 +0 -0
- data/spec/models/notify_user/houston_spec.rb +0 -33
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77798252ab441cbfca131b125e40292b701210dd
|
|
4
|
+
data.tar.gz: 5cc16d6d5c354588db35b6a0797ce61da23a669d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdb8a7efaa8c5e7af6bc9b8a84ae0189ce564c4492d25d8cb00c991a1964df787e00a392e77cea452c358e34380bc785d4638177304dd74df052a5d0ddc90d7c
|
|
7
|
+
data.tar.gz: 13e3307a5e50a15aade5b029bdf9483b3d20df4bd1b704b6057c2ba60dd30dc36433f14542520c5b0203cb485f5afe5f01a55304e19b79d601cf2f5160a1d710
|
data/README.md
CHANGED
|
@@ -141,6 +141,22 @@ Unsubscribe link helper - add this to your views/notify_user/layouts/action_mail
|
|
|
141
141
|
</p>
|
|
142
142
|
<% end %>
|
|
143
143
|
```
|
|
144
|
+
##Unsubscribing/Subscribing to a specific group_id or resource
|
|
145
|
+
Unsubscribing from a specific `group_id` eg. specific resource.
|
|
146
|
+
`put /notify_user/notifications/unsubscribe_from_object.json`
|
|
147
|
+
expects
|
|
148
|
+
```
|
|
149
|
+
subscription: {type: "NotificationType", group_id: 1, unsubscribe: true|false}
|
|
150
|
+
```
|
|
151
|
+
returns
|
|
152
|
+
`response_code 201`
|
|
153
|
+
|
|
154
|
+
##Upgrade v0.1.4 to v0.2
|
|
155
|
+
Run aggregate_interval generator which generates the migrations to add a sent_time field to notifications
|
|
156
|
+
```
|
|
157
|
+
rails generate notify_user:aggr_interval
|
|
158
|
+
rake db:migrate
|
|
159
|
+
```
|
|
144
160
|
|
|
145
161
|
##Upgrading to JSON params data type
|
|
146
162
|
Run json_update generator which generates the migrations to change the params datatype to json as well as convert the current data to json
|
data/Rakefile
CHANGED
|
@@ -3,14 +3,14 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
3
3
|
before_filter :authenticate!, :except => [:unauth_unsubscribe]
|
|
4
4
|
|
|
5
5
|
def index
|
|
6
|
-
collection
|
|
6
|
+
collection
|
|
7
7
|
respond_to_method
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def collection
|
|
11
11
|
@notifications = NotifyUser::BaseNotification.for_target(@user)
|
|
12
12
|
.order("created_at DESC")
|
|
13
|
-
.
|
|
13
|
+
.where('parent_id IS NULL')
|
|
14
14
|
collection_pagination
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -32,17 +32,33 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def mark_all
|
|
35
|
-
@notifications = NotifyUser::BaseNotification.for_target(@user).where('state
|
|
35
|
+
@notifications = NotifyUser::BaseNotification.for_target(@user).where('state != ?', 'read')
|
|
36
36
|
@notifications.update_all(state: :read)
|
|
37
|
-
|
|
37
|
+
render json: @notifications
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def notifications_count
|
|
41
|
-
@notifications = NotifyUser::BaseNotification.for_target(@user)
|
|
42
|
-
|
|
41
|
+
@notifications = NotifyUser::BaseNotification.for_target(@user)
|
|
42
|
+
.where('parent_id IS NULL')
|
|
43
|
+
.where('state IN (?)', ["sent_as_aggregation_parent", "sent", "pending"])
|
|
44
|
+
|
|
45
|
+
render json: { :count => @notifications.count }
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
def unsubscribe_from_object
|
|
49
|
+
case params[:subscription][:unsubscribe]
|
|
50
|
+
when true
|
|
51
|
+
NotifyUser::Unsubscribe.unsubscribe(@user, params[:subscription][:type], params[:subscription][:group_id])
|
|
52
|
+
when false
|
|
53
|
+
NotifyUser::Unsubscribe.subscribe(@user, params[:subscription][:type], params[:subscription][:group_id])
|
|
54
|
+
else
|
|
55
|
+
raise "unsubscribe field required"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
render json: {status: "OK"}, status: 201
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#get
|
|
46
62
|
def read
|
|
47
63
|
@notification = NotifyUser::BaseNotification.for_target(@user).where('id = ?', params[:id]).first
|
|
48
64
|
unless @notification.read?
|
|
@@ -58,8 +74,8 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
58
74
|
|
|
59
75
|
def unsubscribe
|
|
60
76
|
if params[:type]
|
|
61
|
-
|
|
62
|
-
redirect_to notify_user_notifications_unsubscribe_path
|
|
77
|
+
NotifyUser::Unsubscribe.unsubscribe(@user, params[:type])
|
|
78
|
+
redirect_to notify_user_notifications_unsubscribe_path
|
|
63
79
|
end
|
|
64
80
|
@types = build_notification_types
|
|
65
81
|
@unsubscribale_types = NotifyUser.unsubscribable_notifications
|
|
@@ -82,28 +98,25 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
82
98
|
NotifyUser::Unsubscribe.unsubscribe(@user,type[:type])
|
|
83
99
|
else
|
|
84
100
|
if unsubscribe.empty?
|
|
85
|
-
#if unsubscribe doesn't exist create it
|
|
101
|
+
#if unsubscribe doesn't exist create it
|
|
86
102
|
unsubscribe = NotifyUser::Unsubscribe.create(target: @user, type: type[:type])
|
|
87
103
|
end
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
flash[:message] = "Successfully updated your notifcation settings"
|
|
91
|
-
end
|
|
92
|
-
redirect_to notify_user_notifications_unsubscribe_path
|
|
107
|
+
end
|
|
108
|
+
redirect_to notify_user_notifications_unsubscribe_path
|
|
93
109
|
end
|
|
94
110
|
|
|
95
111
|
def update_subscriptions(types)
|
|
96
112
|
types.each do |type|
|
|
97
113
|
unsubscribe = NotifyUser::Unsubscribe.has_unsubscribed_from(@user, type[:type])
|
|
98
114
|
if type[:status] == '0'
|
|
99
|
-
|
|
100
|
-
#if unsubscribe doesn't exist create it
|
|
101
|
-
unsubscribe = NotifyUser::Unsubscribe.create(target: @user, type: type[:type])
|
|
102
|
-
end
|
|
115
|
+
NotifyUser::Unsubscribe.unsubscribe(@user, type[:type])
|
|
103
116
|
else
|
|
104
|
-
|
|
117
|
+
NotifyUser::Unsubscribe.subscribe(@user, type[:type])
|
|
105
118
|
end
|
|
106
|
-
end
|
|
119
|
+
end
|
|
107
120
|
end
|
|
108
121
|
|
|
109
122
|
def unauth_unsubscribe
|
|
@@ -122,7 +135,7 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
122
135
|
end
|
|
123
136
|
|
|
124
137
|
def subscribe
|
|
125
|
-
|
|
138
|
+
NotifyUser::Unsubscribe.subscribe(@user, params[:type]) if params[:type]
|
|
126
139
|
redirect_to notify_user_notifications_unsubscribe_path
|
|
127
140
|
end
|
|
128
141
|
|
|
@@ -141,6 +154,7 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
141
154
|
end
|
|
142
155
|
|
|
143
156
|
private
|
|
157
|
+
|
|
144
158
|
def build_notification_types()
|
|
145
159
|
#dirty way to build a json hash with pagination
|
|
146
160
|
types = {:subscriptions => []}
|
|
@@ -151,28 +165,15 @@ class NotifyUser::BaseNotificationsController < ApplicationController
|
|
|
151
165
|
NotifyUser::BaseNotification.channels.each do |type, options|
|
|
152
166
|
channel = (type.to_s + "_channel").camelize.constantize
|
|
153
167
|
types[:subscriptions] << {type: type, description: channel.default_options[:description],
|
|
154
|
-
status: NotifyUser::Unsubscribe.has_unsubscribed_from(@user, type)
|
|
155
|
-
end
|
|
168
|
+
status: NotifyUser::Unsubscribe.has_unsubscribed_from?(@user, type)}
|
|
169
|
+
end
|
|
156
170
|
|
|
157
171
|
#iterates over type
|
|
158
172
|
notification_types.each do |type|
|
|
159
173
|
types[:subscriptions] << {type: type, description: type.constantize.description,
|
|
160
|
-
status: NotifyUser::Unsubscribe.has_unsubscribed_from(@user, type)
|
|
174
|
+
status: NotifyUser::Unsubscribe.has_unsubscribed_from?(@user, type)}
|
|
161
175
|
end
|
|
162
176
|
return types
|
|
163
177
|
end
|
|
164
178
|
|
|
165
|
-
def unsubscribe_from(type)
|
|
166
|
-
unsubscribe = NotifyUser::Unsubscribe.new(target: @user, type: type)
|
|
167
|
-
if unsubscribe.save
|
|
168
|
-
flash[:message] = "successfully unsubscribed from #{type} notifications"
|
|
169
|
-
else
|
|
170
|
-
flash[:message] = "Please try again"
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def subscribe_to(type)
|
|
175
|
-
NotifyUser::Unsubscribe.unsubscribe(@user,type)
|
|
176
|
-
flash[:message] = "successfully subscribed to #{type} notifications"
|
|
177
|
-
end
|
|
178
179
|
end
|
|
@@ -1,43 +1,61 @@
|
|
|
1
1
|
module NotifyUser
|
|
2
2
|
class APNConnection
|
|
3
3
|
|
|
4
|
-
attr_accessor :connection
|
|
5
|
-
|
|
6
4
|
def initialize
|
|
7
|
-
|
|
5
|
+
connection
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def connection
|
|
9
|
+
@connection ||= setup_connection
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def write(data)
|
|
13
|
+
raise "Connection is closed" unless @connection.open?
|
|
14
|
+
@connection.write(data)
|
|
8
15
|
end
|
|
9
16
|
|
|
10
|
-
def
|
|
17
|
+
def reset
|
|
18
|
+
@connection.close if @connection
|
|
19
|
+
@connection = nil
|
|
20
|
+
connection
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def apn_environment
|
|
26
|
+
return nil unless ENV['APN_ENVIRONMENT']
|
|
27
|
+
|
|
28
|
+
ENV['APN_ENVIRONMENT'].downcase.to_sym
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def setup_connection
|
|
32
|
+
return if Rails.env.test?
|
|
33
|
+
|
|
11
34
|
@uri, @certificate = if Rails.env.development? || apn_environment == :development
|
|
12
35
|
Rails.logger.info "Using development gateway. Rails env: #{Rails.env}, APN_ENVIRONMENT: #{apn_environment}"
|
|
13
36
|
[
|
|
14
37
|
::Houston::APPLE_DEVELOPMENT_GATEWAY_URI,
|
|
15
|
-
File.read(
|
|
38
|
+
File.read(development_certificate)
|
|
16
39
|
]
|
|
17
40
|
else
|
|
18
41
|
Rails.logger.info "Using production gateway. Rails env: #{Rails.env}, APN_ENVIRONMENT: #{apn_environment}"
|
|
19
42
|
[
|
|
20
43
|
::Houston::APPLE_PRODUCTION_GATEWAY_URI,
|
|
21
|
-
File.read(
|
|
44
|
+
File.read(production_certificate)
|
|
22
45
|
]
|
|
23
46
|
end
|
|
24
47
|
|
|
25
48
|
@connection = ::Houston::Connection.new(@uri, @certificate, nil)
|
|
26
|
-
@connection.open
|
|
27
49
|
end
|
|
28
50
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
def development_certificate
|
|
52
|
+
file_path = ENV['APN_DEVELOPMENT_PATH'] || 'config/keys/development_push.pem'
|
|
53
|
+
"#{Rails.root}/#{file_path}"
|
|
32
54
|
end
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return nil unless ENV['APN_ENVIRONMENT']
|
|
38
|
-
|
|
39
|
-
ENV['APN_ENVIRONMENT'].downcase.to_sym
|
|
56
|
+
def production_certificate
|
|
57
|
+
file_path = ENV['APN_PRODUCTION_PATH'] || "config/keys/production_push.pem"
|
|
58
|
+
"#{Rails.root}/#{file_path}"
|
|
40
59
|
end
|
|
41
|
-
|
|
42
60
|
end
|
|
43
61
|
end
|
|
@@ -1,28 +1,138 @@
|
|
|
1
|
+
require_relative 'apn_connection'
|
|
2
|
+
require 'houston'
|
|
3
|
+
|
|
1
4
|
module NotifyUser
|
|
2
|
-
class Apns
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
class Apns < Push
|
|
6
|
+
NO_ERROR = -42
|
|
7
|
+
INVALID_TOKEN_ERROR = 8
|
|
8
|
+
CONNECTION = APNConnection.new
|
|
9
|
+
|
|
10
|
+
attr_accessor :push_options
|
|
5
11
|
|
|
6
|
-
def initialize(
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
def initialize(notifications, devices, options)
|
|
13
|
+
super(notifications, devices, options)
|
|
14
|
+
|
|
15
|
+
@push_options = setup_options
|
|
16
|
+
@devices = devices
|
|
9
17
|
end
|
|
10
18
|
|
|
11
|
-
# Sends push notification:
|
|
12
19
|
def push
|
|
13
|
-
|
|
20
|
+
send_notifications
|
|
14
21
|
end
|
|
15
22
|
|
|
16
23
|
private
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
attr_accessor :devices
|
|
26
|
+
|
|
27
|
+
def connection
|
|
28
|
+
CONNECTION.connection
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def reset_connection
|
|
32
|
+
CONNECTION.reset
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def setup_options
|
|
36
|
+
space_allowance = PAYLOAD_LIMIT - used_space
|
|
37
|
+
|
|
38
|
+
mobile_message = ''
|
|
39
|
+
if @notification.parent_id
|
|
40
|
+
parent = @notification.class.find(@notification.parent_id)
|
|
41
|
+
mobile_message = parent.mobile_message(space_allowance)
|
|
42
|
+
else
|
|
43
|
+
mobile_message = @notification.mobile_message(space_allowance)
|
|
44
|
+
end
|
|
45
|
+
mobile_message.gsub!('\n', "\n")
|
|
46
|
+
|
|
47
|
+
push_options = {
|
|
48
|
+
alert: mobile_message,
|
|
49
|
+
badge: @notification.count_for_target,
|
|
50
|
+
category: @notification.params[:category] || @notification.type,
|
|
51
|
+
custom_data: @notification.params,
|
|
52
|
+
sound: @options[:sound] || 'default'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if @options[:silent]
|
|
56
|
+
push_options.merge!({
|
|
57
|
+
alert: '',
|
|
58
|
+
sound: '',
|
|
59
|
+
content_available: true
|
|
60
|
+
}).delete(:badge)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
push_options
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def valid?(payload)
|
|
67
|
+
payload.to_json.bytesize <= PAYLOAD_LIMIT
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def send_notifications
|
|
71
|
+
connection.open if connection.closed?
|
|
72
|
+
|
|
73
|
+
Rails.logger.info "PAYLOAD"
|
|
74
|
+
Rails.logger.info "----"
|
|
75
|
+
Rails.logger.info "#{@push_options}"
|
|
76
|
+
|
|
77
|
+
unless valid?(@push_options)
|
|
78
|
+
Rails.logger.info "Error: Payload exceeds size limit."
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
devices.each_with_index do |device, index|
|
|
82
|
+
notification = ::Houston::Notification.new(@push_options.dup.merge({ token: device.token, id: index }))
|
|
83
|
+
connection.write(notification.message)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
error_index = io_errors
|
|
87
|
+
|
|
88
|
+
if error_index == NO_ERROR
|
|
89
|
+
return true
|
|
90
|
+
else
|
|
91
|
+
# Resend all notifications after the once that produced the error:
|
|
92
|
+
send_notifications
|
|
93
|
+
end
|
|
94
|
+
rescue OpenSSL::SSL::SSLError, Errno::EPIPE, Errno::ETIMEDOUT => e
|
|
95
|
+
Rails.logger.error "[##{connection.object_id}] Exception occurred: #{e.inspect}."
|
|
96
|
+
reset_connection
|
|
97
|
+
Rails.logger.debug "[##{connection.object_id}] Socket reestablished."
|
|
98
|
+
retry
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def io_errors
|
|
102
|
+
error_index = NO_ERROR
|
|
103
|
+
ssl = connection.ssl
|
|
104
|
+
|
|
105
|
+
Rails.logger.info "READING ERRORS"
|
|
106
|
+
Rails.logger.info "----"
|
|
107
|
+
read_socket, write_socket = IO.select([ssl], [], [ssl], 1)
|
|
108
|
+
Rails.logger.info "#{ssl}"
|
|
109
|
+
|
|
110
|
+
if (read_socket && read_socket[0])
|
|
111
|
+
error = connection.read(6)
|
|
112
|
+
|
|
113
|
+
Rails.logger.info "#{error}"
|
|
114
|
+
|
|
115
|
+
if error
|
|
116
|
+
command, status, error_index = error.unpack("ccN")
|
|
117
|
+
|
|
118
|
+
Rails.logger.info "Error: #{status} with id: #{error_index}."
|
|
119
|
+
|
|
120
|
+
# Remove all the devices prior to the error (we assume they were successful), and close the current connection:
|
|
121
|
+
if error_index != NO_ERROR
|
|
122
|
+
device = devices.at(error_index)
|
|
22
123
|
|
|
23
|
-
|
|
124
|
+
# If we encounter the Invalid Token error from APNS, just remove the device:
|
|
125
|
+
if status == INVALID_TOKEN_ERROR
|
|
126
|
+
Rails.logger.info "Invalid token encountered, removing device. Token: #{device.token}."
|
|
127
|
+
device.destroy
|
|
128
|
+
end
|
|
24
129
|
|
|
25
|
-
|
|
130
|
+
devices.slice!(0..error_index)
|
|
131
|
+
reset_connection
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
return error_index
|
|
26
136
|
end
|
|
27
137
|
end
|
|
28
138
|
end
|
|
@@ -11,7 +11,7 @@ module NotifyUser
|
|
|
11
11
|
after_commit :deliver, on: :create
|
|
12
12
|
|
|
13
13
|
if ActiveRecord::VERSION::MAJOR < 4
|
|
14
|
-
attr_accessible :params, :target, :type, :state
|
|
14
|
+
attr_accessible :params, :target, :type, :state, :group_id, :parent_id
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Override point in case of collisions, plus keeps the table name tidy.
|
|
@@ -29,6 +29,7 @@ module NotifyUser
|
|
|
29
29
|
belongs_to :target, polymorphic: true
|
|
30
30
|
|
|
31
31
|
validates_presence_of :target_id, :target_type, :target, :type, :state
|
|
32
|
+
validate :presence_of_group_id
|
|
32
33
|
|
|
33
34
|
aasm column: :state do
|
|
34
35
|
|
|
@@ -41,12 +42,25 @@ module NotifyUser
|
|
|
41
42
|
# Email/SMS/APNS has been sent.
|
|
42
43
|
state :sent
|
|
43
44
|
|
|
45
|
+
# Identifies which notification within the aggregation window that was actually delayed
|
|
46
|
+
state :sent_as_aggregation_parent
|
|
47
|
+
state :pending_as_aggregation_parent
|
|
48
|
+
|
|
44
49
|
# The user has seen this notification.
|
|
45
50
|
state :read
|
|
46
51
|
|
|
47
52
|
# Record that we have sent message(s) to the user about this notification.
|
|
48
53
|
event :mark_as_sent do
|
|
49
|
-
transitions from: [:
|
|
54
|
+
transitions from: [:pending_as_aggregation_parent], to: :sent_as_aggregation_parent, :if => :pending_as_aggregation_parent?
|
|
55
|
+
transitions from: [:pending, :pending_no_aggregation], to: :sent, :unless => :pending_as_aggregation_parent?
|
|
56
|
+
after do
|
|
57
|
+
self.sent_time = Time.now
|
|
58
|
+
self.save
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
event :mark_as_pending_as_aggregation_parent do
|
|
63
|
+
transitions from: [:pending], to: :pending_as_aggregation_parent
|
|
50
64
|
end
|
|
51
65
|
|
|
52
66
|
event :dont_aggregate do
|
|
@@ -57,7 +71,7 @@ module NotifyUser
|
|
|
57
71
|
# A notification can go straight from pending to read if it's seen in a view before
|
|
58
72
|
# sent in an email.
|
|
59
73
|
event :mark_as_read do
|
|
60
|
-
transitions from: [:pending, :sent], to: :read
|
|
74
|
+
transitions from: [:pending, :sent, :pending_as_aggregation_parent, :sent_as_aggregation_parent], to: :read
|
|
61
75
|
end
|
|
62
76
|
end
|
|
63
77
|
|
|
@@ -71,23 +85,35 @@ module NotifyUser
|
|
|
71
85
|
|
|
72
86
|
# returns the global unread notification count for a user
|
|
73
87
|
def count_for_target
|
|
74
|
-
NotifyUser::BaseNotification.for_target(target)
|
|
88
|
+
NotifyUser::BaseNotification.for_target(target)
|
|
89
|
+
.where('parent_id IS NULL')
|
|
90
|
+
.where('state IN (?)', ["sent_as_aggregation_parent", "sent", "pending"])
|
|
91
|
+
.count
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.aggregate_message(notifications)
|
|
95
|
+
string = ActionView::Base.new(
|
|
96
|
+
ActionController::Base.view_paths).render(
|
|
97
|
+
:template => self.class.views[:mobile_sdk][:aggregate_path].call(self), :formats => [:html],
|
|
98
|
+
:locals => { :notifications => notifications})
|
|
99
|
+
|
|
100
|
+
return ::CGI.unescapeHTML("#{string}")
|
|
75
101
|
end
|
|
76
102
|
|
|
77
103
|
def message
|
|
78
104
|
string = ActionView::Base.new(
|
|
79
|
-
|
|
105
|
+
ActionController::Base.view_paths).render(
|
|
80
106
|
:template => self.class.views[:mobile_sdk][:template_path].call(self), :formats => [:html],
|
|
81
|
-
:locals => { :params => self.params})
|
|
107
|
+
:locals => { :params => self.params, :notification => self})
|
|
82
108
|
|
|
83
109
|
return ::CGI.unescapeHTML("#{string}")
|
|
84
110
|
end
|
|
85
111
|
|
|
86
112
|
def mobile_message(length=115)
|
|
87
113
|
string = truncate(ActionView::Base.new(
|
|
88
|
-
|
|
114
|
+
ActionController::Base.view_paths).render(
|
|
89
115
|
:template => self.class.views[:mobile_sdk][:template_path].call(self), :formats => [:html],
|
|
90
|
-
:locals => { :params => self.params}), :length => length)
|
|
116
|
+
:locals => { :params => self.params, :notification => self}), :length => length)
|
|
91
117
|
|
|
92
118
|
return ::CGI.unescapeHTML("#{string}")
|
|
93
119
|
end
|
|
@@ -103,15 +129,33 @@ module NotifyUser
|
|
|
103
129
|
self
|
|
104
130
|
end
|
|
105
131
|
|
|
132
|
+
def grouped_by_id(group_id)
|
|
133
|
+
self.group_id = group_id
|
|
134
|
+
self
|
|
135
|
+
end
|
|
136
|
+
|
|
106
137
|
def notify!
|
|
107
138
|
# Bang version of 'notify' ignores aggregation
|
|
108
139
|
dont_aggregate!
|
|
109
140
|
end
|
|
110
141
|
|
|
111
142
|
# Send any Emails/SMS/APNS
|
|
112
|
-
def notify
|
|
143
|
+
def notify(deliver=true)
|
|
144
|
+
#All notifications except the notification at interval 0 should have there parent_id set
|
|
145
|
+
if self.aggregate_grouping
|
|
146
|
+
parents = current_parents.where(parent_id: nil).where('created_at >= ?', 24.hours.ago).order('created_at DESC')
|
|
147
|
+
|
|
148
|
+
if parents.any?
|
|
149
|
+
self.parent_id = parents.first.id
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
113
153
|
# Sends with aggregation if enabled
|
|
114
154
|
save
|
|
155
|
+
|
|
156
|
+
## if deliver == false don't perform deliver log but still perform aggregation logic
|
|
157
|
+
## notification then gets marked as sent
|
|
158
|
+
mark_as_sent! unless deliver
|
|
115
159
|
end
|
|
116
160
|
|
|
117
161
|
def generate_unsubscribe_hash
|
|
@@ -119,6 +163,25 @@ module NotifyUser
|
|
|
119
163
|
return NotifyUser::UserHash.where(target_id: self.target.id).where(target_type: self.target.class.base_class).where(type: self.type).where(active: true).first || NotifyUser::UserHash.create(target: self.target, type: self.type, active: true)
|
|
120
164
|
end
|
|
121
165
|
|
|
166
|
+
def aggregation_interval
|
|
167
|
+
pending_and_sent_aggregation_parents.count
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def delay_time(options)
|
|
171
|
+
a_interval = options[:aggregate_per][aggregation_interval]
|
|
172
|
+
|
|
173
|
+
# uses the last interval by default once we deplete the intervals
|
|
174
|
+
a_interval = options[:aggregate_per].last if a_interval.nil?
|
|
175
|
+
|
|
176
|
+
# last sent notification
|
|
177
|
+
last_sent_parent = sent_aggregation_parents.first
|
|
178
|
+
# Uses the time of the last notification sent otherwise will send it now.
|
|
179
|
+
delay_time = last_sent_parent ? last_sent_parent.sent_time : created_at
|
|
180
|
+
|
|
181
|
+
# If this is the first notification the aggregate interval will return 0. Thus sending the notification now!
|
|
182
|
+
return delay_time + a_interval.minutes
|
|
183
|
+
end
|
|
184
|
+
|
|
122
185
|
## Notification description
|
|
123
186
|
class_attribute :description
|
|
124
187
|
self.description = ""
|
|
@@ -128,12 +191,23 @@ module NotifyUser
|
|
|
128
191
|
self.channels = {
|
|
129
192
|
}
|
|
130
193
|
|
|
194
|
+
## Aggregation
|
|
195
|
+
|
|
196
|
+
class_attribute :aggregate_per
|
|
197
|
+
self.aggregate_per = 1.minute
|
|
198
|
+
|
|
199
|
+
## True will implement a grouping/aggregation algorithm so that even though 10 notifications are delivered eg. Push Notifications
|
|
200
|
+
## Only 1 notification will be displayed to the user within the notification.json payload
|
|
201
|
+
class_attribute :aggregate_grouping
|
|
202
|
+
self.aggregate_grouping = false
|
|
203
|
+
|
|
131
204
|
# Not sure about this. The JSON and web feeds don't fit into channels, because nothing is broadcast through
|
|
132
205
|
# them. Not sure if they really need another concept though, they could just be formats on the controller.
|
|
133
206
|
class_attribute :views
|
|
134
207
|
self.views = {
|
|
135
208
|
mobile_sdk: {
|
|
136
|
-
template_path: Proc.new {|n| "notify_user/#{n.class.name.underscore}/mobile_sdk/notification" }
|
|
209
|
+
template_path: Proc.new {|n| "notify_user/#{n.class.name.underscore}/mobile_sdk/notification" },
|
|
210
|
+
aggregate_path: Proc.new {|n| "notify_user/#{n.class.name.underscore}/mobile_sdk/aggregate_notifications" }
|
|
137
211
|
}
|
|
138
212
|
}
|
|
139
213
|
|
|
@@ -144,11 +218,6 @@ module NotifyUser
|
|
|
144
218
|
self.channels = channels_clone
|
|
145
219
|
end
|
|
146
220
|
|
|
147
|
-
## Aggregation
|
|
148
|
-
|
|
149
|
-
class_attribute :aggregate_per
|
|
150
|
-
self.aggregate_per = 1.minute
|
|
151
|
-
|
|
152
221
|
## Sending
|
|
153
222
|
|
|
154
223
|
def self.for_target(target)
|
|
@@ -156,31 +225,91 @@ module NotifyUser
|
|
|
156
225
|
.where(target_type: target.class.base_class)
|
|
157
226
|
end
|
|
158
227
|
|
|
228
|
+
# Returns all parent notifications with a given group_id
|
|
229
|
+
def current_parents
|
|
230
|
+
self.class
|
|
231
|
+
.for_target(self.target)
|
|
232
|
+
.where(group_id: group_id)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def aggregation_parents
|
|
236
|
+
current_parents
|
|
237
|
+
.where('id != ?', id)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def sent_aggregation_parents
|
|
241
|
+
aggregation_parents
|
|
242
|
+
.where(state: :sent_as_aggregation_parent)
|
|
243
|
+
.order('created_at DESC')
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def pending_and_sent_aggregation_parents
|
|
247
|
+
aggregation_parents
|
|
248
|
+
.where(state: [:sent_as_aggregation_parent, :pending_as_aggregation_parent])
|
|
249
|
+
.order('created_at DESC')
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Used for aggregation when grouping isn't enabled
|
|
253
|
+
def self.pending_aggregations_marked_as_parent(notification)
|
|
254
|
+
where(type: notification.type)
|
|
255
|
+
.for_target(notification.target)
|
|
256
|
+
.where(state: :pending_as_aggregation_parent)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Used for aggregation when grouping based on group_id for target
|
|
260
|
+
def self.pending_aggregations_grouped_marked_as_parent(notification)
|
|
261
|
+
where(type: notification.type)
|
|
262
|
+
.for_target(notification.target)
|
|
263
|
+
.where(state: :pending_as_aggregation_parent)
|
|
264
|
+
.where(group_id: notification.group_id)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Used to find all pending notifications with aggregation enabled for target
|
|
268
|
+
def self.pending_aggregation_by_group_with(notification)
|
|
269
|
+
for_target(notification.target)
|
|
270
|
+
.where(state: [:pending, :pending_as_aggregation_parent])
|
|
271
|
+
.where(group_id: notification.group_id)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Used to find all pending notifications for target
|
|
159
275
|
def self.pending_aggregation_with(notification)
|
|
160
276
|
where(type: notification.type)
|
|
161
277
|
.for_target(notification.target)
|
|
162
|
-
.where(state: :pending)
|
|
278
|
+
.where(state: [:pending, :pending_as_aggregation_parent])
|
|
163
279
|
end
|
|
164
280
|
|
|
165
281
|
def aggregation_pending?
|
|
166
282
|
# A notification of the same type, that would have an aggregation job associated with it,
|
|
167
283
|
# already exists.
|
|
168
|
-
|
|
284
|
+
|
|
285
|
+
# When group aggregation is enabled we provide a different scope
|
|
286
|
+
if self.aggregate_grouping
|
|
287
|
+
return (self.class.pending_aggregations_grouped_marked_as_parent(self).where('id != ?', id).count > 0)
|
|
288
|
+
else
|
|
289
|
+
return (self.class.pending_aggregations_marked_as_parent(self).where('id != ?', id).count > 0)
|
|
290
|
+
end
|
|
169
291
|
end
|
|
170
292
|
|
|
171
293
|
# Aggregates appropriately
|
|
172
294
|
def deliver
|
|
173
295
|
if pending? and not user_has_unsubscribed?
|
|
174
|
-
self.mark_as_sent!
|
|
175
|
-
|
|
176
296
|
# if aggregation is false bypass aggregation completely
|
|
177
297
|
self.channels.each do |channel_name, options|
|
|
178
298
|
if(options[:aggregate_per] == false)
|
|
299
|
+
self.mark_as_sent!
|
|
179
300
|
self.class.delay.deliver_notification_channel(self.id, channel_name)
|
|
180
301
|
else
|
|
181
|
-
# only notifies channels if no pending
|
|
182
|
-
|
|
183
|
-
self.
|
|
302
|
+
# only notifies channels if no pending aggregate notifications
|
|
303
|
+
unless aggregation_pending?
|
|
304
|
+
self.mark_as_pending_as_aggregation_parent!
|
|
305
|
+
|
|
306
|
+
# adds fallback support for integer or array of integers
|
|
307
|
+
if options[:aggregate_per].kind_of?(Array)
|
|
308
|
+
self.class.delay_until(delay_time(options)).notify_aggregated_channel(self.id, channel_name)
|
|
309
|
+
else
|
|
310
|
+
a_interval = options[:aggregate_per] ? options[:aggregate_per].minutes : self.aggregate_per
|
|
311
|
+
self.class.delay_for(a_interval).notify_aggregated_channel(self.id, channel_name)
|
|
312
|
+
end
|
|
184
313
|
end
|
|
185
314
|
end
|
|
186
315
|
end
|
|
@@ -221,7 +350,7 @@ module NotifyUser
|
|
|
221
350
|
channel_options = channels[channel_name.to_sym]
|
|
222
351
|
channel = (channel_name.to_s + "_channel").camelize.constantize
|
|
223
352
|
|
|
224
|
-
unless
|
|
353
|
+
unless notification.user_has_unsubscribed?(channel_name)
|
|
225
354
|
channel.deliver(notification, channel_options)
|
|
226
355
|
end
|
|
227
356
|
end
|
|
@@ -232,7 +361,7 @@ module NotifyUser
|
|
|
232
361
|
channel = (channel_name.to_s + "_channel").camelize.constantize
|
|
233
362
|
|
|
234
363
|
#check if user unsubsribed from channel type
|
|
235
|
-
unless
|
|
364
|
+
unless notifications.first.user_has_unsubscribed?(channel_name)
|
|
236
365
|
channel.deliver_aggregated(notifications, channel_options)
|
|
237
366
|
end
|
|
238
367
|
end
|
|
@@ -242,12 +371,18 @@ module NotifyUser
|
|
|
242
371
|
notification = self.find(notification_id) # Raise an exception if not found.
|
|
243
372
|
|
|
244
373
|
# Find any pending notifications with the same type and target, which can all be sent in one message.
|
|
245
|
-
|
|
374
|
+
if self.aggregate_grouping
|
|
375
|
+
notifications = self.pending_aggregation_by_group_with(notification)
|
|
376
|
+
else
|
|
377
|
+
notifications = self.pending_aggregation_with(notification)
|
|
378
|
+
end
|
|
246
379
|
|
|
247
380
|
notifications.map(&:mark_as_sent)
|
|
248
381
|
notifications.map(&:save)
|
|
249
382
|
|
|
250
|
-
|
|
383
|
+
# If the notification has been marked as read before it's sent we don't want to send it.
|
|
384
|
+
return if notification.read? || notifications.empty?
|
|
385
|
+
|
|
251
386
|
if notifications.length == 1
|
|
252
387
|
# Despite waiting for more to aggregate, we only got one in the end.
|
|
253
388
|
self.deliver_notification_channel(notifications.first.id, channel_name)
|
|
@@ -257,24 +392,26 @@ module NotifyUser
|
|
|
257
392
|
end
|
|
258
393
|
end
|
|
259
394
|
|
|
395
|
+
def user_has_unsubscribed?(channel_name=nil)
|
|
396
|
+
#return true if user has unsubscribed
|
|
397
|
+
return Unsubscribe.has_unsubscribed_from?(self.target, self.type, self.group_id, channel_name)
|
|
398
|
+
end
|
|
399
|
+
|
|
260
400
|
private
|
|
261
401
|
|
|
262
|
-
def
|
|
263
|
-
|
|
402
|
+
def presence_of_group_id
|
|
403
|
+
if self.aggregate_grouping && group_id.blank?
|
|
404
|
+
errors.add(:group_id, "required when aggregate_grouping is set to true")
|
|
405
|
+
end
|
|
264
406
|
end
|
|
265
407
|
|
|
266
|
-
def
|
|
267
|
-
|
|
268
|
-
return true unless NotifyUser::Unsubscribe.has_unsubscribed_from(self.target, self.type).empty?
|
|
269
|
-
|
|
270
|
-
return false
|
|
408
|
+
def unsubscribed_validation
|
|
409
|
+
errors.add(:target, (" has unsubscribed from this type")) if user_has_unsubscribed?
|
|
271
410
|
end
|
|
272
411
|
|
|
273
412
|
def self.unsubscribed_from_channel?(user, type)
|
|
274
413
|
#return true if user has unsubscribed
|
|
275
414
|
return !NotifyUser::Unsubscribe.has_unsubscribed_from(user, type).empty?
|
|
276
415
|
end
|
|
277
|
-
|
|
278
|
-
|
|
279
416
|
end
|
|
280
417
|
end
|