feste 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 780ca4e3f5539ccc209639ad6c74c73b13c273a5a7caf22b1775c36339606445
4
+ data.tar.gz: a4b6d5c3ffbc86aaf53d08c5ee767121a66b26d4959b1036b35d3c90ce8338f6
5
+ SHA512:
6
+ metadata.gz: 8bb1dcf62ce31f4594b065cfd9ac5c8e4fc0df1fa7c83a345a2bf363432652469f84963f92b90fffe371ab429c760af824747889cf08601d45fade0b3b82f76f
7
+ data.tar.gz: 264e8a84f385e74f8f5cc0333546b6f2fa90b6bd30dab3ab72b0354ef8606124642bba0b314a73ccbdd7604d11adf0e999dd6190e4f8adaf6463fae811be0aa6
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .byebug_history
11
+ /*.gem
12
+
13
+ # rspec failure tracking
14
+ .rspec_status
15
+
16
+ # dummy app
17
+ /spec/dummy/.bundle
18
+ /spec/dummy/.byebug_history
19
+ /spec/dummy/.env
20
+ /spec/dummy/.yardoc
21
+ /spec/dummy/coverage/
22
+ /spec/dummy/doc/
23
+ /spec/dummy/pkg/
24
+ /spec/dummy/spec/reports/
25
+ /spec/dummy/tags
26
+ /spec/dummy/*.DS_STORE
27
+ /spec/dummy/tmp/*
28
+ /spec/dummy/log/*
29
+ !/spec/dummy/log/.keep
30
+ !/spec/dummy/tmp/.keep
31
+ /spec/dummy/node_modules
32
+ /spec/dummy/yarn-error.log
33
+ /spec/dummy/public/system
34
+ /spec/dummy/public/assets
35
+ /spec/dummy/public/uploads
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in feste.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Josh Reinhardt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ :email: Feste is an easy way to give your users the ability to manage email subscriptions in your Rails application.
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'feste'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+ $ rails generate feste:install
15
+ $ rake db:migrate
16
+
17
+ Once installed, you will need to mount Feste in your application.
18
+
19
+ ```ruby
20
+ # config/routes.rb
21
+ mount Feste::Engine => "/email-subscriptions", as: "feste"
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ Feste organizes subscribable emails by separating them into categories that you define (see <a href="#mailer">Mailer</a> for more details). This requires an array of available categories (represented by symbols) to be provided to the `categories` configuration.
27
+
28
+ Out of the box, Feste allows your users to manage their subscriptions from a url in their email, which includes an identifying token. If you would like for them to be able to do so from within your application, you will need to provide a method for identifying the currently logged in user. Luckily, Feste provides authentication adapters for applications that use Devise and Clearance to manage user sessions. Otherwise, you can provide a Proc to the `authenticate_with` option.
29
+
30
+ Optionally, you can set the attribute your user model(s) use(s) to reference a user's email address (`email_source`) and the host that is used by the `subscriptions_url` helper (`host`).
31
+
32
+ ```ruby
33
+ # initializers/feste.rb
34
+ authentication_method = Proc.new do |controller|
35
+ ::User.find_by(id: controller.session[:user_id])
36
+ end
37
+
38
+ Feste.configure do |config|
39
+ # set your category names
40
+ config.categories = [:marketing_emails, :reminder_emails]
41
+ # for applications that use clearance
42
+ config.authenticate_with = :clearance
43
+ # for applications that use devise
44
+ config.authenticate_with = :devise
45
+ # for applications that use custom authentication
46
+ config.authenticate_with = authentication_method
47
+ # set the email attribute of your user model
48
+ config.email_source = :email
49
+ # set the host for subscription_url
50
+ config.host = ActionMailer::Base.default_url_options[:host]
51
+ end
52
+ ```
53
+ ## Usage
54
+
55
+ ### Model
56
+
57
+ In the model that holds your users' data, include `Feste::User`.
58
+
59
+ ```ruby
60
+ class User < ApplicationRecord
61
+ include Feste::User
62
+ end
63
+ ```
64
+
65
+ This will give your user model a `has_many` relationship to `subscriptions`. Since this relationship is polymorphic, your can include the `Feste::User` module in multiple models.
66
+
67
+ ### Mailer
68
+
69
+ In your mailer, include the `Feste::Mailer` module.
70
+
71
+ Feste keeps track of email subscriptions by grouping mailer actions into categories that you define. Your users will not be able to subscribe or unsubscribe to emails until you assign specific actions to a category. In order to do this, you can call the `categorize` method within your mailer. Doing so will automatically assign all actions in that mailer to the category you provide through the `as` option.
72
+
73
+ When calling the `mail` method within an action, make sure to explicitly state which user the subscription should be applied to using the `subscriber` option.
74
+
75
+ ```ruby
76
+ class CouponMailer < ApplicationMailer
77
+ include Feste::Mailer
78
+
79
+ categorize as: :marketing_emails
80
+
81
+ def send_coupon(user)
82
+ mail(to: user.email, from: "support@here.com", subscriber: user)
83
+ end
84
+ end
85
+ ```
86
+
87
+ If you only want to categorize specific actions in a mailer, you can do so by listing those actions in an array as your first arguement.
88
+
89
+ ```ruby
90
+ class CouponMailer < ApplicationMailer
91
+ include Feste::Mailer
92
+
93
+ categorize [:send_coupon], as: :marketing_emails
94
+ categorize [:send_coupon_reminder], as: :reminder_emails
95
+
96
+ def send_coupon(user)
97
+ mail(to: user.email, from: "support@here.com", subscriber: user)
98
+ end
99
+
100
+ def send_coupon_reminder(user)
101
+ mail(to: user.email, from: "support@here.com", subscriber: user)
102
+ end
103
+ end
104
+ ```
105
+
106
+ ### View
107
+
108
+ #### Mailer View
109
+
110
+ In our view file, you can use the helper method `subscription_url` to link to the page where users can manage their subscriptions.
111
+
112
+ ```html
113
+ <a href="<%= subscription_url %>">click here to unsubscribe</a>
114
+ ```
115
+
116
+ When a user clicks this link, they are taken to a page that allows them to choose which emails (by category) they would like to keep receiving, and which ones they would like to unsubscribe to.
117
+
118
+ #### Application View
119
+
120
+ The route to the subscriptions page is the root of the feste engine. You can link to this page from anywhere in your app using the `feste.subscriptions_url` helper (assuming the engine is mounted as 'feste'). When a logged in user visits this page from your application, they will be authenticated through the method which you provide in the configuration, and shown their email subscriptions.
121
+
122
+ ### Human Readable Category Names
123
+
124
+ In order to create category names that are human readable, add a `feste.categories`section to your i18n `locales` files. Create keys in this section that correspond to the `categories` configuration.
125
+
126
+ ```yml
127
+ # config/locales/en.yml
128
+
129
+ en:
130
+ feste:
131
+ categories:
132
+ marketing_emails: Marketing Emails
133
+ reminder_emails: Reminder Emails
134
+ ```
135
+
136
+ ### When not to use
137
+
138
+ It is recommended you DO NOT include any important emails, such as password reset emails, into a subscribable category. It is also recommended you do not include the subscription link in any email that is sent to multiple recipients. Though Feste comes with some security measures, it is assumed that each email is intended for only one recipient, and the `subscription_url` helper leads to a subsciption page meant only for that recipient. Exposing this page to other users may allow them to change subscription preferences for someone else's account.
139
+
140
+ ## Development
141
+
142
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
143
+
144
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
145
+
146
+ ## Contributing
147
+
148
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jereinhardt/feste.
149
+
150
+
151
+ ## License
152
+
153
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
154
+
155
+ l create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require "bundler/setup"
3
+ rescue LoadError
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
+ end
6
+
7
+
8
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
9
+ load 'rails/tasks/engine.rake'
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ require "bundler/gem_tasks"
14
+ require "rspec/core"
15
+ require "rspec/core/rake_task"
16
+ require "pg"
17
+
18
+ desc "Run all specs in spec directory (excluding plugin specs)"
19
+ RSpec::Core::RakeTask.new(spec: "app:db:test:prepare")
20
+
21
+ task :default => :spec
@@ -0,0 +1 @@
1
+ //= require_tree .
@@ -0,0 +1,72 @@
1
+ subscriptionVerification = (function () {
2
+ return function () {
3
+ var $form = document.getElementById("edit-subscriptions-form");
4
+ var $modal = document.getElementById("confirmation-modal");
5
+
6
+ $form.addEventListener("submit", handleMainFormSubmission);
7
+
8
+ function handleMainFormSubmission(event) {
9
+ event.preventDefault();
10
+ openModal($modal);
11
+ }
12
+
13
+ function openModal($modalContainer) {
14
+ $modalContainer.classList.remove("hidden");
15
+ bindModalEvents($modalContainer);
16
+ }
17
+
18
+ function closeModal($modalContainer) {
19
+ $modalContainer.className += " hidden";
20
+ unbindModalEvents($modalContainer);
21
+ }
22
+
23
+ function bindModalEvents($modalContainer) {
24
+ var $closeButtons = $modalContainer.querySelectorAll("[data-js-modal-close]");
25
+ for (var i = 0; i < $closeButtons.length; i ++) {
26
+ var $button = $closeButtons[i];
27
+ $button.addEventListener("click", function(event) {
28
+ if ( event.target == event.currentTarget ) {
29
+ event.preventDefault();
30
+ closeModal($modalContainer);
31
+ }
32
+ });
33
+ }
34
+
35
+ var $confirmForm = document.getElementById("cancel-subscription-form");
36
+ $confirmForm.addEventListener("submit", handleConfirmFormSubmission);
37
+ }
38
+
39
+ function unbindModalEvents($modalContainer) {
40
+ var $closeButtons = $modalContainer.querySelectorAll("[data-js-modal-close]");
41
+ for (var i = 0; i < $closeButtons.length; i ++) {
42
+ var $button = $closeButtons[i];
43
+ $button.removeEventListener("click", function(event) {
44
+ if ( event.target == event.currentTarget ) {
45
+ event.preventDefault();
46
+ closeModal($modalContainer);
47
+ }
48
+ });
49
+ }
50
+
51
+ var $confirmForm = document.getElementById("cancel-subscription-form");
52
+ $confirmForm.removeEventListener("submit", handleConfirmFormSubmission);
53
+ }
54
+
55
+ function handleConfirmFormSubmission(event) {
56
+ event.preventDefault();
57
+ var $form = event.target;
58
+ var $input = document.getElementById("email-confirmation-input");
59
+ var $required = $form.querySelectorAll("[data-js-required]")[0];
60
+ var $errorMessage = $required.querySelectorAll("[data-js-error]")[0];
61
+ var val = $required.querySelectorAll("input")[0].value;
62
+ var email = document.getElementById("subscriber-email").value
63
+ $errorMessage.className += " hidden";
64
+ if (!val || val != email) {
65
+ $errorMessage.classList.remove("hidden");
66
+ } else {
67
+ var $mainForm = document.getElementById("edit-subscriptions-form");
68
+ $mainForm.submit();
69
+ }
70
+ }
71
+ }
72
+ })();
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= require_tree .
3
+ *= require_self
4
+ */
@@ -0,0 +1,150 @@
1
+ body {
2
+ background: transparent;
3
+ color: #4f4d4b;
4
+ font-family: "Whitney A", "Whitney B", "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
5
+ margin: 0;
6
+ }
7
+
8
+ .container {
9
+ padding: 0 1.25rem;
10
+ }
11
+
12
+ .main-body {
13
+ max-width: 750px;
14
+ }
15
+
16
+ .hidden {
17
+ display: none;
18
+ }
19
+
20
+ .flash-message {
21
+ border-radius: 30px;
22
+ color: white;
23
+ font-size: 1rem;
24
+ font-weight: 300;
25
+ margin-bottom: 3em;
26
+ padding: 15px;
27
+ text-align: center;
28
+ }
29
+
30
+ .flash-success {
31
+ background-color: #e6efc2;
32
+ color: #56651a;
33
+ display: block;
34
+ font-weight: 600;
35
+ margin-bottom: 0.625rem;
36
+ padding: 0.625rem;
37
+ text-align: center;
38
+ }
39
+
40
+ .flash-notice {
41
+ background-color: #e5edf8;
42
+ color: #244e87;
43
+ display: block;
44
+ font-weight: 600;
45
+ margin-bottom: 0.625rem;
46
+ padding: 0.625rem;
47
+ text-align: center;
48
+ }
49
+
50
+ .button {
51
+ -webkit-appearance: none;
52
+ -moz-appearance: none;
53
+ -ms-appearance: none;
54
+ -o-appearance: none;
55
+ appearance: none;
56
+ -webkit-font-smoothing: antialiased;
57
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #ffffff), to(#f7f7f7));
58
+ background-image: linear-gradient(#ffffff 20%, #f7f7f7);
59
+ border-radius: 2px;
60
+ border: 1px solid #e4e4e3;
61
+ border-top-color: rgb(228, 228, 227);
62
+ border-top-style: solid;
63
+ border-top-width: 1px;
64
+ border-right-color: rgb(228, 228, 227);
65
+ border-right-style: solid;
66
+ border-right-width: 1px;
67
+ border-bottom-color: rgb(228, 228, 227);
68
+ border-bottom-style: solid;
69
+ border-bottom-width: 1px;
70
+ border-left-color: rgb(228, 228, 227);
71
+ border-left-style: solid;
72
+ border-left-width: 1px;
73
+ border-image-source: initial;
74
+ border-image-slice: initial;
75
+ border-image-width: initial;
76
+ border-image-outset: initial;
77
+ border-image-repeat: initial;
78
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
79
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
80
+ color: #4383c5;
81
+ cursor: pointer;
82
+ display: inline-block;
83
+ font-family: "Whitney A", "Whitney B", "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
84
+ font-size: 0.875rem;
85
+ font-weight: 600;
86
+ line-height: 2.3;
87
+ padding: 0 1.25rem;
88
+ text-align: center;
89
+ text-decoration: none;
90
+ text-decoration-line: none;
91
+ text-decoration-style: initial;
92
+ text-decoration-color: initial;
93
+ -webkit-transition: all 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
94
+ transition: all 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
95
+ -webkit-user-select: none;
96
+ -moz-user-select: none;
97
+ -ms-user-select: none;
98
+ user-select: none;
99
+ vertical-align: middle;
100
+ white-space: nowrap;
101
+ }
102
+
103
+ .button.button--submit {
104
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(20%, #afca7d), to(#99bb59)), #99bb59;
105
+ background-image: linear-gradient(rgb(175, 202, 125) 20%, rgb(153, 187, 89)), initial;
106
+ background-position-x: initial, initial;
107
+ background-position-y: initial, initial;
108
+ background-size: initial, initial;
109
+ background-repeat-x: initial, initial;
110
+ background-repeat-y: initial, initial;
111
+ background-attachment: initial, initial;
112
+ background-origin: initial, initial;
113
+ background-clip: initial, initial;
114
+ background-color: rgb(153, 187, 89);
115
+ background: linear-gradient(#afca7d 20%, #99bb59), #99bb59;
116
+ background-image: linear-gradient(rgb(175, 202, 125) 20%, rgb(153, 187, 89)), initial;
117
+ background-position-x: initial, initial;
118
+ background-position-y: initial, initial;
119
+ background-size: initial, initial;
120
+ background-repeat-x: initial, initial;
121
+ background-repeat-y: initial, initial;
122
+ background-attachment: initial, initial;
123
+ background-origin: initial, initial;
124
+ background-clip: initial, initial;
125
+ background-color: rgb(153, 187, 89);
126
+ border: 1px solid #748e42;
127
+ border-top-color: rgb(116, 142, 66);
128
+ border-top-style: solid;
129
+ border-top-width: 1px;
130
+ border-right-color: rgb(116, 142, 66);
131
+ border-right-style: solid;
132
+ border-right-width: 1px;
133
+ border-bottom-color: rgb(116, 142, 66);
134
+ border-bottom-style: solid;
135
+ border-bottom-width: 1px;
136
+ border-left-color: rgb(116, 142, 66);
137
+ border-left-style: solid;
138
+ border-left-width: 1px;
139
+ border-image-source: initial;
140
+ border-image-slice: initial;
141
+ border-image-width: initial;
142
+ border-image-outset: initial;
143
+ border-image-repeat: initial;
144
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
145
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
146
+ color: #ffffff;
147
+ display: inline-block;
148
+ font-size: 1rem;
149
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
150
+ }
@@ -0,0 +1,40 @@
1
+ .modal-overlay {
2
+ background-color: #ffffff;
3
+ height: 100%;
4
+ width: 100%;
5
+ }
6
+
7
+ .confirmation-modal-container {
8
+ height: 100%;
9
+ left: 0;
10
+ position: fixed;
11
+ top: 0;
12
+ width: 100%;
13
+ }
14
+
15
+ .modal-content {
16
+ background-color: #ffffff;
17
+ border: 1px solid #cbcbc9;
18
+ border-radius: 2px;
19
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.07);
20
+ box-sizing: border-box;
21
+ max-width: 500px;
22
+ margin: 0 auto;
23
+ padding: 1.875rem;
24
+ text-align: center;
25
+ position: relative;
26
+ top: 50px;
27
+ width: 100%;
28
+ -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.07);
29
+ }
30
+
31
+ .modal-header {
32
+ font-size: 1.25rem;
33
+ margin: 0;
34
+ }
35
+
36
+ .modal-body {
37
+ color: #4f4d4b;
38
+ font-size: 1rem;
39
+ font-weight: 400;
40
+ }
@@ -0,0 +1,40 @@
1
+ input {
2
+ font-size: 1rem;
3
+ }
4
+
5
+ .subscription-category-container {
6
+ padding: 0.325rem 0;
7
+ width: 100%;
8
+ }
9
+
10
+ .subscription-submit-button {
11
+ margin-top: 1.25rem;
12
+ }
13
+
14
+ .error-label {
15
+ color: #aa1111;
16
+ font-weight: 600;
17
+ }
18
+
19
+ .form-submit-button {
20
+ background: #aa1111;
21
+ border: 0;
22
+ border-radius: 30px;
23
+ color: white;
24
+ font-size: 1.2em;
25
+ font-weight: 600;
26
+ padding: 15px;
27
+ }
28
+
29
+ .email-field-container {
30
+ margin-bottom: 1.2em;
31
+ }
32
+
33
+ .email-field-input {
34
+ box-sizing: border-box;
35
+ width: 100%;
36
+ padding: 5px;
37
+ font-size: 1em;
38
+ text-align: center;
39
+ font-weight: 300;
40
+ }
@@ -0,0 +1,41 @@
1
+ module Feste
2
+ module Authenticatable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ if Feste.options[:authenticate_with] == :clearance
7
+ include Feste::Authentication::Clearance
8
+ elsif Feste.options[:authenticate_with] == :devise
9
+ include Feste::Authentication::Devise
10
+ elsif Feste.options[:authenticate_with].is_a?(Proc)
11
+ include Feste::Authentication::Custom
12
+ else
13
+ include Feste::Authentication::DefaultInstanceMethods
14
+ end
15
+
16
+ before_action :get_user_data
17
+ end
18
+
19
+ def subscriber
20
+ @_subscriber ||= Feste::Subscription.find_by(token: params[:token])&.subscriber ||
21
+ current_user
22
+ end
23
+
24
+ def get_user_data
25
+ if subscriber.present?
26
+ find_or_create_subscriptions
27
+ else
28
+ render file: "#{Rails.root}/public/404.html", status: 404
29
+ end
30
+ end
31
+
32
+ def find_or_create_subscriptions
33
+ Feste.options[:categories].each do |category|
34
+ Feste::Subscription.find_or_create_by(
35
+ subscriber: subscriber,
36
+ category: category
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ module Feste
2
+ class SubscriptionsController < ActionController::Base
3
+ include Feste::Authenticatable
4
+
5
+ protect_from_forgery with: :exception
6
+
7
+ layout "feste/application"
8
+
9
+ def index
10
+ @subscriber = subscriber
11
+ end
12
+
13
+ def update
14
+ if update_subscriptions
15
+ flash[:success] = "You have successfully updated your subscriptions!"
16
+ else
17
+ flash[:notice] = "Something went wrong! Please try again later."
18
+ end
19
+ redirect_back(
20
+ fallback_location: subscriptions_path(token: params[:token])
21
+ )
22
+ end
23
+
24
+ private
25
+
26
+ def user_params
27
+ params.require(subscriber.class.to_s.downcase.to_sym).
28
+ permit(subscriptions: [])
29
+ end
30
+
31
+ def subscriptions_params
32
+ user_params[:subscriptions]
33
+ end
34
+
35
+ def update_subscriptions
36
+ subscribed = subscriber.subscriptions.where(id: subscriptions_params)
37
+ unsubscribed = subscriber.subscriptions.where.
38
+ not(id: subscriptions_params)
39
+ subscribed.update_all(canceled: false) &&
40
+ unsubscribed.update_all(canceled: true)
41
+ end
42
+ end
43
+ end