effective_postmark 0.1.0

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: a6ecb092515ab49a0f89d1fefbab3544e5f2408ef0c02a4fb76ac1459cc1ab44
4
+ data.tar.gz: c29c83b5f843b66394163919a8d9300311eeca4e3a400824b1e62070fbcebd31
5
+ SHA512:
6
+ metadata.gz: f191d390a066d092cfb47e914b868b01f8c4fc583327552325a7db086dc387b6d9621288891d0749a8f2b394664fedc7cc1f8be664458b899eca9e595197ba45
7
+ data.tar.gz: b0431e3de67d57b31b0097bce467a99010457a1128fddc7c1b7a645578151540a1405348967864d4d8609cb53583b8bfdaa7f2cda1f7773ed0aa189407e4b28b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2024 Code and Effect Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Effective Postmark
2
+
3
+ A mailer concern to capture `Postmark::InactiveRecipientError` and mark users as bounced.
4
+
5
+ Uses the postmark API to reactivate users. Includes a datatable report of all inactive users.
6
+
7
+ ## effective_postmark 1.0
8
+
9
+ This is the 1.0 series of effective_postmark.
10
+
11
+ This requires Twitter Bootstrap 4.
12
+
13
+ ## Getting Started
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'haml-rails'
19
+ gem 'effective_postmark'
20
+ ```
21
+
22
+ Run the bundle command to install it:
23
+
24
+ ```console
25
+ bundle install
26
+ ```
27
+
28
+ Add the following to your user model:
29
+
30
+ ```ruby
31
+ class User
32
+ effective_postmark_user
33
+ end
34
+ ```
35
+
36
+ And add two database fields to your user model:
37
+
38
+ ```ruby
39
+ class AddEffectivePostmarkFieldsToUsers < ActiveRecord::Migration[7.0]
40
+ def change
41
+ add_column :users, :postmark_error, :string
42
+ add_column :users, :postmark_error_at, :datetime
43
+ end
44
+ end
45
+ ```
46
+
47
+ Then migrate the database:
48
+
49
+ ```ruby
50
+ rake db:migrate
51
+ ```
52
+
53
+ ## Admin View
54
+
55
+ To add an admin alert when the user has been marked inactive with a link to reactivate
56
+
57
+ ```ruby
58
+ = effective_postmark_admin_alert(current_user, from: 'My Site')
59
+ ```
60
+
61
+ ## Dashboard View
62
+
63
+ To add an alert when the user has been marked inactive
64
+
65
+ ```ruby
66
+ = effective_postmark_alert(current_user, from: 'My Site')
67
+ ```
68
+
69
+ ## Devise Password Reset
70
+
71
+ If you want to include the inactive alert on the devise reset password page:
72
+
73
+ ```ruby
74
+ class Users::PasswordsController < Devise::PasswordsController
75
+ after_action(only: :create) do
76
+ if resource.try(:postmark_invalid?)
77
+ flash.delete(:notice)
78
+ flash[:danger] = view_context.effective_postmark_alert(resource, from: 'MyARTA', html_class: '')
79
+ end
80
+ end
81
+ end
82
+ ```
83
+
84
+ ## Inactive Recipients Report
85
+
86
+ Add a link to the admin report:
87
+
88
+ ```ruby
89
+ = nav_link_to 'Inactive Recipients', effective_postmark.inactive_recipients_admin_postmark_reports_path
90
+ ```
91
+
92
+ ### Permissions
93
+
94
+ Give the following permissions to your admin user:
95
+
96
+ ```ruby
97
+ can :admin, :effective_postmark
98
+ can(:postmark_reactivate, User) { |user| user.postmark_invalid? }
99
+ can(:index, Admin::ReportInactiveRecipientsDatatable)
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
105
+
106
+
107
+ ## Contributing
108
+
109
+ 1. Fork it
110
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
111
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
112
+ 4. Push to the branch (`git push origin my-new-feature`)
113
+ 5. Bonus points for test coverage
114
+ 6. Create new Pull Request
@@ -0,0 +1,17 @@
1
+ module Admin
2
+ class PostmarkController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_postmark) }
5
+
6
+ include Effective::CrudController
7
+
8
+ resource_scope -> { current_user.class.all }
9
+
10
+ on :postmark_reactivate, success: -> { "Successfully reactivated and sent an email to #{resource.email}" }
11
+
12
+ def permitted_params
13
+ params.require(:user).permit!
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class PostmarkReportsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_postmark) }
5
+
6
+ def inactive_recipients
7
+ @datatable = Admin::ReportInactiveRecipientsDatatable.new
8
+ @page_title = @datatable.datatable_name
9
+
10
+ authorize! :index, @datatable
11
+
12
+ render 'index'
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ # Postmark: Inactive Recipients
2
+
3
+ module Admin
4
+ class ReportInactiveRecipientsDatatable < Effective::Datatable
5
+ datatable do
6
+ col :id, visible: false
7
+
8
+ col(:to_s, label: 'User', sql_column: true, partial: 'admin/users/col')
9
+ .search do |collection, term|
10
+ collection.where(id: effective_resource.search_any(term))
11
+ end.sort do |collection, direction|
12
+ collection.order(first_name: direction)
13
+ end
14
+
15
+ col :email, visible: false
16
+ col :first_name, visible: false
17
+ col :last_name, visible: false
18
+
19
+ col :postmark_error
20
+ col :postmark_error_at
21
+
22
+ actions_col(actions: []) do |user|
23
+ dropdown_link_to('Reactivate', effective_postmark.postmark_reactivate_admin_postmark_path(user), remote: true, method: :post)
24
+ dropdown_link_to('Edit', "/admin/users/#{user.to_param}/edit")
25
+ end
26
+ end
27
+
28
+ collection do
29
+ current_user.class.postmark_inactive_recipients
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ class EffectivePostmarkDatatable < Effective::Datatable
2
+ datatable do
3
+ order :subject, :asc
4
+ length :all
5
+
6
+ col :updated_at, visible: false
7
+ col :created_at, visible: false
8
+ col :id, visible: false
9
+
10
+ col :template_name, label: 'Name'
11
+
12
+ col :from, search: EffectivePostmark.mailer_froms do |email_template|
13
+ ERB::Util.html_escape_once(email_template.from)
14
+ end
15
+
16
+ col :cc
17
+ col :bcc
18
+ col :subject
19
+ col :body
20
+
21
+ col :content_type, visible: false
22
+
23
+ actions_col
24
+ end
25
+
26
+ collection do
27
+ Effective::EmailTemplate.all
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module EffectivePostmarkHelper
2
+
3
+ # For use on the regular user alerts and forgot password pages
4
+ def effective_postmark_alert(user, from: nil, html_class: 'alert alert-danger')
5
+ raise('expected an effective_postmark_user') unless user.class.try(:effective_postmark_user?)
6
+ return unless user.postmark_invalid?
7
+
8
+ content_tag(:div, class: html_class) do
9
+ [
10
+ succeed(',') { "IMPORTANT: The email address you use to sign in#{" to #{from}" if from}" },
11
+ succeed(',') { user.email },
12
+ "has been marked as inactive.",
13
+ "You will not receive any emails",
14
+ ("from #{from}" if from),
15
+ "while inactive.",
16
+ "Please contact us to update your email address."
17
+ ].compact.join(' ').html_safe
18
+ end
19
+ end
20
+
21
+ # For use on the admin/users#edit page
22
+ def effective_postmark_admin_alert(user, from: nil, html_class: 'alert alert-danger')
23
+ raise('expected an effective_postmark_user') unless user.class.try(:effective_postmark_user?)
24
+ return unless user.postmark_invalid?
25
+
26
+ content_tag(:div, class: html_class) do
27
+ [
28
+ "This user has an inactive email address.",
29
+ "They will not receive any emails",
30
+ ("from #{from}" if from),
31
+ "while inactive.",
32
+ "Please update their email address, or" +
33
+ link_to('reactivate their email', effective_postmark.postmark_reactivate_admin_postmark_path(user), 'data-method': :post),
34
+ "and automatically attempt an email delivery."
35
+ ].compact.join(' ').html_safe
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,42 @@
1
+ module EffectivePostmarkMailer
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ # Make sure config.action_mailer.raise_delivery_errors = true
6
+ before_action do
7
+ unless (Rails.application.config.action_mailer.raise_delivery_errors rescue false)
8
+ raise("Expected config.action_mailer.raise_delivery_errors to be true. Please update your environment for use with effective_postmark.")
9
+ end
10
+ end
11
+
12
+ rescue_from Postmark::InactiveRecipientError, with: :effective_postmark_inactive_recipient_error
13
+ end
14
+
15
+ def effective_postmark_inactive_recipient_error(exception)
16
+ # Read the current app's Tenant if defined
17
+ tenant = if defined?(Tenant)
18
+ Tenant.current || raise("Missing tenant in effective_postmark exception")
19
+ end
20
+
21
+ # Find the user to associate it with
22
+ user_klass = (tenant ? Tenant.engine_user(tenant) : "User".safe_constantize)
23
+ raise("Expected an effective_postmark_user") unless user_klass.try(:effective_postmark_user?)
24
+
25
+ # All recipients
26
+ recipients = (Array(exception.recipients) - [nil, "", " "]).map { |email| email.downcase.strip }
27
+
28
+ # Find each user and mark them postmark_inactive and make a log
29
+ recipients.each do |email|
30
+ user = user_klass.find_for_database_authentication(email: email)
31
+
32
+ if user.present?
33
+ user.postmark_inactive_recipient!
34
+ end
35
+
36
+ ::EffectiveLogger.email("[ERROR] Inactive Recipient - #{email}", user: user, error_code: exception.error_code, message: exception.message)
37
+ end
38
+
39
+ true
40
+ end
41
+
42
+ end
@@ -0,0 +1,17 @@
1
+ module Effective
2
+ class PostmarkMailer < EffectivePostmark.parent_mailer_class
3
+ include EffectiveMailer
4
+ include EffectivePostmarkMailer
5
+
6
+ def reactivated(resource, opts = {})
7
+ raise('expected an effective_postmark_user') unless resource.class.try(:effective_postmark_user?)
8
+
9
+ @user = resource
10
+ subject = subject_for(__method__, 'Your email address has been reactivated', resource, opts)
11
+ headers = headers_for(resource, opts)
12
+
13
+ mail(to: resource.email, subject: subject, **headers)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,62 @@
1
+ # EffectivePostmarkUsr
2
+ #
3
+ # Mark your user model with effective_postmark_user
4
+ #
5
+ # #<Postmark::InactiveRecipientError: You tried to send to recipient(s) that have been marked as inactive. Found inactive addresses: test@bounce-testing.postmarkapp.com. Inactive recipients are ones that have generated a hard bounce, a spam complaint, or a manual suppression.>
6
+
7
+ module EffectivePostmarkUser
8
+ extend ActiveSupport::Concern
9
+
10
+ module Base
11
+ def effective_postmark_user
12
+ include ::EffectivePostmarkUser
13
+ end
14
+ end
15
+
16
+ included do
17
+ effective_resource do
18
+ postmark_error :string
19
+ postmark_error_at :datetime
20
+ end
21
+
22
+ before_validation(if: -> { email_changed? && postmark_invalid? }) do
23
+ assign_attributes(postmark_error: nil, postmark_error_at: nil)
24
+ end
25
+
26
+ scope :postmark_inactive_recipients, -> { where.not(postmark_error_at: nil) }
27
+ end
28
+
29
+ module ClassMethods
30
+ def effective_postmark_user?; true; end
31
+ end
32
+
33
+ # Triggered by the EffectivePostmarkMailer concern when a Postmark::InactiveRecipientError is raised
34
+ def postmark_inactive_recipient!
35
+ return unless postmark_valid? # If we already marked invalid, don't mark again
36
+
37
+ update_columns(postmark_error: 'Inactive Recipient', postmark_error_at: Time.zone.now)
38
+ end
39
+
40
+ # Triggered by an admin to reactivate the email address
41
+ def postmark_reactivate!
42
+ # Make an API request to reactivate this user
43
+ EffectivePostmark.api.reactivate(self)
44
+
45
+ # Send a reactivation email.
46
+ # This could fail again and call postmark_inactive_recipient! behind the scenes
47
+ message = EffectivePostmark.mailer_class.send(:reactivated, self).deliver_now
48
+ return false if message.kind_of?(Exception)
49
+
50
+ # This worked. We've been reactivated. Clear the error
51
+ update_columns(postmark_error: nil, postmark_error_at: nil)
52
+ end
53
+
54
+ def postmark_valid?
55
+ postmark_error.blank?
56
+ end
57
+
58
+ def postmark_invalid?
59
+ postmark_error.present?
60
+ end
61
+
62
+ end
@@ -0,0 +1,30 @@
1
+ # https://github.com/ActiveCampaign/postmark-gem/blob/main/lib/postmark/api_client.rb
2
+
3
+ module Effective
4
+ class PostmarkApi
5
+ attr_accessor :client
6
+
7
+ def initialize(api_token:)
8
+ raise('expected an api token') unless api_token.present?
9
+ @client = ::Postmark::ApiClient.new(api_token)
10
+ end
11
+
12
+ # EffectivePostmark.api.reactivate(email)
13
+ def reactivate(user)
14
+ raise('expected an effective_postmark_user') unless user.class.try(:effective_postmark_user?)
15
+
16
+ # Emails to reactivate
17
+ emails = [user.email, user.try(:alternate_email).presence].compact
18
+
19
+ # There are multiple streams. outbound / broadcast / inbound
20
+ begin
21
+ client.delete_suppressions(:outbound, emails)
22
+ client.delete_suppressions(:broadcast, emails)
23
+ true
24
+ rescue => e
25
+ false
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ %p Hello #{@user},
2
+
3
+ %p The email address associated with your account has been reactivated - you will now again receive emails from us and can use the password recovery page to sign in to your account.
4
+
5
+ %p Your email address will become inactive again if you mark an email from us as spam or if you click an unsubscribe link. To avoid becoming inactive, please add this email address to your contacts.
6
+
7
+ %p This is an automated message, please do not reply.
@@ -0,0 +1,23 @@
1
+ EffectivePostmark.setup do |config|
2
+ # Layout Settings
3
+ # Configure the Layout per controller, or all at once
4
+ # config.layout = { application: 'application', admin: 'admin' }
5
+
6
+ # API Key
7
+ config.api_token = ENV.fetch('POSTMARK_API_TOKEN')
8
+
9
+ # Mailer Settings
10
+ # Please see config/initializers/effective_resources.rb for default effective_* gem mailer settings
11
+ #
12
+ # Configure the class responsible to send e-mails.
13
+ # config.mailer = 'Effective::ClassifiedsMailer'
14
+ #
15
+ # Override effective_resource mailer defaults
16
+ #
17
+ # config.parent_mailer = nil # The parent class responsible for sending emails
18
+ # config.deliver_method = nil # The deliver method, deliver_later or deliver_now
19
+ # config.mailer_layout = nil # Default mailer layout
20
+ # config.mailer_sender = nil # Default From value
21
+ # config.mailer_admin = nil # Default To value for Admin correspondence
22
+ # config.mailer_subject = nil # Proc.new method used to customize Subject
23
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,17 @@
1
+ EffectivePostmark::Engine.routes.draw do
2
+ namespace :admin do
3
+ resources :postmark, only: [] do
4
+ post :postmark_reactivate, on: :member
5
+ end
6
+
7
+ resources :postmark_reports, only: [] do
8
+ collection do
9
+ get :inactive_recipients
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Rails.application.routes.draw do
16
+ mount EffectivePostmark::Engine => '/', as: 'effective_postmark'
17
+ end
@@ -0,0 +1,5 @@
1
+ class CreateEffectivePostmark < ActiveRecord::Migration[6.0]
2
+ def change
3
+ end
4
+ end
5
+
data/db/seeds.rb ADDED
@@ -0,0 +1 @@
1
+ puts "No seeds present"
@@ -0,0 +1,18 @@
1
+ module EffectivePostmark
2
+ class Engine < ::Rails::Engine
3
+ engine_name 'effective_postmark'
4
+
5
+ # Set up our default configuration options.
6
+ initializer 'effective_postmark.defaults', before: :load_config_initializers do |app|
7
+ eval File.read("#{config.root}/config/effective_postmark.rb")
8
+ end
9
+
10
+ # Include effective_postmark_user concern and allow any ActiveRecord object to call it
11
+ initializer 'effective_postmark.active_record' do |app|
12
+ app.config.to_prepare do
13
+ ActiveRecord::Base.extend(EffectivePostmarkUser::Base)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module EffectivePostmark
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,25 @@
1
+ require 'effective_resources'
2
+ require 'effective_logging'
3
+ require 'effective_postmark/engine'
4
+ require 'effective_postmark/version'
5
+
6
+ module EffectivePostmark
7
+
8
+ def self.config_keys
9
+ [
10
+ :mailer, :parent_mailer, :deliver_method, :mailer_layout, :mailer_sender, :mailer_admin, :mailer_subject,
11
+ :layout, :api_token
12
+ ]
13
+ end
14
+
15
+ include EffectiveGem
16
+
17
+ def self.api
18
+ Effective::PostmarkApi.new(api_token: api_token)
19
+ end
20
+
21
+ def self.mailer_class
22
+ mailer&.constantize || Effective::PostmarkMailer
23
+ end
24
+
25
+ end
@@ -0,0 +1,27 @@
1
+ module EffectivePostmark
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ desc 'Creates an EffectivePostmark initializer in your application.'
7
+
8
+ source_root File.expand_path('../../templates', __FILE__)
9
+
10
+ def self.next_migration_number(dirname)
11
+ unless ActiveRecord::Base.timestamped_migrations
12
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
13
+ else
14
+ "%.3d" % (current_migration_number(dirname) + 1)
15
+ end
16
+ end
17
+
18
+ def copy_initializer
19
+ template ('../' * 3) + 'config/effective_postmark.rb', 'config/initializers/effective_postmark.rb'
20
+ end
21
+
22
+ def create_migration_file
23
+ migration_template ('../' * 3) + 'db/migrate/101_create_effective_postmark.rb', 'db/migrate/create_effective_postmark.rb'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ namespace :effective_postmark do
2
+ end
metadata ADDED
@@ -0,0 +1,262 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: effective_postmark
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Code and Effect
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: postmark-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: effective_bootstrap
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: effective_datatables
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 4.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 4.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: effective_logging
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: effective_resources
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: devise
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: dotenv-rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: haml
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry-byebug
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: effective_test_bot
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: effective_developer
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: psych
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "<"
200
+ - !ruby/object:Gem::Version
201
+ version: '4'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "<"
207
+ - !ruby/object:Gem::Version
208
+ version: '4'
209
+ description: Effective Postmark captures Postmark::InvalidRecipientError and marks
210
+ Users as bounced
211
+ email:
212
+ - info@codeandeffect.com
213
+ executables: []
214
+ extensions: []
215
+ extra_rdoc_files: []
216
+ files:
217
+ - MIT-LICENSE
218
+ - README.md
219
+ - app/controllers/admin/postmark_controller.rb
220
+ - app/controllers/admin/postmark_reports_controller.rb
221
+ - app/datatables/admin/report_inactive_recipients_datatable.rb
222
+ - app/datatables/effective_postmark_datatable.rb
223
+ - app/helpers/effective_postmark_helper.rb
224
+ - app/mailers/concerns/effective_postmark_mailer.rb
225
+ - app/mailers/effective/postmark_mailer.rb
226
+ - app/models/concerns/effective_postmark_user.rb
227
+ - app/models/effective/postmark_api.rb
228
+ - app/views/effective/postmark_mailer/reactivated.html.haml
229
+ - config/effective_postmark.rb
230
+ - config/routes.rb
231
+ - db/migrate/101_create_effective_postmark.rb
232
+ - db/seeds.rb
233
+ - lib/effective_postmark.rb
234
+ - lib/effective_postmark/engine.rb
235
+ - lib/effective_postmark/version.rb
236
+ - lib/generators/effective_postmark/install_generator.rb
237
+ - lib/tasks/effective_postmark_tasks.rake
238
+ homepage: https://github.com/code-and-effect/effective_postmark
239
+ licenses:
240
+ - MIT
241
+ metadata: {}
242
+ post_install_message:
243
+ rdoc_options: []
244
+ require_paths:
245
+ - lib
246
+ required_ruby_version: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ required_rubygems_version: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - ">="
254
+ - !ruby/object:Gem::Version
255
+ version: '0'
256
+ requirements: []
257
+ rubygems_version: 3.3.7
258
+ signing_key:
259
+ specification_version: 4
260
+ summary: Effective Postmark captures Postmark::InvalidRecipientError and marks Users
261
+ as bounced
262
+ test_files: []