unsubscribe 0.1.0 → 1.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b90d08ba0979b64c7de3ec38af50fc8e39a5efb80d92218c9c9cac9b409b3cb9
4
- data.tar.gz: ce95af4f4bb3ba2be36a11dce5fa62d960c9eedc0d23093acd6e3fa4c7113868
3
+ metadata.gz: 28f28f3bede2e59008c148b3262c718e621613bff193fcf8f22b0a6ad5302a50
4
+ data.tar.gz: 37b067207a3ed305fda44ca609f78e7d4307d10914ca2c2683f6df87bb978f43
5
5
  SHA512:
6
- metadata.gz: efc5e6c92daacab73a5be06558db5c6fa2fa80b01d069f5eac91349a1fe1da37f2b4b080a35ca1502f7fbf9e7e1710cdc810709cc99f166187b50c28a2d031e3
7
- data.tar.gz: 8eee60eac59948bef5731df07086797992585d4b246386495bf47938b57400d4752d425b8da0ccb42cff5376997c8c18bc656075d7529a2a64dfbbadfba98274
6
+ metadata.gz: b5881a6787fd24d5860a433a93094addf13a143b71b4784a08dfaa1700432ddcbf2e2237351d573cb0030ec472ed49d41242a7fd2d67c73bb309905d4a49c06d
7
+ data.tar.gz: 19bd2ff135befe0b3d5212f9b72b76a8db310df77b1d27eb7abb488443078619160bb6bc60af12a6ade697475855e8db265796676555c578830580170d24578d
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
- # Unsubscribe
2
- Automatically unsubscribe from emails in Rails.
1
+ # 📭 Unsubscribe
3
2
 
4
- ## Usage
5
- TODO
3
+ Automatically unsubscribe from emails in Rails.
4
+
5
+ ![Demo](./docs/demo.gif)
6
+
7
+ ## 🚀 Installation
6
8
 
7
- ## Installation
8
9
  Add this line to your application's Gemfile:
9
10
 
10
11
  ```ruby
@@ -21,8 +22,112 @@ Or install it yourself as:
21
22
  $ gem install unsubscribe
22
23
  ```
23
24
 
24
- ## Contributing
25
- Contribution directions go here.
25
+ Then run the installation commands:
26
+
27
+ ```bash
28
+ rails g unsubscribe:install
29
+ rails unsubscribe:install:migrations
30
+ rails db:migrate
31
+ ```
32
+
33
+ ## 📚 Usage
34
+
35
+ ### Unsubscribe::Owner
36
+
37
+ - Add `include Unsubscribe::Owner` to a `Model`. The `Model` must have an `email` column.
38
+
39
+ ```ruby
40
+ class User < ApplicationRecord
41
+ include Unsubscribe::Owner
42
+ end
43
+ ```
44
+
45
+ #### Available Methods
46
+
47
+ ```ruby
48
+ User.first.subscribed_to_mailer? "MarketingMailer"
49
+ # => true/false
50
+
51
+ User.first.to_sgid_for_mailer_subscription
52
+ # => #<SignedGlobalID:123 ...>
53
+ ```
54
+
55
+ ### Unsubscribe::Mailer
56
+
57
+ - Add `include Unsubscribe::Mailer` to a `Mailer`.
58
+ - Optionally call `unsubscribe_settings` to set a `name` and `description`. This will be used in the unsubscribe page.
59
+ - Set `mail to:` to `@recipient.email`. The `@recipient` is an instance of whatever Class `include Unsubscribe::Owner` was added to.
60
+
61
+ ```ruby
62
+ class MarketingMailer < ApplicationMailer
63
+ include Unsubscribe::Mailer
64
+
65
+ unsubscribe_settings name: "Marketing Emails", description: "Updates on promotions and sales."
66
+
67
+ def promotion
68
+ mail to: @recipient.email
69
+ end
70
+ end
71
+ ```
72
+
73
+ - Call the `Mailer` with a `recipient` parameter.
74
+
75
+ ```ruby
76
+ MarketingMailer.with(
77
+ recipient: User.first
78
+ ).promotion.deliver_now
79
+ ```
80
+
81
+ #### Available Methods
82
+
83
+ ```ruby
84
+ Unsubscribe::MailerSubscription.first.action
85
+ # => "Unsubscribe from"/"Subscribe to"
86
+
87
+ Unsubscribe::MailerSubscription.first.call_to_action
88
+ # => "Unsubscribe from Marketing Emails"/"Subscribe to Marketing Emails"
89
+
90
+ Unsubscribe::MailerSubscription.first.description
91
+ # => "Updates on promotions and sales."
92
+
93
+ Unsubscribe::MailerSubscription.first.name
94
+ # => "Marketing Emails"
95
+ ```
96
+
97
+ ### Unsubscribe Link
98
+
99
+ - Add the `@unsubscribe_url` link to the `Mailer`.
100
+
101
+ ```html+erb
102
+ <%= link_to "Unsubscribe", @unsubscribe_url %>
103
+ ```
104
+
105
+ ## ⚙️ Customize Templates
106
+
107
+ Run `rails g unsubscribe:views` if you want to modify the existing templates.
108
+
109
+ ## 🌐 I18n
110
+
111
+ The language used for `Unsubscribe::MailerSubscription#action` can be translated.
112
+
113
+ ```yml
114
+ # config/locales/en.yml
115
+ en:
116
+ unsubscribe:
117
+ action:
118
+ subscribe: "Subscribe to"
119
+ unsubscribe: "Unsubscribe from"
120
+ ```
121
+
122
+ ## 🙏 Contributing
123
+
124
+ If you'd like to open a PR please make sure the following things pass:
125
+
126
+ ```ruby
127
+ bin/rails test
128
+ bundle exec standardrb
129
+ ```
130
+
131
+ ## 📜 License
26
132
 
