notify_user 0.1.4 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|