disclosure 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright 2013 Josh McArthur
2
+
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Disclosure
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Disclosure'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rspec/core/rake_task'
31
+
32
+ RSpec::Core::RakeTask.new(:spec)
33
+
34
+ task :default => :spec
@@ -0,0 +1,15 @@
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 vendor/assets/javascripts of plugins, if any, 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
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
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 vendor/assets/stylesheets of plugins, if any, 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 top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module Disclosure
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Disclosure
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,18 @@
1
+ module Disclosure
2
+ class EmailReactor < ActionMailer::Base
3
+ default Disclosure.configuration.email_reactor_defaults
4
+
5
+ def react!(model, action, rule)
6
+ self.notification(model, action, rule).deliver
7
+ end
8
+
9
+ def notification(model, action, rule)
10
+ mail(
11
+ :to => rule.owner.email,
12
+ :subject => t("disclosure.email_reactor.#{rule.notifier_class}.#{action}.subject"),
13
+ :template_path => "disclosure/email/#{rule.notifier_class}",
14
+ :template_name => "action"
15
+ )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,74 @@
1
+ module Disclosure
2
+ class Rule < ActiveRecord::Base
3
+ belongs_to :owner, :class_name => Disclosure.configuration.owner_class
4
+
5
+ validates :notifier_class, :inclusion => {
6
+ :in => proc { Disclosure.configuration.notifier_classes.map(&:name) }
7
+ }
8
+
9
+ validates :reactor_class, :inclusion => {
10
+ :in => proc { Disclosure.configuration.reactor_classes.map(&:name) }
11
+ }
12
+
13
+ validates :action, :uniqueness => {
14
+ :scope => [:owner_id, :notifier_class]
15
+ }
16
+
17
+ validate :action_in_notifier_actions
18
+
19
+ # Public: Find the notifier class instance from the
20
+ # class name (string) that is saved in the model table.
21
+ #
22
+ # Returns the notifier class or nil
23
+ def notifier
24
+ Disclosure.configuration.notifier_classes.select do |nc|
25
+ nc.name == self.notifier_class
26
+ end.first
27
+ end
28
+
29
+ # Public: Find the reactor class instance from the
30
+ # class name (string) that is saved in the model table.
31
+ #
32
+ # Returns the reactor class or nil
33
+ def reactor
34
+ Disclosure.configuration.reactor_classes.select do |rc|
35
+ rc.name == self.reactor_class
36
+ end.first
37
+ end
38
+
39
+ # Public: Tell the reactor for this rule that it should now react to a change.
40
+ #
41
+ # Passes along the instance of the model that changed, the action that occurred,
42
+ # and the owner of the rule
43
+ #
44
+ # model - The model instance that changed
45
+ #
46
+ # Returns truthy value if the reactor returned successfully, and false if not.
47
+ # Depending on the reactor, may raise exceptions - for example, Net::SMTP errors
48
+ # if sending email.
49
+ def react!(model)
50
+ reactor.react!(model, action, owner)
51
+ end
52
+
53
+ private
54
+
55
+ # Private: Ensure that the configured action is within the
56
+ # actions recorded against the notifier.
57
+ #
58
+ # For example, an Issue may notify actions such as:
59
+ # * created
60
+ # * closed
61
+ #
62
+ # While a Project may only notify creating and updating actions
63
+ #
64
+ # Returns true if the action is valid, or false if not
65
+ def action_in_notifier_actions
66
+ unless notifier && notifier.notifiable_actions.include?(self.action)
67
+ errors.add(:action, :not_in_notifiable_actions)
68
+ return false
69
+ end
70
+
71
+ return true
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Disclosure</title>
5
+ <%= stylesheet_link_tag "disclosure/application", :media => "all" %>
6
+ <%= javascript_include_tag "disclosure/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Disclosure::Engine.routes.draw do
2
+ # This engine has no routes, as it is more of a backend system for you to use
3
+ # in your own application as you need.
4
+ #
5
+ # If you would like to make your notification rules content-editable, that's really easy!
6
+ # Just add your own controller where you need it, and have it manage all the
7
+ # Disclosure::Rule models in your database!
8
+
9
+ end
@@ -0,0 +1,13 @@
1
+ class CreateDisclosureRules < ActiveRecord::Migration
2
+ def change
3
+ create_table :disclosure_rules do |t|
4
+ t.belongs_to :owner
5
+ t.string :notifier_class
6
+ t.string :reactor_class
7
+ t.string :action
8
+
9
+ t.timestamps
10
+ end
11
+ add_index :disclosure_rules, :owner_id
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ class Disclosure::Configuration
2
+ attr_accessor :owner_class, :notifier_classes, :reactor_classes, :email_reactor_defaults
3
+
4
+ def initialize
5
+ self.owner_class = "User"
6
+ self.reactor_classes = []
7
+ self.notifier_classes = []
8
+ self.email_reactor_defaults = {:from => "please-change-me@localhost"}
9
+ end
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ module Disclosure
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Disclosure
4
+
5
+ initializer 'disclosure.subscribe_to_model_events' do
6
+ ActiveSupport::Notifications.subscribe "disclosure.model_saved" do |name, start, finish, id, payload|
7
+ Disclosure.react_to!(payload[:model])
8
+ end
9
+ end
10
+
11
+ initializer 'disclosure.extend_model_classes' do
12
+ Disclosure.bootstrap!
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ class Disclosure::NotifiableActionsNotDefined < RuntimeError
2
+ end
3
+
4
+ class Disclosure::ActionMethodNotDefined < RuntimeError
5
+ end
@@ -0,0 +1,3 @@
1
+ module Disclosure
2
+ VERSION = "0.0.1"
3
+ end
data/lib/disclosure.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "disclosure/engine"
2
+ require 'disclosure/exceptions'
3
+ require "disclosure/configuration"
4
+
5
+ module Disclosure
6
+ class << self
7
+ attr_accessor :configuration
8
+ end
9
+
10
+ def self.bootstrap!
11
+ Disclosure.configuration.notifier_classes.each do |klass|
12
+ if !klass.methods.include?(:notifiable_actions)
13
+ klass.class_eval do
14
+ class << self
15
+ def notifiable_actions
16
+ raise Disclosure::NotifiableActionsNotDefined.new("Notifiable actions must be defined in #{self.name}.")
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # We don't use an else here, because we may have *just* added
23
+ # the method - it's not a if this, else that - we may need to do both
24
+ if klass.methods.include?(:notifiable_actions)
25
+ # If notifiable actions has just been defined, it will raise an exception
26
+ (klass.notifiable_actions rescue []).each do |action|
27
+ unless klass.instance_methods.include?(:"#{action}?")
28
+ klass.define_method(:"#{action}?") do
29
+ raise Disclosure::ActionMethodNotDefined.new("#{action}? must be defined in #{klass}.")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.react_to!(model)
38
+ unless Disclosure.configuration.notifier_classes.include?(model.class)
39
+ return nil
40
+ end
41
+
42
+ Disclosure::Rule.where(
43
+ :notifier_class => model.class.name,
44
+ :owner_id => model.send("#{Disclosure.configuration.owner_class.underscore}_id")
45
+ ).each do |rule|
46
+ next if !model.send("#{rule.action}?")
47
+ rule.react!(model)
48
+ end
49
+ end
50
+
51
+ def self.configuration
52
+ @configuration ||= Configuration.new
53
+ end
54
+
55
+ def self.configure
56
+ yield(configuration) if block_given?
57
+ end
58
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :disclosure do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: disclosure
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh McArthur
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.12
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.12
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A Rails engine to allow you to easily set up rules and events for when
63
+ each user should receive notifications - great for adding configurable notifications.
64
+ email:
65
+ - joshua.mcarthur+disclosure@gmail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - app/assets/javascripts/disclosure/application.js
71
+ - app/assets/stylesheets/disclosure/application.css
72
+ - app/controllers/disclosure/application_controller.rb
73
+ - app/helpers/disclosure/application_helper.rb
74
+ - app/mailers/disclosure/email_reactor.rb
75
+ - app/models/disclosure/rule.rb
76
+ - app/views/layouts/disclosure/application.html.erb
77
+ - config/routes.rb
78
+ - db/migrate/20130331060054_create_disclosure_rules.rb
79
+ - lib/disclosure/configuration.rb
80
+ - lib/disclosure/engine.rb
81
+ - lib/disclosure/exceptions.rb
82
+ - lib/disclosure/version.rb
83
+ - lib/disclosure.rb
84
+ - lib/tasks/disclosure_tasks.rake
85
+ - MIT-LICENSE
86
+ - Rakefile
87
+ - README.rdoc
88
+ homepage: https://github.com/joshmcarthur/disclosure
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ segments:
101
+ - 0
102
+ hash: -1278575994465868298
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ segments:
110
+ - 0
111
+ hash: -1278575994465868298
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.23
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: A Rails engine to allow you to easily set up rules and events for notificaitons.
118
+ test_files: []