27
- ## License
28
133
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -10,8 +10,8 @@ require "bundler/gem_tasks"
10
10
  require "rake/testtask"
11
11
 
12
12
  Rake::TestTask.new(:test) do |t|
13
- t.libs << 'test'
14
- t.pattern = 'test/**/*_test.rb'
13
+ t.libs << "test"
14
+ t.pattern = "test/**/*_test.rb"
15
15
  t.verbose = false
16
16
  end
17
17
 
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,66 @@
1
+ require_dependency "unsubscribe/application_controller"
2
+
3
+ module Unsubscribe
4
+ class MailerSubscriptionsController < ApplicationController
5
+ before_action :set_owner, only: [:show, :create, :update]
6
+ before_action :set_mailer, only: [:show]
7
+
8
+ def show
9
+ end
10
+
11
+ def create
12
+ @mailer = Unsubscribe::MailerSubscription.new(mailer_subscription_params)
13
+
14
+ if @owner != @mailer.owner
15
+ redirect_to(
16
+ mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: params[:mailer_subscription][:mailer]),
17
+ alert: "You are not authorized to perform this action."
18
+ ) and return
19
+ end
20
+
21
+ if @mailer.save
22
+ redirect_to(
23
+ mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: params[:mailer_subscription][:mailer]),
24
+ notice: "Settings updated."
25
+ )
26
+ else
27
+ redirect_to(
28
+ mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: params[:mailer_subscription][:mailer]),
29
+ alert: @mailer.errors.full_messages.to_sentence
30
+ )
31
+ end
32
+ end
33
+
34
+ def update
35
+ @mailer = Unsubscribe::MailerSubscription.find(params[:mailer_subscription_id])
36
+
37
+ if @owner != @mailer.owner
38
+ redirect_to(
39
+ mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: @mailer.mailer),
40
+ alert: "You are not authorized to perform this action."
41
+ ) and return
42
+ end
43
+
44
+ if @mailer.toggle!(:subscribed)
45
+ redirect_to mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: @mailer.mailer), notice: "Settings updated."
46
+ else
47
+ redirect_to mailer_subscription_path(@owner.to_sgid_for_mailer_subscription, mailer: @mailer.mailer), alert: @mailer.errors.full_messages.to_sentence
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def set_owner
54
+ @owner = GlobalID::Locator.locate_signed(params[:id], for: :mailer_subscription)
55
+ raise ActiveRecord::RecordNotFound if @owner.nil?
56
+ end
57
+
58
+ def set_mailer
59
+ @mailer = @owner.mailer_subscriptions.find_or_initialize_by(mailer: params[:mailer])
60
+ end
61
+
62
+ def mailer_subscription_params
63
+ params.require(:mailer_subscription).permit(:owner_id, :owner_type, :subscribed, :mailer)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ module Unsubscribe
2
+ module MailerSubscriptionsHelper
3
+ end
4
+ end
@@ -1,6 +1,6 @@
1
1
  module Unsubscribe
2
2
  class ApplicationMailer < ActionMailer::Base
3
- default from: 'from@example.com'
4
- layout 'mailer'
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
5
  end
6
6
  end
