acts_as_warnable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +27 -0
- data/app/assets/javascripts/acts_as_warnable/application.js +13 -0
- data/app/assets/stylesheets/acts_as_warnable/application.css +15 -0
- data/app/controllers/acts_as_warnable/application_controller.rb +4 -0
- data/app/controllers/acts_as_warnable/warning_emails_controller.rb +35 -0
- data/app/controllers/acts_as_warnable/warnings_controller.rb +70 -0
- data/app/helpers/acts_as_warnable/application_helper.rb +15 -0
- data/app/mailers/warning_email_mailer.rb +13 -0
- data/app/models/warning.rb +56 -0
- data/app/models/warning_email.rb +55 -0
- data/app/validators/name_and_email_validator.rb +9 -0
- data/app/views/acts_as_warnable/warning_emails/_form.html.erb +19 -0
- data/app/views/acts_as_warnable/warning_emails/edit.html.erb +2 -0
- data/app/views/acts_as_warnable/warning_emails/index.html.erb +37 -0
- data/app/views/acts_as_warnable/warning_emails/new.html.erb +2 -0
- data/app/views/acts_as_warnable/warnings/index.html.erb +37 -0
- data/app/views/acts_as_warnable/warnings/show.html.erb +17 -0
- data/app/views/layouts/acts_as_warnable/application.html.erb +14 -0
- data/app/views/public_activity/warning/_dismiss.html.erb +11 -0
- data/app/views/public_activity/warning/_issue.html.erb +13 -0
- data/app/views/warning_email_mailer/send_report.html.erb +26 -0
- data/config/routes.rb +4 -0
- data/lib/acts_as_warnable.rb +55 -0
- data/lib/acts_as_warnable/engine.rb +10 -0
- data/lib/acts_as_warnable/rails/routes.rb +18 -0
- data/lib/acts_as_warnable/version.rb +3 -0
- data/lib/generators/acts_as_warnable/warning_email_thread_generator.rb +9 -0
- data/lib/generators/acts_as_warnable/warning_generator.rb +9 -0
- data/lib/generators/templates/email_initializer.rb +15 -0
- data/lib/tasks/acts_as_warnable_tasks.rake +4 -0
- metadata +173 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d197bb659d0bf521569b61ebfa66a8d062c52bdd
|
|
4
|
+
data.tar.gz: e92d4711cd67f9e4b5ef5482964381efca51c7aa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 79a8ac2bd90b660996be0ac7733cf44ccae1026a654997b493736c2d44594e4c3d5a23b217bbb0939dce48b0a8f48721645f09c6a837260d82b82d6c3ff446b1
|
|
7
|
+
data.tar.gz: e4233b6f27e12180a56a6a28a7009f3b6b4119a24566de5d0f6cd71daff4314f6c5237927d810dde521bede55189b726b46ed3302cb516cd0b4c7971c9cc9ed0
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2015 Nigel Baillie
|
|
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/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'ActsAsWarnable'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
load 'rails/tasks/statistics.rake'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Bundler::GemHelper.install_tasks
|
|
26
|
+
|
|
27
|
+
require 'rake/testtask'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
|
2
|
+
// listed below.
|
|
3
|
+
//
|
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
|
6
|
+
//
|
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
8
|
+
// compiled file.
|
|
9
|
+
//
|
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
|
11
|
+
// about supported directives.
|
|
12
|
+
//
|
|
13
|
+
//= require_tree .
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
|
11
|
+
* file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module ActsAsWarnable
|
|
2
|
+
class WarningEmailsController < ::InheritedResources::Base
|
|
3
|
+
respond_to :html
|
|
4
|
+
|
|
5
|
+
helper do
|
|
6
|
+
def method_missing(name, *args, &block)
|
|
7
|
+
if main_app.respond_to?(name)
|
|
8
|
+
main_app.send(name, *args, &block)
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def warnable_classes
|
|
15
|
+
::ActiveRecord::Base.descendants.select do |c|
|
|
16
|
+
c.reflect_on_all_associations.any? { |a| a.name == :warnings }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def show
|
|
22
|
+
redirect_to action: :edit
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
|
|
27
|
+
def permitted_params
|
|
28
|
+
params.permit(
|
|
29
|
+
warning_email: [
|
|
30
|
+
:model, :recipient, :minutes, :url
|
|
31
|
+
]
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module ActsAsWarnable
|
|
2
|
+
class WarningsController < ::ApplicationController
|
|
3
|
+
before_filter :fetch_warning, only: [:update, :show]
|
|
4
|
+
respond_to :html, :js
|
|
5
|
+
|
|
6
|
+
helper do
|
|
7
|
+
def method_missing(name, *args, &block)
|
|
8
|
+
if main_app.respond_to?(name)
|
|
9
|
+
main_app.send(name, *args, &block)
|
|
10
|
+
else
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def index
|
|
17
|
+
@warnings = Warning.all
|
|
18
|
+
@warnings = @warnings.active if params[:active_only]
|
|
19
|
+
|
|
20
|
+
if params.key?(:warnable_id) && params.key?(:warnable_type)
|
|
21
|
+
@warnings = @warnings.where(warnable_id: params[:warnable_id], warnable_type: params[:warnable_type])
|
|
22
|
+
|
|
23
|
+
@warnable = params[:warnable_type].constantize.find(params[:warnable_id])
|
|
24
|
+
instance_variable_set('@'+params[:warnable_type].underscore, @warnable)
|
|
25
|
+
|
|
26
|
+
warnables = params[:warnable_type].underscore.pluralize
|
|
27
|
+
if lookup_context.exists?('warnings', warnables, false)
|
|
28
|
+
render "#{warnables}/warnings"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update
|
|
34
|
+
was_dismissed = @warning.dismissed?
|
|
35
|
+
if @warning.update_attributes(permitted_params[:warning])
|
|
36
|
+
if !was_dismissed && @warning.dismissed?
|
|
37
|
+
flash[:success] = "Warning dismissed"
|
|
38
|
+
else
|
|
39
|
+
flash[:success] = "Successfully updated warning"
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
flash[:error] = "Failed to update warning: #{@warning.errors.full_messages.join(', ')}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if params[:redirect_to]
|
|
46
|
+
redirect_to params[:redirect_to]
|
|
47
|
+
else
|
|
48
|
+
redirect_to warnings_path
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show
|
|
53
|
+
render params[:render] if params.key?(:render)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
protected
|
|
57
|
+
|
|
58
|
+
def fetch_warning
|
|
59
|
+
@warning = Warning.find(params[:id])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def permitted_params
|
|
63
|
+
params.permit(
|
|
64
|
+
warning: [
|
|
65
|
+
:warnable_id, :warnable_type, :message, :dismissed_at, :dismisser_id
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ActsAsWarnable
|
|
2
|
+
module ApplicationHelper
|
|
3
|
+
def current_devise_user
|
|
4
|
+
send("current_#{Devise.mappings.values.first.name.to_s.underscore.singularize}")
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def button_to_dismiss_warning(warning, options = {})
|
|
8
|
+
form_for(warning, options) do |f|
|
|
9
|
+
f.hidden_field(:dismisser_id, value: current_devise_user.id) +
|
|
10
|
+
hidden_field_tag(:redirect_to, request.fullpath) +
|
|
11
|
+
f.submit('Dismiss', class: 'btn btn-warning')
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class WarningEmailMailer < ActionMailer::Base
|
|
2
|
+
def send_report(warning_email, warnings)
|
|
3
|
+
@warning_email = warning_email
|
|
4
|
+
@warnings = warnings
|
|
5
|
+
@site_url = defined?(Figaro) ? Figaro.env.site_url : ENV['SITE_URL']
|
|
6
|
+
|
|
7
|
+
mail(
|
|
8
|
+
to: @warning_email.recipient,
|
|
9
|
+
from: (defined?(Figaro) ? Figaro.env.warning_email_sender : ENV['WARNING_EMAIL_SENDER']) || raise('Please set ENV["WARNING_EMAIL_SENDER"]'),
|
|
10
|
+
subject: "[#{Time.now.strftime('%D %r')}] There are outstanding warnings for #{@warning_email.model}",
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
class Warning < ActiveRecord::Base
|
|
2
|
+
def self.dismisser_class
|
|
3
|
+
Devise.mappings.values.first.class_name
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
belongs_to :warnable, polymorphic: true
|
|
7
|
+
if Devise.mappings.size > 1
|
|
8
|
+
Rails.logger.warn "acts_as_warnable does not currently support multiple Devise models. "\
|
|
9
|
+
"Only #{dismisser_class} will be able to dismiss warnings."
|
|
10
|
+
end
|
|
11
|
+
belongs_to :dismisser, class_name: dismisser_class
|
|
12
|
+
|
|
13
|
+
validates :message, :source, presence: true
|
|
14
|
+
|
|
15
|
+
before_save :check_if_dismissed
|
|
16
|
+
after_save :fire_dismissal_activity, if: :just_dismissed?
|
|
17
|
+
|
|
18
|
+
def self.active
|
|
19
|
+
where(dismissed_at: nil)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def dismiss(user)
|
|
23
|
+
update_attributes dismissed_at: Time.now, dismisser: user
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dismissed?
|
|
27
|
+
!dismissed_at.nil? && !dismisser.nil?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def just_dismissed?
|
|
31
|
+
@just_dismissed
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
protected
|
|
35
|
+
|
|
36
|
+
def fire_dismissal_activity
|
|
37
|
+
return unless warnable.respond_to?(:create_activity)
|
|
38
|
+
|
|
39
|
+
warnable.create_activity(
|
|
40
|
+
key: 'warning.dismiss',
|
|
41
|
+
recipient: self,
|
|
42
|
+
owner: dismisser
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def check_if_dismissed
|
|
47
|
+
if dismisser_id_changed?
|
|
48
|
+
if dismisser_id.nil?
|
|
49
|
+
self.dismissed_at = nil
|
|
50
|
+
else
|
|
51
|
+
self.dismissed_at = Time.now
|
|
52
|
+
@just_dismissed = true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class WarningEmail < ActiveRecord::Base
|
|
2
|
+
validates :recipient, name_and_email: true
|
|
3
|
+
validate :model_is_actually_a_model
|
|
4
|
+
|
|
5
|
+
def warnings
|
|
6
|
+
Warning.where(warnable_type: model, dismissed_at: nil)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def send_report
|
|
10
|
+
WarningEmailMailer.send_report(self, warnings).deliver
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.spawn_sender_thread
|
|
14
|
+
Thread.main[:warning_email_sender] = Thread.new do
|
|
15
|
+
loop do
|
|
16
|
+
begin
|
|
17
|
+
warning_times = {}
|
|
18
|
+
|
|
19
|
+
min_wait_time = 600
|
|
20
|
+
WarningEmail.find_each do |warning_email|
|
|
21
|
+
next if warning_email.minutes.blank?
|
|
22
|
+
|
|
23
|
+
warning_times[warning_email.id] ||= Time.now.to_i
|
|
24
|
+
wait_time = warning_email.minutes * 60.0 - (Time.now.to_i - warning_times[warning_email.id])
|
|
25
|
+
|
|
26
|
+
if wait_time < 0
|
|
27
|
+
warning_email.send_report if warning_email.warnings.any?
|
|
28
|
+
warning_times[warning_email.id] = Time.now.to_i
|
|
29
|
+
wait_time = warning_email.minutes * 60.0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
min_wait_time = wait_time if wait_time < min_wait_time
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
ActiveRecord::Base.clear_active_connections!
|
|
36
|
+
sleep min_wait_time.seconds + 1
|
|
37
|
+
rescue StandardError => e
|
|
38
|
+
Rails.logger.error "WARNING_EMAILS ERROR DURING LOOP #{e.class.name}: #{e.message}"
|
|
39
|
+
|
|
40
|
+
ActiveRecord::Base.clear_active_connections!
|
|
41
|
+
sleep 1.minute
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def model_is_actually_a_model
|
|
50
|
+
model_class = model.constantize
|
|
51
|
+
unless model_class.ancestors.include?(ActiveRecord::Base)
|
|
52
|
+
errors.add 'model', 'must correspond to a valid model'
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
unless defined?(NameAndEmailValidator)
|
|
2
|
+
class NameAndEmailValidator < ActiveModel::EachValidator
|
|
3
|
+
def validate_each(record, attribute, value)
|
|
4
|
+
unless value =~ /\A(.+)?\s<([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})?>\Z/i
|
|
5
|
+
record.errors[attribute] << (options[:message] || "is not a valid email address. Proper format is 'Name First <email@second.com>' separated by commas ")
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%= link_to 'List', ActsAsWarnable::Engine.routes.url_helpers.warning_emails_path, class: 'btn btn-sm btn-primary' %>
|
|
2
|
+
|
|
3
|
+
<%= form_for warning_email do |f| %>
|
|
4
|
+
<%= f.label :model, class: 'control-label' %>
|
|
5
|
+
<%= f.select :model, options_for_select(warnable_classes.map(&:name), f.object.try(:model)), {}, class: 'form-control' %>
|
|
6
|
+
|
|
7
|
+
<%= f.label :recipient, class: 'control-label' %>
|
|
8
|
+
<%= f.text_field :recipient, class: 'form-control' %>
|
|
9
|
+
|
|
10
|
+
<%= f.label :minutes, 'Send Every', class: 'control-label' %>
|
|
11
|
+
<div class='input-group'>
|
|
12
|
+
<%= f.text_field :minutes, class: 'form-control' %>
|
|
13
|
+
<span class='input-group-addon'>
|
|
14
|
+
minutes
|
|
15
|
+
</span>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<%= f.submit class: 'btn btn-primary' %>
|
|
19
|
+
<% end %>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<h1>Warning Emails</h1>
|
|
2
|
+
|
|
3
|
+
<dl>
|
|
4
|
+
<dt>Dispatcher Thread Status</dt>
|
|
5
|
+
<dd><%= Thread.main[:warning_email_sender].try(:status).try(:humanize) || '<em>Not Running</em>'.html_safe %></dd>
|
|
6
|
+
</dl>
|
|
7
|
+
|
|
8
|
+
<%= link_to 'New Warning Email', ActsAsWarnable::Engine.routes.url_helpers.new_warning_email_path, class: 'btn btn-primary' %>
|
|
9
|
+
|
|
10
|
+
<table class="table table-striped">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>Model</th>
|
|
14
|
+
<th>Recipient</th>
|
|
15
|
+
<th>Send Interval</th>
|
|
16
|
+
<th>Actions</th>
|
|
17
|
+
</tr>
|
|
18
|
+
|
|
19
|
+
<tbody>
|
|
20
|
+
<% @warning_emails.each do |warning_email| %>
|
|
21
|
+
<tr>
|
|
22
|
+
<td><%= warning_email.model %></td>
|
|
23
|
+
<td><%= warning_email.recipient %></td>
|
|
24
|
+
<td><%= warning_email.minutes || '<not set>' %> minutes</td>
|
|
25
|
+
<td>
|
|
26
|
+
<%= link_to(ActsAsWarnable::Engine.routes.url_helpers.edit_warning_email_path(warning_email), data: { toggle: 'tooltip' }, class: 'btn btn-xs btn-warning', title: 'Edit') do %>
|
|
27
|
+
<%= content_tag :i, '', class: 'glyphicon glyphicon-edit' %>
|
|
28
|
+
<% end %>
|
|
29
|
+
<%= link_to(warning_email, method: :delete, data: { toggle: 'tooltip' }, class: 'btn btn-xs btn-danger', title: 'Destroy') do %>
|
|
30
|
+
<%= content_tag :i, '', class: 'glyphicon glyphicon-remove-circle' %>
|
|
31
|
+
<% end %>
|
|
32
|
+
</td>
|
|
33
|
+
</tr>
|
|
34
|
+
<% end %>
|
|
35
|
+
</tbody>
|
|
36
|
+
</thead>
|
|
37
|
+
</table>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<% if params[:warnable_type] %>
|
|
2
|
+
<h1>
|
|
3
|
+
<%= link_to "#{params[:warnable_type].humanize} ##{params[:warnable_id]}", @warnable %> Warnings
|
|
4
|
+
</h1>
|
|
5
|
+
<% else %>
|
|
6
|
+
<h1>Warnings</h1>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<% if params[:active_only] %>
|
|
10
|
+
<% if params[:warnable_type] %>
|
|
11
|
+
<%= link_to "Show dismissed", send("#{params[:warnable_type].underscore}_warnings_path", params[:warnable_id]), { class: 'btn btn-info' } %>
|
|
12
|
+
<% else %>
|
|
13
|
+
<%= link_to "Show dismissed", ActsAsWarnable::Engine.routes.url_helpers.warnings_path, { class: 'btn btn-info' } %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% else %>
|
|
16
|
+
<% if params[:warnable_type] %>
|
|
17
|
+
<%= link_to "Hide dismissed", send("#{params[:warnable_type].underscore}_warnings_path", params[:warnable_id], active_only: true), { class: 'btn btn-info' } %>
|
|
18
|
+
<% else %>
|
|
19
|
+
<%= link_to "Hide dismissed", ActsAsWarnable::Engine.routes.url_helpers.warnings_path(active_only: true), { class: 'btn btn-info' } %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<ul>
|
|
24
|
+
<% @warnings.each do |warning| %>
|
|
25
|
+
<li>
|
|
26
|
+
<% if warning.respond_to?(:created_at) && warning.created_at %>
|
|
27
|
+
(<%= warning.created_at %>)
|
|
28
|
+
<% end %>
|
|
29
|
+
<%= link_to "##{warning.id}: (#{warning.source}) #{warning.message.truncate(50)}", ActsAsWarnable::Engine.routes.url_helpers.warning_path(warning) %>
|
|
30
|
+
<% if warning.dismissed? %>
|
|
31
|
+
<%= "(dismissed by #{warning.dismisser.try(:full_name) || warning.dismisser.try(:email) || 'nobody'} on #{warning.dismissed_at.strftime('%c')})" if warning.dismissed? %>
|
|
32
|
+
<% else %>
|
|
33
|
+
<%= button_to_dismiss_warning(warning) %>
|
|
34
|
+
<% end %>
|
|
35
|
+
</li>
|
|
36
|
+
<% end %>
|
|
37
|
+
</ul>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<h1>Warning #<%= @warning.id %> (<%= @warning.source %>) from <%= link_to "#{@warning.warnable_type.underscore.humanize} ##{@warning.warnable_id}", @warning.warnable %></h1>
|
|
2
|
+
<% if @warning.respond_to?(:created_at) && @warning.created_at %>
|
|
3
|
+
(<%= warning.created_at %>)
|
|
4
|
+
<% end %>
|
|
5
|
+
<div class='well'>
|
|
6
|
+
<% @warning.message.each_line do |line| %>
|
|
7
|
+
<p style='word-wrap: break-word'><%= line %></p>
|
|
8
|
+
<% end %>
|
|
9
|
+
</div>
|
|
10
|
+
<div class=''>
|
|
11
|
+
<% if @warning.dismissed? %>
|
|
12
|
+
Dismissed by <%= @warning.dismisser.try(:full_name) || @warning.dismisser.try(:email) || 'nobody' %>
|
|
13
|
+
on <%= @warning.dismissed_at.strftime('%c') %>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= button_to_dismiss_warning(@warning) %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>ActsAsWarnable</title>
|
|
5
|
+
<%= stylesheet_link_tag "acts_as_warnable/application", media: "all" %>
|
|
6
|
+
<%= javascript_include_tag "acts_as_warnable/application" %>
|
|
7
|
+
<%= csrf_meta_tags %>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
|
|
11
|
+
<%= yield %>
|
|
12
|
+
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<% if activity.recipient %>
|
|
2
|
+
<%= link_to "Warning ##{activity.recipient_id}", ActsAsWarnable::Engine.routes.url_helpers.warning_path(activity.recipient_id) %> from "<%= activity.recipient.source %>"
|
|
3
|
+
was dismissed by <%= activity.owner.try(:full_name) || activity.owner.try(:email) || 'Someone' %>
|
|
4
|
+
<% else %>
|
|
5
|
+
Unknown warning was dismissed
|
|
6
|
+
<% if activity.owner %>
|
|
7
|
+
by <%= activity.owner.try(:full_name) || activity.owner.try(:email) || "Someone (#{activity.owner.inspect})" %>
|
|
8
|
+
<% else %>
|
|
9
|
+
and we don't know who did it.
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<% if activity.recipient %>
|
|
2
|
+
<div>
|
|
3
|
+
"<%= activity.recipient.source %>" issued
|
|
4
|
+
<%= link_to "a warning (##{activity.recipient_id})", ActsAsWarnable::Engine.routes.url_helpers.warning_path(activity.recipient) %>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div>
|
|
8
|
+
"<%= activity.recipient.message.truncate(40) %>"
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<% else %>
|
|
12
|
+
Unknown warning was issued.
|
|
13
|
+
<% end %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<h3>The following warnings have not been dismissed</h3>
|
|
2
|
+
|
|
3
|
+
<% if @site_url.blank? %>
|
|
4
|
+
<p>(Set ENV["SITE_URL"] to have these list entries link to their warning page for dismissal.)</p>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<ul>
|
|
8
|
+
<% @warnings.each do |warning| %>
|
|
9
|
+
<li>
|
|
10
|
+
<ul>
|
|
11
|
+
<li>
|
|
12
|
+
<% if @site_url.blank? %>
|
|
13
|
+
<span>#<%= warning.id %>: (<%= warning.source %>)</span>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= link_to "##{warning.id}: (#{warning.source})", @site_url + ActsAsWarnable::Engine.routes.url_helpers.warning_path(warning.id) %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
<% warning.message.each_line do |line| %>
|
|
20
|
+
<p style='word-wrap: break-word'><%= line %></p>
|
|
21
|
+
<% end %>
|
|
22
|
+
</li>
|
|
23
|
+
</ul>
|
|
24
|
+
</li>
|
|
25
|
+
<% end %>
|
|
26
|
+
</ul>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'acts_as_warnable/version'
|
|
2
|
+
require 'acts_as_warnable/engine'
|
|
3
|
+
require 'acts_as_warnable/rails/routes'
|
|
4
|
+
|
|
5
|
+
module ActsAsWarnable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def acts_as_warnable(options = {})
|
|
10
|
+
has_many :warnings, as: :warnable
|
|
11
|
+
include WarnableInstanceMethods
|
|
12
|
+
extend WarnableClassMethods
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module WarnableClassMethods
|
|
17
|
+
def warn_on_failure_of(*methods)
|
|
18
|
+
options = methods.last.is_a?(Hash) ? methods.pop : {}
|
|
19
|
+
|
|
20
|
+
methods.each do |method_name|
|
|
21
|
+
define_method "#{method_name}_with_warning" do |*args, &block|
|
|
22
|
+
begin
|
|
23
|
+
send("#{method_name}_without_warning", *args, &block)
|
|
24
|
+
rescue Exception => e
|
|
25
|
+
issue_warning(
|
|
26
|
+
warning_source(method_name),
|
|
27
|
+
"#{e.class.name}: #{e.message}\n\n#{e.backtrace.join("\n")}"
|
|
28
|
+
)
|
|
29
|
+
raise if options[:raise_anyway]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
alias_method_chain method_name, :warning
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module WarnableInstanceMethods
|
|
38
|
+
def warning_source(method_name)
|
|
39
|
+
"#{self.class.name}##{method_name}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def issue_warning(source, message)
|
|
43
|
+
warning = warnings.create(source: source, message: message)
|
|
44
|
+
|
|
45
|
+
if respond_to?(:create_activity)
|
|
46
|
+
create_activity(
|
|
47
|
+
key: 'warning.issue',
|
|
48
|
+
recipient: warning
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ActiveRecord::Base.send :include, ActsAsWarnable
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module ActsAsWarnable
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace ActsAsWarnable
|
|
4
|
+
initializer 'acts_as_warnable.action_controller' do |app|
|
|
5
|
+
ActiveSupport.on_load :action_controller do
|
|
6
|
+
helper ActsAsWarnable::ApplicationHelper
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module ActionDispatch
|
|
2
|
+
module Routing
|
|
3
|
+
class Mapper
|
|
4
|
+
def warning_paths_for(*resources)
|
|
5
|
+
resources.map(&:to_s).each do |resource|
|
|
6
|
+
get "/#{resource.pluralize}/:warnable_id/warnings",
|
|
7
|
+
to: 'acts_as_warnable/warnings#index',
|
|
8
|
+
defaults: { warnable_type: resource.singularize.camelize },
|
|
9
|
+
as: "#{resource.singularize}_warnings"
|
|
10
|
+
|
|
11
|
+
post "/#{resource.pluralize}/:warnable_id/warnings",
|
|
12
|
+
to: 'acts_as_warnable/warnings#post',
|
|
13
|
+
defaults: { warnable_type: resource.singularize.camelize }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class ActsAsWarnable::WarningEmailThreadGenerator < Rails::Generators::Base
|
|
2
|
+
desc "Generates an initializer that kicks off a thread that sends warning emails on their intervals."
|
|
3
|
+
|
|
4
|
+
source_root File.expand_path("../../templates", __FILE__)
|
|
5
|
+
|
|
6
|
+
def create_initializer
|
|
7
|
+
copy_file 'email_initializer.rb', 'config/initializers/send_warning_emails.rb'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class ActsAsWarnable::WarningGenerator < Rails::Generators::Base
|
|
2
|
+
desc "Generates the migration for warnings"
|
|
3
|
+
|
|
4
|
+
def create_migrations
|
|
5
|
+
generate 'migration', 'CreateWarnings warnable_id:integer warnable_type:string source:string message:text dismissed_at:datetime dismisser_id:integer'
|
|
6
|
+
generate 'migration', 'AddTimestampsToWarnings created_at:datetime updated_at:datetime'
|
|
7
|
+
generate 'migration', 'CreateWarningEmails model:string minutes:decimal\\{10,2\\} recipient:string url:string'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
### Here we spawn a thread that sends warning emails on their set intervals ###
|
|
2
|
+
|
|
3
|
+
# You may modify this to your needs
|
|
4
|
+
conditions = [
|
|
5
|
+
# Only send emails on production
|
|
6
|
+
Rails.env.production?,
|
|
7
|
+
|
|
8
|
+
# Don't send emails from rails console sessions
|
|
9
|
+
!defined?(Rails::Console),
|
|
10
|
+
|
|
11
|
+
# Don't send emails from rake tasks
|
|
12
|
+
$0 !~ /rake$/
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
WarningEmail.spawn_sender_thread if conditions.all?
|
metadata
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: acts_as_warnable
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nigel Baillie
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-08-28 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: 4.2.1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 4.2.1
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: devise
|
|
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: inherited_resources
|
|
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: sqlite3
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec-rails
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: factory_girl_rails
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 4.2.0
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 4.2.0
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: shoulda-matchers
|
|
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
|
+
description: Allows models to issue warnings if a task goes wrong
|
|
112
|
+
email:
|
|
113
|
+
- metreckk@gmail.com
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- MIT-LICENSE
|
|
119
|
+
- Rakefile
|
|
120
|
+
- app/assets/javascripts/acts_as_warnable/application.js
|
|
121
|
+
- app/assets/stylesheets/acts_as_warnable/application.css
|
|
122
|
+
- app/controllers/acts_as_warnable/application_controller.rb
|
|
123
|
+
- app/controllers/acts_as_warnable/warning_emails_controller.rb
|
|
124
|
+
- app/controllers/acts_as_warnable/warnings_controller.rb
|
|
125
|
+
- app/helpers/acts_as_warnable/application_helper.rb
|
|
126
|
+
- app/mailers/warning_email_mailer.rb
|
|
127
|
+
- app/models/warning.rb
|
|
128
|
+
- app/models/warning_email.rb
|
|
129
|
+
- app/validators/name_and_email_validator.rb
|
|
130
|
+
- app/views/acts_as_warnable/warning_emails/_form.html.erb
|
|
131
|
+
- app/views/acts_as_warnable/warning_emails/edit.html.erb
|
|
132
|
+
- app/views/acts_as_warnable/warning_emails/index.html.erb
|
|
133
|
+
- app/views/acts_as_warnable/warning_emails/new.html.erb
|
|
134
|
+
- app/views/acts_as_warnable/warnings/index.html.erb
|
|
135
|
+
- app/views/acts_as_warnable/warnings/show.html.erb
|
|
136
|
+
- app/views/layouts/acts_as_warnable/application.html.erb
|
|
137
|
+
- app/views/public_activity/warning/_dismiss.html.erb
|
|
138
|
+
- app/views/public_activity/warning/_issue.html.erb
|
|
139
|
+
- app/views/warning_email_mailer/send_report.html.erb
|
|
140
|
+
- config/routes.rb
|
|
141
|
+
- lib/acts_as_warnable.rb
|
|
142
|
+
- lib/acts_as_warnable/engine.rb
|
|
143
|
+
- lib/acts_as_warnable/rails/routes.rb
|
|
144
|
+
- lib/acts_as_warnable/version.rb
|
|
145
|
+
- lib/generators/acts_as_warnable/warning_email_thread_generator.rb
|
|
146
|
+
- lib/generators/acts_as_warnable/warning_generator.rb
|
|
147
|
+
- lib/generators/templates/email_initializer.rb
|
|
148
|
+
- lib/tasks/acts_as_warnable_tasks.rake
|
|
149
|
+
homepage:
|
|
150
|
+
licenses:
|
|
151
|
+
- MIT
|
|
152
|
+
metadata: {}
|
|
153
|
+
post_install_message:
|
|
154
|
+
rdoc_options: []
|
|
155
|
+
require_paths:
|
|
156
|
+
- lib
|
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
|
+
requirements:
|
|
159
|
+
- - ">="
|
|
160
|
+
- !ruby/object:Gem::Version
|
|
161
|
+
version: '0'
|
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
requirements: []
|
|
168
|
+
rubyforge_project:
|
|
169
|
+
rubygems_version: 2.4.8
|
|
170
|
+
signing_key:
|
|
171
|
+
specification_version: 4
|
|
172
|
+
summary: Allows models to issue warnings if a task goes wrong
|
|
173
|
+
test_files: []
|