@@ -0,0 +1,45 @@
1
+ module Unsubscribe
2
+ class MailerSubscription < ApplicationRecord
3
+ belongs_to :owner, polymorphic: true
4
+
5
+ validates :subscribed, inclusion: [true, false], allow_nil: true
6
+ validates :mailer, presence: true
7
+ validates :owner_id, uniqueness: {scope: [:mailer, :owner_type]}
8
+ validate :mailer_should_exist
9
+
10
+ def action
11
+ case subscribed
12
+ when nil
13
+ I18n.t("unsubscribe.action.unsubscribe")
14
+ else
15
+ subscribed? ? I18n.t("unsubscribe.action.unsubscribe") : I18n.t("unsubscribe.action.subscribe")
16
+ end
17
+ end
18
+
19
+ def call_to_action
20
+ "#{action} #{name}"
21
+ end
22
+
23
+ def description
24
+ details[:description]
25
+ end
26
+
27
+ def name
28
+ details[:name].present? ? details[:name] : mailer
29
+ end
30
+
31
+ def details
32
+ mailer.constantize.unsubscribe_settings
33
+ rescue NoMethodError
34
+ raise Unsubscribe::Error, "Make sure to include Unsubscribe::Mailer in #{mailer}"
35
+ end
36
+
37
+ private
38
+
39
+ def mailer_should_exist
40
+ errors.add(:mailer, "is not a enbled") unless mailer.constantize.enabled
41
+ rescue NameError
42
+ errors.add(:mailer, "is not a Mailer")
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ <h1><%= @mailer.name %></h1>
2
+ <p><%= @mailer.description %></p>
3
+ <% if @mailer.new_record? %>
4
+ <%= button_to(
5
+ @mailer.call_to_action,
6
+ mailer_subscriptions_path,
7
+ params: {
8
+ id: @owner.to_sgid_for_mailer_subscription,
9
+ mailer_subscription: {
10
+ owner_id: @owner.id,
11
+ owner_type: @owner.class,
12
+ subscribed: false,
13
+ mailer: @mailer.mailer
14
+ }
15
+ }
16
+ ) %>
17
+ <% else %>
18
+ <%= button_to(
19
+ @mailer.call_to_action,
20
+ mailer_subscription_path(@owner.to_sgid_for_mailer_subscription),
21
+ method: :put,
22
+ params: {
23
+ mailer_subscription_id: @mailer.id
24
+ }
25
+ ) %>
26
+ <% end %>
@@ -0,0 +1,5 @@
1
+ en:
2
+ unsubscribe:
3
+ action:
4
+ subscribe: "Subscribe to"
5
+ unsubscribe: "Unsubscribe from"
data/config/routes.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  Unsubscribe::Engine.routes.draw do
2
+ resources :mailer_subscriptions, only: [:create, :update, :show]
2
3
  end
@@ -0,0 +1,15 @@
1
+ class CreateUnsubscribeMailerSubscriptions < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :unsubscribe_mailer_subscriptions do |t|
4
+ # Needs to be polymorphic to be flexible. Owner may not always be a User.
5
+ t.references :owner, polymorphic: true, null: false, index: { name: "unsubscribe_owner_index" }
6
+ t.boolean :subscribed
7
+ t.string :mailer, null: false
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ # An owner should only have one Unsubscribe::MailerSubscription record per Mailer.
13
+ add_index :unsubscribe_mailer_subscriptions, [:owner_id, :owner_type, :mailer], unique: true, name: "unsubscribe_owner_mailer_index"
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require "rails/generators"
2
+
3
+ module Unsubscribe
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("./../../../../../", __FILE__)
7
+
8
+ def link_manifest_js
9
+ inject_into_file "app/assets/config/manifest.js" do
10
+ <<~EOF
11
+ \n//= link unsubscribe_manifest.js
12
+ EOF
13
+ end
14
+ end
15
+
16
+ def mount_engine
17
+ inject_into_file "config/routes.rb", after: "Rails.application.routes.draw do" do
18
+ <<~EOF
19
+ \n\tmount Unsubscribe::Engine => "/unsubscribe"
20
+ EOF
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ require "rails/generators"
2
+
3
+ module Unsubscribe
4
+ module Generators
5
+ class ViewsGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("./../../../../../", __FILE__)
7
+
8
+ def copy_views
9
+ directory "app/views/unsubscribe", "app/views/unsubscribe"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module Unsubscribe
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,41 @@
1
+ module Unsubscribe
2
+ module Mailer
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :set_recipient
7
+ before_action :set_unsubscribe_url, if: :should_unsubscribe?
8
+ before_action :set_headers, if: :should_unsubscribe?
9
+ after_action :prevent_delivery_if_recipient_opted_out, if: :should_unsubscribe?
10
+ end
11
+
12
+ class_methods do
13
+ def unsubscribe_settings(name: nil, description: nil)
14
+ cattr_accessor :unsubscribe_settings, default: {name: name, description: description}
15
+ cattr_accessor :enabled, default: true
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def prevent_delivery_if_recipient_opted_out
22
+ mail.perform_deliveries = @recipient.subscribed_to_mailer? self.class.to_s
23
+ end
24
+
25
+ def set_recipient
26
+ @recipient = params[:recipient]
27
+ end
28
+
29
+ def set_unsubscribe_url
30
+ @unsubscribe_url = unsubscribe.mailer_subscription_url(@recipient.to_sgid_for_mailer_subscription, mailer: self.class)
31
+ end
32
+
33
+ def should_unsubscribe?
34
+ @recipient.present? && @recipient.respond_to?(:subscribed_to_mailer?)
35
+ end
36
+
37
+ def set_headers
38
+ headers["List-Unsubscribe"] = "<#{@unsubscribe_url}>"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ module Unsubscribe
2
+ module Owner
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :mailer_subscriptions, class_name: "Unsubscribe::MailerSubscription", as: :owner, inverse_of: :owner, dependent: :destroy
7
+ end
8
+
9
+ def subscribed_to_mailer?(mailer)
10
+ Unsubscribe::MailerSubscription.find_by(
11
+ owner: self,
12
+ mailer: mailer,
13
+ subscribed: false
14
+ ).nil?
15
+ end
16
+
17
+ def to_sgid_for_mailer_subscription
18
+ to_sgid(for: :mailer_subscription)
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Unsubscribe
2
- VERSION = '0.1.0'
2
+ VERSION = "1.0.0.alpha.1"
3
3
  end
data/lib/unsubscribe.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require "unsubscribe/version"
2
2
  require "unsubscribe/engine"
3
3
 
4
+ require "unsubscribe/error"
5
+ require "unsubscribe/mailer"
6
+ require "unsubscribe/owner"
7
+
4
8
  module Unsubscribe
5
- # Your code goes here...
6
9
  end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unsubscribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0.alpha.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Polito
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-17 00:00:00.000000000 Z
11
+ date: 2021-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 6.1.4
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 6.1.4.1
19
+ version: 6.0.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 6.1.4
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 6.1.4.1
26
+ version: 6.0.0
33
27
  description: 'Automatically unsubscribe from emails in Rails. '
34
28
  email:
35
29
  - stevepolito@hey.com
@@ -42,16 +36,28 @@ files:
42
36
  - Rakefile
43
37
  - app/assets/config/unsubscribe_manifest.js
44
38
  - app/assets/stylesheets/unsubscribe/application.css
39
+ - app/assets/stylesheets/unsubscribe/mailer_subscriptions.css
45
40
  - app/controllers/unsubscribe/application_controller.rb
41
+ - app/controllers/unsubscribe/mailer_subscriptions_controller.rb
46
42
  - app/helpers/unsubscribe/application_helper.rb
43
+ - app/helpers/unsubscribe/mailer_subscriptions_helper.rb
47
44
  - app/jobs/unsubscribe/application_job.rb
48
45
  - app/mailers/unsubscribe/application_mailer.rb
49
46
  - app/models/unsubscribe/application_record.rb
47
+ - app/models/unsubscribe/mailer_subscription.rb
50
48
  - app/views/layouts/unsubscribe/application.html.erb
49
+ - app/views/unsubscribe/mailer_subscriptions/show.html.erb
50
+ - config/locales/en.yml
51
51
  - config/routes.rb
52
+ - db/migrate/20210918092925_create_unsubscribe_mailer_subscriptions.rb
53
+ - lib/generators/unsubscribe/install/install_generator.rb
54
+ - lib/generators/unsubscribe/views/views_generator.rb
52
55
  - lib/tasks/unsubscribe_tasks.rake
53
56
  - lib/unsubscribe.rb
54
57
  - lib/unsubscribe/engine.rb
58
+ - lib/unsubscribe/error.rb
59
+ - lib/unsubscribe/mailer.rb
60
+ - lib/unsubscribe/owner.rb
55
61
  - lib/unsubscribe/version.rb
56
62
  homepage: https://github.com/stevepolitodesign/unsubscribe
57
63
  licenses:
@@ -71,9 +77,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
77
  version: '0'
72
78
  required_rubygems_version: !ruby/object:Gem::Requirement
73
79
  requirements:
74
- - - ">="
80
+ - - ">"
75
81
  - !ruby/object:Gem::Version
76
- version: '0'
82
+ version: 1.3.1
77
83
  requirements: []
78
84
  rubygems_version: 3.1.2
79
85
  signing_key: