mail-control 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/.document +5 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +145 -0
- data/Rakefile +11 -0
- data/lib/generators/mail-control.rb +14 -0
- data/lib/generators/mail-control/activity/activity_generator.rb +17 -0
- data/lib/generators/mail-control/activity/templates/logged_email.rb +5 -0
- data/lib/generators/mail-control/migration/migration_generator.rb +17 -0
- data/lib/generators/mail-control/migration/templates/migration.rb +33 -0
- data/lib/mail-control.rb +9 -0
- data/lib/mail-control/actor.rb +58 -0
- data/lib/mail-control/definition.rb +52 -0
- data/lib/mail-control/definition_dsl.rb +49 -0
- data/lib/mail-control/errors.rb +44 -0
- data/lib/mail-control/logged_email.rb +216 -0
- data/lib/mail-control/version.rb +3 -0
- data/mail-control.gemspec +29 -0
- data/test/migrations/001_create_logged_emails.rb +33 -0
- data/test/migrations/002_create_articles.rb +11 -0
- data/test/migrations/003_create_users.rb +8 -0
- data/test/migrations/004_add_nonstandard_to_activities.rb +7 -0
- data/test/migrations/005_create_volumes.rb +9 -0
- data/test/test_helper.rb +81 -0
- data/test/unit/activity_test.rb +92 -0
- data/test/unit/actor_test.rb +46 -0
- data/test/unit/definition_dsl_test.rb +29 -0
- data/test/unit/definition_test.rb +51 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: caaf2a07f4cecd59194d9daf3e6d0e04ee9dedf7
|
4
|
+
data.tar.gz: 4c943c088a90bd54ad7e7f3ae62eaf943325ebce
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 776a0118e140c9692d61cb15b728a04b932697ac4091606fa537ae61eb097792140ebc12c11a6044dc990ecbc80d26d5b37b7d67d2a102ec4b4798bcd2f79663
|
7
|
+
data.tar.gz: 219ff9a4a205aea11a8144ea63164b575405dae75347dfb41275d7dae6a41a123dcd5889ce71446405b97063834500ac9e3710285964a9cfae137c399d1f6344
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Andreas Saebjoernsen
|
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,145 @@
|
|
1
|
+
# MailControl
|
2
|
+
|
3
|
+
MailControl is a simple gem for giving both your app and users control over when, if and how emails are sent out.
|
4
|
+
|
5
|
+
This gem is inspired by Streama by Christopher Pappas.
|
6
|
+
|
7
|
+
[](http://travis-ci.org/digitalplaywright/mail-control) [](https://gemnasium.com/digitalplaywright/mail-control) [](https://codeclimate.com/github/digitalplaywright/mail-control)
|
8
|
+
|
9
|
+
|
10
|
+
## Install
|
11
|
+
|
12
|
+
gem install mail-control
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
### Create migration for logged_emails and migrate the database (in your Rails project):
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
rails g mail-control:migration
|
20
|
+
rake db:migrate
|
21
|
+
```
|
22
|
+
|
23
|
+
### Define Queued Task
|
24
|
+
|
25
|
+
Create an LoggedEmail model and define the logged_emails and the fields you would like to cache within the queued_task.
|
26
|
+
|
27
|
+
An queued_task consists of an actor, a verb, an act_object, and a target.
|
28
|
+
|
29
|
+
``` ruby
|
30
|
+
class LoggedEmail < ActiveRecord::Base
|
31
|
+
include MailControl::LoggedEmail
|
32
|
+
|
33
|
+
queued_task :new_enquiry do
|
34
|
+
actor :User
|
35
|
+
act_object :Article
|
36
|
+
act_target :Volume
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
The queued_task verb is implied from the queued_task name, in the above example the verb is :new_enquiry
|
42
|
+
|
43
|
+
The act_object may be the entity performing the queued_task, or the entity on which the queued_task was performed.
|
44
|
+
e.g John(actor) shared a video(act_object)
|
45
|
+
|
46
|
+
The target is the act_object that the verb is enacted on.
|
47
|
+
e.g. Geraldine(actor) posted a photo(act_object) to her album(target)
|
48
|
+
|
49
|
+
This is based on the LoggedEmail Streams 1.0 specification (http://logged_emailstrea.ms)
|
50
|
+
|
51
|
+
### Setup Actors
|
52
|
+
|
53
|
+
Include the Actor module in a class and override the default followers method.
|
54
|
+
|
55
|
+
``` ruby
|
56
|
+
class User < ActiveRecord::Base
|
57
|
+
include MailControl::Actor
|
58
|
+
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
### Publishing LoggedEmail
|
65
|
+
|
66
|
+
In your controller or background worker:
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
current_user.send_email(:new_enquiry, :act_object => @enquiry, :target => @listing)
|
70
|
+
```
|
71
|
+
|
72
|
+
This will publish the queued_task to the mongoid act_objects returned by the #followers method in the Actor.
|
73
|
+
|
74
|
+
|
75
|
+
## Retrieving LoggedEmail
|
76
|
+
|
77
|
+
To retrieve all queued_task for an actor
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
current_user.logged_emails
|
81
|
+
```
|
82
|
+
|
83
|
+
To retrieve and filter to a particular queued_task type
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
current_user.logged_emails(:verb => 'new_enquiry')
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Options
|
90
|
+
|
91
|
+
Additional options can be required:
|
92
|
+
|
93
|
+
``` ruby
|
94
|
+
class LoggedEmail < ActiveRecord::Base
|
95
|
+
include MailControl::LoggedEmail
|
96
|
+
|
97
|
+
queued_task :new_enquiry do
|
98
|
+
actor :User
|
99
|
+
act_object :Article
|
100
|
+
act_target :Volume
|
101
|
+
option :country
|
102
|
+
option :city
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
The option fields are stored using the ActiveRecord 'store' feature.
|
108
|
+
|
109
|
+
|
110
|
+
#### Bond type
|
111
|
+
|
112
|
+
A verb can have one bond type. This bond type can be used to classify and quickly retrieve
|
113
|
+
queued_task feed items that belong to a particular aggregate feed, like e.g the global feed.
|
114
|
+
|
115
|
+
``` ruby
|
116
|
+
class LoggedEmail < ActiveRecord::Base
|
117
|
+
include MailControl::LoggedEmail
|
118
|
+
|
119
|
+
queued_task :new_enquiry do
|
120
|
+
actor :User
|
121
|
+
act_object :Article
|
122
|
+
act_target :Volume
|
123
|
+
bond_type :global
|
124
|
+
end
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
### Poll for changes and empty queue
|
129
|
+
|
130
|
+
There is a poll changes interface that will group tasks by first actor and then verb.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
LoggedEmail.poll_for_changes() do |verb, hash|
|
134
|
+
#verb is the verb the changes are group by
|
135
|
+
#hash has format: {:actor => _actor /* actor is the */,
|
136
|
+
# :act_objects => act_objects /* all objects from matching tasks */,
|
137
|
+
# :act_object_ids => act_object_ids /* all object ids from matching tasks */,
|
138
|
+
# :act_targets => act_targets /* all targets from matching tasks */ ,
|
139
|
+
# :act_target_ids => act_target_ids /* all target ids from matching tasks}
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators/named_base'
|
2
|
+
|
3
|
+
module MailControl
|
4
|
+
# A generator module with LoggedEmail table schema.
|
5
|
+
module Generators
|
6
|
+
# A base module
|
7
|
+
module Base
|
8
|
+
# Get path for migration template
|
9
|
+
def source_root
|
10
|
+
@_mail_control_source_root ||= File.expand_path(File.join('../mail-control', generator_name, 'templates'), __FILE__)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'generators/mail-control'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module MailControl
|
5
|
+
module Generators
|
6
|
+
# LoggedEmail generator that creates queued_task model file from template
|
7
|
+
class LoggedEmailGenerator < ActiveRecord::Generators::Base
|
8
|
+
extend Base
|
9
|
+
|
10
|
+
argument :name, :type => :string, :default => 'logged_email'
|
11
|
+
# Create model in project's folder
|
12
|
+
def generate_files
|
13
|
+
copy_file 'logged_email.rb', "app/models/#{name}.rb"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'generators/mail-control'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module LiveStream
|
5
|
+
module Generators
|
6
|
+
# Migration generator that creates migration file from template
|
7
|
+
class MigrationGenerator < ActiveRecord::Generators::Base
|
8
|
+
extend Base
|
9
|
+
|
10
|
+
argument :name, :type => :string, :default => 'create_logged_emails'
|
11
|
+
# Create migration in project's folder
|
12
|
+
def generate_files
|
13
|
+
migration_template 'migration.rb', "db/migrate/#{name}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Migration responsible for creating a table with logged_emails
|
2
|
+
class CreateLoggedEmails < ActiveRecord::Migration
|
3
|
+
# Create table
|
4
|
+
def self.up
|
5
|
+
create_table :logged_emails do |t|
|
6
|
+
t.belongs_to :actor, :polymorphic => true
|
7
|
+
t.belongs_to :act_object, :polymorphic => true
|
8
|
+
t.belongs_to :act_target, :polymorphic => true
|
9
|
+
|
10
|
+
t.string :verb
|
11
|
+
t.string :description
|
12
|
+
t.string :options
|
13
|
+
t.string :bond_type
|
14
|
+
|
15
|
+
t.time :send_after
|
16
|
+
t.time :send_before
|
17
|
+
t.string :state
|
18
|
+
|
19
|
+
t.string :unsubscribe_by
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
add_index :logged_emails, [:verb]
|
25
|
+
add_index :logged_emails, [:actor_id, :actor_type]
|
26
|
+
add_index :logged_emails, [:act_object_id, :act_object_type]
|
27
|
+
add_index :logged_emails, [:act_target_id, :act_target_type]
|
28
|
+
end
|
29
|
+
# Drop table
|
30
|
+
def self.down
|
31
|
+
drop_table :logged_emails
|
32
|
+
end
|
33
|
+
end
|
data/lib/mail-control.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/module/delegation.rb'
|
4
|
+
require "mail-control/version"
|
5
|
+
require "mail-control/actor"
|
6
|
+
require "mail-control/logged_email"
|
7
|
+
require "mail-control/definition"
|
8
|
+
require "mail-control/definition_dsl"
|
9
|
+
require "mail-control/errors"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module MailControl
|
2
|
+
|
3
|
+
module Actor
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
cattr_accessor :queued_task_klass
|
8
|
+
|
9
|
+
has_many :logged_emails, :class_name => "LoggedEmail", :as => :actor
|
10
|
+
has_many :act_object_logged_emails, :class_name => "LoggedEmail", :as => :act_object
|
11
|
+
has_many :act_target_logged_emails, :class_name => "LoggedEmail", :as => :act_target
|
12
|
+
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def mail_control_class(klass)
|
19
|
+
self.queued_task_klass = klass.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Publishes the queued_task to the receivers
|
26
|
+
#
|
27
|
+
# @param [ Hash ] options The options to publish with.
|
28
|
+
#
|
29
|
+
# @example publish an queued_task with a act_object and act_target
|
30
|
+
# current_user.send_email(:enquiry, :act_object => @enquiry, :act_target => @listing)
|
31
|
+
#
|
32
|
+
def send_email(name, options={})
|
33
|
+
options[:send_after] = Time.now + options[:send_after] if options[:send_after].kind_of?(Fixnum)
|
34
|
+
options[:send_before] = Time.now + options[:send_before] if options[:send_before].kind_of?(Fixnum)
|
35
|
+
|
36
|
+
raise "Expected Time type. Got:" + options[:send_after].class.name unless options[:send_after].kind_of?(Time)
|
37
|
+
raise "Expected Time type. Got:" + options[:send_before].class.name unless options[:send_before].kind_of?(Time)
|
38
|
+
|
39
|
+
mail_control_class.send_email(name, {:actor => self}.merge(options))
|
40
|
+
end
|
41
|
+
|
42
|
+
def mail_control_class
|
43
|
+
@queued_task_klass ||= queued_task_klass ? queued_task_klass.classify.constantize : ::LoggedEmail
|
44
|
+
end
|
45
|
+
|
46
|
+
def actor_logged_emails(options = {})
|
47
|
+
|
48
|
+
if options.empty?
|
49
|
+
logged_emails
|
50
|
+
else
|
51
|
+
logged_emails.where(options)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MailControl
|
2
|
+
|
3
|
+
class Definition
|
4
|
+
|
5
|
+
attr_reader :name, :actor, :act_object, :act_target, :grouped_actor, :bond_type, :options
|
6
|
+
attr_reader :unsubscribe_by
|
7
|
+
|
8
|
+
|
9
|
+
# @param dsl [MailControl::DefinitionDSL] A DSL act_object
|
10
|
+
def initialize(definition)
|
11
|
+
@name = definition[:name]
|
12
|
+
@actor = definition[:actor] || nil
|
13
|
+
@act_object = definition[:act_object] || nil
|
14
|
+
@act_target = definition[:act_target] || nil
|
15
|
+
@grouped_actor = definition[:grouped_actor] || nil
|
16
|
+
@bond_type = definition[:bond_type] || nil
|
17
|
+
@options = definition[:options] || []
|
18
|
+
@unsubscribe_by = definition[:unsubscribe_by] || nil
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Registers a new definition
|
24
|
+
#
|
25
|
+
# @param definition [Definition] The definition to register
|
26
|
+
# @return [Definition] Returns the registered definition
|
27
|
+
def self.register(definition)
|
28
|
+
return false unless definition.is_a? DefinitionDSL
|
29
|
+
definition = new(definition)
|
30
|
+
self.registered << definition
|
31
|
+
return definition || false
|
32
|
+
end
|
33
|
+
|
34
|
+
# List of registered definitions
|
35
|
+
# @return [Array<MailControl::Definition>]
|
36
|
+
def self.registered
|
37
|
+
@definitions ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.find(name)
|
41
|
+
unless definition = registered.find{|definition| definition.name == name.to_sym}
|
42
|
+
raise MailControl::InvalidLoggedEmail, "Could not find a definition for `#{name}`"
|
43
|
+
else
|
44
|
+
definition
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module MailControl
|
4
|
+
|
5
|
+
class DefinitionDSL
|
6
|
+
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@attributes = {
|
11
|
+
:name => name.to_sym,
|
12
|
+
:actor => nil,
|
13
|
+
:act_object => nil,
|
14
|
+
:act_target => nil,
|
15
|
+
:grouped_actor => nil,
|
16
|
+
:reverses => nil,
|
17
|
+
:bond_type => nil,
|
18
|
+
:unsubscribe_by => nil,
|
19
|
+
:options => nil
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_option(option)
|
24
|
+
@attributes[:options] ||= []
|
25
|
+
|
26
|
+
@attributes[:options] << option
|
27
|
+
end
|
28
|
+
|
29
|
+
def option(text)
|
30
|
+
add_option( text )
|
31
|
+
end
|
32
|
+
|
33
|
+
delegate :[], :to => :@attributes
|
34
|
+
|
35
|
+
def self.data_methods(*args)
|
36
|
+
args.each do |method|
|
37
|
+
define_method method do |*args|
|
38
|
+
|
39
|
+
@attributes[method] = args[0]
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
data_methods :actor, :act_object, :act_target, :grouped_actor, :bond_type, :unsubscribe_by
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module MailControl
|
2
|
+
|
3
|
+
class MailControlError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class InvalidLoggedEmail < MailControlError
|
7
|
+
end
|
8
|
+
|
9
|
+
# This error is raised when an act_object isn't defined
|
10
|
+
# as an actor, act_object or act_target
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# <tt>InvalidField.new('field_name')</tt>
|
15
|
+
class InvalidData < MailControlError
|
16
|
+
attr_reader :message
|
17
|
+
|
18
|
+
def initialize message
|
19
|
+
@message = "Invalid Data: #{message}"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# This error is raised when trying to store a field that doesn't exist
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# <tt>InvalidField.new('field_name')</tt>
|
29
|
+
class InvalidField < MailControlError
|
30
|
+
attr_reader :message
|
31
|
+
|
32
|
+
def initialize message
|
33
|
+
@message = "Invalid Field: #{message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class LoggedEmailNotSaved < MailControlError
|
39
|
+
end
|
40
|
+
|
41
|
+
class NoFollowersDefined < MailControlError
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
module MailControl
|
2
|
+
module LoggedEmail
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
|
7
|
+
store :options
|
8
|
+
|
9
|
+
belongs_to :actor, :polymorphic => true
|
10
|
+
belongs_to :act_object, :polymorphic => true
|
11
|
+
belongs_to :act_target, :polymorphic => true
|
12
|
+
|
13
|
+
validates_presence_of :actor_id, :actor_type, :verb
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def poll_for_changes()
|
19
|
+
#if block_given?
|
20
|
+
|
21
|
+
::LoggedEmail.where('state = ? AND send_after <= ?', 'initial', Time.now).select(:act_target_id).uniq.each do |cur_target|
|
22
|
+
target_id = cur_target['act_target_id']
|
23
|
+
|
24
|
+
::LoggedEmail.where('state = ? AND act_target_id = ? AND send_after <= ?', 'initial', target_id, Time.now).select(:verb).uniq.each do |verb|
|
25
|
+
|
26
|
+
changes_for_target = ::LoggedEmail.where(:act_target_id => target_id, :state => 'initial', :verb => verb['verb'].to_s)
|
27
|
+
|
28
|
+
yield verb, changes_for_target
|
29
|
+
|
30
|
+
changes_for_target.each do |cft|
|
31
|
+
cft.update_attributes(:state => 'complete')
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Defines a new LoggedEmail2 type and registers a definition
|
44
|
+
#
|
45
|
+
# @param [ String ] name The name of the queued_task
|
46
|
+
#
|
47
|
+
# @example Define a new queued_task
|
48
|
+
# queued_task(:enquiry) do
|
49
|
+
# actor :user, :cache => [:full_name]
|
50
|
+
# act_object :enquiry, :cache => [:subject]
|
51
|
+
# act_target :listing, :cache => [:title]
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @return [Definition] Returns the registered definition
|
55
|
+
def queued_task(name, &block)
|
56
|
+
definition = MailControl::DefinitionDSL.new(name)
|
57
|
+
definition.instance_eval(&block)
|
58
|
+
MailControl::Definition.register(definition)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Publishes an queued_task using an queued_task name and data
|
62
|
+
#
|
63
|
+
# @param [ String ] verb The verb of the queued_task
|
64
|
+
# @param [ Hash ] data The data to initialize the queued_task with.
|
65
|
+
#
|
66
|
+
# @return [MailControl::LoggedEmail2] An LoggedEmail instance with data
|
67
|
+
def send_email(verb, data)
|
68
|
+
new.send_email({:verb => verb}.merge(data))
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# Publishes the queued_task to the receivers
|
76
|
+
#
|
77
|
+
# @param [ Hash ] options The options to send_email with.
|
78
|
+
#
|
79
|
+
def send_email(data = {})
|
80
|
+
assign_properties(data)
|
81
|
+
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def refresh_data
|
87
|
+
save(:validate => false)
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
|
92
|
+
def assign_properties(data = {})
|
93
|
+
|
94
|
+
self.verb = data.delete(:verb)
|
95
|
+
|
96
|
+
|
97
|
+
write_attribute(:send_after, data[:send_after])
|
98
|
+
data.delete(:send_after)
|
99
|
+
|
100
|
+
write_attribute(:send_before, data[:send_before])
|
101
|
+
data.delete(:send_before)
|
102
|
+
|
103
|
+
self.state = 'initial'
|
104
|
+
|
105
|
+
[:actor, :act_object, :act_target].each do |type|
|
106
|
+
|
107
|
+
cur_object = data[type]
|
108
|
+
|
109
|
+
unless cur_object
|
110
|
+
if definition.send(type.to_sym)
|
111
|
+
raise verb.to_json
|
112
|
+
#raise MailControl::InvalidData.new(type)
|
113
|
+
else
|
114
|
+
next
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class_sym = cur_object.class.name.to_sym
|
120
|
+
|
121
|
+
raise MailControl::InvalidData.new(class_sym) unless definition.send(type) == class_sym
|
122
|
+
|
123
|
+
case type
|
124
|
+
when :actor
|
125
|
+
self.actor = cur_object
|
126
|
+
when :act_object
|
127
|
+
self.act_object = cur_object
|
128
|
+
when :act_target
|
129
|
+
self.act_target = cur_object
|
130
|
+
else
|
131
|
+
raise "unknown type"
|
132
|
+
end
|
133
|
+
|
134
|
+
data.delete(type)
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
[:grouped_actor].each do |group|
|
139
|
+
|
140
|
+
|
141
|
+
grp_object = data[group]
|
142
|
+
|
143
|
+
if grp_object == nil
|
144
|
+
if definition.send(group.to_sym)
|
145
|
+
raise verb.to_json
|
146
|
+
#raise MailControl::InvalidData.new(group)
|
147
|
+
else
|
148
|
+
next
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
grp_object.each do |cur_obj|
|
154
|
+
raise MailControl::InvalidData.new(class_sym) unless definition.send(group) == cur_obj.class.name.to_sym
|
155
|
+
|
156
|
+
self.grouped_actors << cur_obj
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
data.delete(group)
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
cur_bond_type = definition.send(:bond_type)
|
165
|
+
|
166
|
+
if cur_bond_type
|
167
|
+
write_attribute( :bond_type, cur_bond_type.to_s )
|
168
|
+
end
|
169
|
+
|
170
|
+
if definition.send(:unsubscribe_by)
|
171
|
+
write_attribute(:unsubscribe_by, definition.send(:unsubscribe_by))
|
172
|
+
else
|
173
|
+
raise "definition must define unsubscribe_by option"
|
174
|
+
end
|
175
|
+
|
176
|
+
if self.act_target.is_unsubscribed_to?(self)
|
177
|
+
self.state = 'unsubscribed'
|
178
|
+
end
|
179
|
+
|
180
|
+
def_options = definition.send(:options)
|
181
|
+
def_options.each do |cur_option|
|
182
|
+
cur_object = data[cur_option]
|
183
|
+
|
184
|
+
if cur_object
|
185
|
+
|
186
|
+
if cur_option == :description
|
187
|
+
self.description = cur_object
|
188
|
+
else
|
189
|
+
options[cur_option] = cur_object
|
190
|
+
end
|
191
|
+
data.delete(cur_option)
|
192
|
+
|
193
|
+
else
|
194
|
+
#all options defined must be used
|
195
|
+
raise Streama::InvalidData.new(cur_object[0])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
if data.size > 0
|
200
|
+
raise "unexpected arguments: " + data.to_json
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
self.save
|
206
|
+
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
def definition
|
211
|
+
@definition ||= MailControl::Definition.find(verb)
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mail-control/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mail-control"
|
7
|
+
s.version = MailControl::VERSION
|
8
|
+
s.authors = ["Andreas Saebjoernsen"]
|
9
|
+
s.email = ["andreas.saebjoernsen@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{LoggedEmail Streams for rails}
|
12
|
+
s.description = %q{MailControl is a simple gem for giving both your app and users control over when, if and how emails are sent out.}
|
13
|
+
s.homepage = 'https://github.com/digitalplaywright/mail-control'
|
14
|
+
|
15
|
+
s.rubyforge_project = "mail-control"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.required_ruby_version = '>= 1.9.3'
|
23
|
+
|
24
|
+
s.add_dependency 'activerecord', '>= 3'
|
25
|
+
s.add_dependency 'activesupport', '>= 3'
|
26
|
+
s.add_dependency 'actionpack', '>= 3'
|
27
|
+
s.add_development_dependency 'rake'
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Migration responsible for creating a table with logged_emails
|
2
|
+
class CreateLoggedEmails < ActiveRecord::Migration
|
3
|
+
# Create table
|
4
|
+
def self.up
|
5
|
+
create_table :logged_emails do |t|
|
6
|
+
t.belongs_to :actor, :polymorphic => true
|
7
|
+
t.belongs_to :act_object, :polymorphic => true
|
8
|
+
t.belongs_to :act_target, :polymorphic => true
|
9
|
+
|
10
|
+
t.string :verb
|
11
|
+
t.string :description
|
12
|
+
t.string :options
|
13
|
+
t.string :bond_type
|
14
|
+
|
15
|
+
t.time :send_after
|
16
|
+
t.time :send_before
|
17
|
+
t.string :state
|
18
|
+
|
19
|
+
t.string :unsubscribe_by
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
add_index :logged_emails, [:verb]
|
25
|
+
add_index :logged_emails, [:actor_id, :actor_type]
|
26
|
+
add_index :logged_emails, [:act_object_id, :act_object_type]
|
27
|
+
add_index :logged_emails, [:act_target_id, :act_target_type]
|
28
|
+
end
|
29
|
+
# Drop table
|
30
|
+
def self.down
|
31
|
+
drop_table :logged_emails
|
32
|
+
end
|
33
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup(:default, :test)
|
4
|
+
|
5
|
+
$:.unshift File.expand_path('../../lib/', __FILE__)
|
6
|
+
require 'active_support/testing/setup_and_teardown'
|
7
|
+
require 'mail-control'
|
8
|
+
require 'minitest/autorun'
|
9
|
+
|
10
|
+
#LiveStream.config # touch config to load ORM, needed in some separate tests
|
11
|
+
|
12
|
+
require 'active_record'
|
13
|
+
require 'active_record/connection_adapters/sqlite3_adapter'
|
14
|
+
|
15
|
+
|
16
|
+
class ActiveSupport::TestCase
|
17
|
+
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
|
18
|
+
#
|
19
|
+
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
20
|
+
# -- they do not yet inherit this setting
|
21
|
+
#fixtures :all
|
22
|
+
|
23
|
+
# Add more helper methods to be used by all tests here...
|
24
|
+
end
|
25
|
+
|
26
|
+
class LoggedEmail < ActiveRecord::Base
|
27
|
+
include MailControl::LoggedEmail
|
28
|
+
|
29
|
+
queued_task :new_enquiry do
|
30
|
+
actor :User
|
31
|
+
act_object :Article
|
32
|
+
act_target :User
|
33
|
+
#option :description
|
34
|
+
unsubscribe_by :option_1
|
35
|
+
end
|
36
|
+
|
37
|
+
queued_task :test_description do
|
38
|
+
actor :User
|
39
|
+
act_object :Article
|
40
|
+
act_target :User
|
41
|
+
option :description
|
42
|
+
unsubscribe_by :option_1
|
43
|
+
end
|
44
|
+
|
45
|
+
queued_task :test_option do
|
46
|
+
actor :User
|
47
|
+
act_object :Article
|
48
|
+
act_target :User
|
49
|
+
option :country
|
50
|
+
unsubscribe_by :option_1
|
51
|
+
end
|
52
|
+
|
53
|
+
queued_task :test_bond_type do
|
54
|
+
actor :User
|
55
|
+
act_object :Article
|
56
|
+
act_target :User
|
57
|
+
bond_type :global
|
58
|
+
unsubscribe_by :option_1
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class User < ActiveRecord::Base
|
64
|
+
include MailControl::Actor
|
65
|
+
|
66
|
+
def is_unsubscribed_to?(_logged_email)
|
67
|
+
_logged_email.unsubscribe_by == 'i_am_unsubscribed'
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class Article < ActiveRecord::Base
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class Volume < ActiveRecord::Base
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
81
|
+
ActiveRecord::Migrator.migrate(File.expand_path('../migrations', __FILE__))
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class LoggedEmailTest < ActiveSupport::TestCase
|
2
|
+
|
3
|
+
def test_truth
|
4
|
+
assert true
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_register_definition
|
8
|
+
|
9
|
+
@definition = LoggedEmail.queued_task(:test_queued_task) do
|
10
|
+
actor :user, :cache => [:full_name]
|
11
|
+
act_object :listing, :cache => [:title, :full_address]
|
12
|
+
act_target :listing, :cache => [:title]
|
13
|
+
end
|
14
|
+
|
15
|
+
assert @definition.is_a?(MailControl::Definition)
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_publish_new_queued_task
|
20
|
+
_user = User.create()
|
21
|
+
_article = Article.create()
|
22
|
+
_user_t = User.create()
|
23
|
+
|
24
|
+
_queued_task = LoggedEmail.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :actor => _user, :act_object => _article, :act_target => _user_t )
|
25
|
+
|
26
|
+
assert _queued_task.persisted?
|
27
|
+
#_queued_task.should be_an_instance_of LoggedEmail
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_description
|
32
|
+
_user = User.create()
|
33
|
+
_article = Article.create()
|
34
|
+
_user_t = User.create()
|
35
|
+
|
36
|
+
_description = "this is a test"
|
37
|
+
_queued_task = LoggedEmail.send_email(:test_description, :send_after => Time.now, :send_before => Time.now + 1.hour, :actor => _user, :act_object => _article, :act_target => _user_t ,
|
38
|
+
:description => _description )
|
39
|
+
|
40
|
+
assert _queued_task.description == _description
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_options
|
45
|
+
_user = User.create()
|
46
|
+
_article = Article.create()
|
47
|
+
_user_t = User.create()
|
48
|
+
|
49
|
+
_country = "denmark"
|
50
|
+
_queued_task = LoggedEmail.send_email(:test_option, :send_after => Time.now, :send_before => Time.now + 1.hour, :actor => _user, :act_object => _article, :act_target => _user_t ,
|
51
|
+
:country => _country )
|
52
|
+
|
53
|
+
assert _queued_task.options[:country] == _country
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_bond_type
|
58
|
+
_user = User.create()
|
59
|
+
_article = Article.create()
|
60
|
+
_user_t = User.create()
|
61
|
+
|
62
|
+
_queued_task = LoggedEmail.send_email(:test_bond_type, :send_after => Time.now, :send_before => Time.now + 1.hour, :actor => _user, :act_object => _article, :act_target => _user_t )
|
63
|
+
|
64
|
+
assert _queued_task.bond_type == 'global'
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_poll_changes
|
69
|
+
|
70
|
+
_user = User.create()
|
71
|
+
_article = Article.create()
|
72
|
+
_user_t = User.create()
|
73
|
+
|
74
|
+
_activity = LoggedEmail.send_email(:test_bond_type, :send_after => Time.now - 1.hour, :send_before => Time.now + 1.hour, :actor => _user, :act_object => _article, :act_target => _user_t )
|
75
|
+
|
76
|
+
assert LoggedEmail.all.size > 0
|
77
|
+
|
78
|
+
|
79
|
+
LoggedEmail.poll_for_changes() do |verb, hash|
|
80
|
+
#assert hash[:actor].id == _user.id
|
81
|
+
end
|
82
|
+
|
83
|
+
assert LoggedEmail.where('state = ? AND act_target_id = ?', 'initial', _user_t.id).size == 0
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class ActorTest < ActiveSupport::TestCase
|
2
|
+
|
3
|
+
|
4
|
+
def test_send_email
|
5
|
+
|
6
|
+
_user = User.create()
|
7
|
+
_article = Article.create()
|
8
|
+
_user_t = User.create()
|
9
|
+
|
10
|
+
queued_task = _user.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
11
|
+
|
12
|
+
assert queued_task.persisted?
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_retrieves_the_stream_for_an_actor
|
17
|
+
_user = User.create()
|
18
|
+
_article = Article.create()
|
19
|
+
_user_t = User.create()
|
20
|
+
|
21
|
+
_user.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
22
|
+
_user.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
23
|
+
|
24
|
+
assert _user.logged_emails.size == 2
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def test_retrieves_the_stream_for_a_particular_queued_task_type
|
30
|
+
_user = User.create()
|
31
|
+
_article = Article.create()
|
32
|
+
_user_t = User.create()
|
33
|
+
|
34
|
+
_user.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
35
|
+
_user.send_email(:new_enquiry, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
36
|
+
_user.send_email(:test_bond_type, :send_after => Time.now, :send_before => Time.now + 1.hour, :act_object => _article, :act_target => _user_t )
|
37
|
+
|
38
|
+
assert _user.actor_logged_emails(:verb => 'new_enquiry').size == 2
|
39
|
+
assert _user.actor_logged_emails(:bond_type => 'global').size == 1
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class DefinitionDslTest < ActiveSupport::TestCase
|
2
|
+
|
3
|
+
def definition_dsl
|
4
|
+
MailControl::DefinitionDSL.new(:new_enquiry)
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_initializes_with_name
|
8
|
+
assert definition_dsl.attributes[:name] == :new_enquiry
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_adds_an_actor_to_the_definition
|
12
|
+
dsl = definition_dsl
|
13
|
+
dsl.actor(:User)
|
14
|
+
assert dsl.attributes[:actor] == :User
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_adds_an_act_object_to_the_definition
|
18
|
+
dsl = definition_dsl
|
19
|
+
dsl.act_object(:Article)
|
20
|
+
assert dsl.attributes[:act_object] == :Article
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_adds_a_act_target_to_the_definition
|
24
|
+
dsl = definition_dsl
|
25
|
+
dsl.act_target(:Volume)
|
26
|
+
assert dsl.attributes[:act_target] == :Volume
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class DefinitionTest < ActiveSupport::TestCase
|
2
|
+
|
3
|
+
def definition_dsl
|
4
|
+
dsl = MailControl::DefinitionDSL.new(:new_enquiry)
|
5
|
+
dsl.actor(:User)
|
6
|
+
dsl.act_object(:Article)
|
7
|
+
dsl.act_target(:Volume)
|
8
|
+
dsl
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_initialization
|
12
|
+
_definition_dsl = definition_dsl
|
13
|
+
_definition = MailControl::Definition.new(_definition_dsl)
|
14
|
+
|
15
|
+
assert _definition.actor == :User
|
16
|
+
assert _definition.act_object == :Article
|
17
|
+
assert _definition.act_target == :Volume
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_register_definition_and_return_new_definition
|
22
|
+
|
23
|
+
assert MailControl::Definition.register(definition_dsl).is_a?(MailControl::Definition)
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_register_invalid_definition
|
28
|
+
|
29
|
+
assert MailControl::Definition.register(false) == false
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_return_registered_definitions
|
34
|
+
|
35
|
+
MailControl::Definition.register(definition_dsl)
|
36
|
+
assert MailControl::Definition.registered.size > 0
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_return_definition_by_name
|
41
|
+
assert MailControl::Definition.find(:new_enquiry).name == :new_enquiry
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_raise_exception_if_invalid_queued_task
|
46
|
+
|
47
|
+
assert_raises(MailControl::InvalidLoggedEmail){ MailControl::Definition.find(:unknown_queued_task) }
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mail-control
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andreas Saebjoernsen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
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
|
+
description: MailControl is a simple gem for giving both your app and users control
|
70
|
+
over when, if and how emails are sent out.
|
71
|
+
email:
|
72
|
+
- andreas.saebjoernsen@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .document
|
78
|
+
- .gitignore
|
79
|
+
- .rspec
|
80
|
+
- .travis.yml
|
81
|
+
- CHANGELOG.md
|
82
|
+
- Gemfile
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- lib/generators/mail-control.rb
|
87
|
+
- lib/generators/mail-control/activity/activity_generator.rb
|
88
|
+
- lib/generators/mail-control/activity/templates/logged_email.rb
|
89
|
+
- lib/generators/mail-control/migration/migration_generator.rb
|
90
|
+
- lib/generators/mail-control/migration/templates/migration.rb
|
91
|
+
- lib/mail-control.rb
|
92
|
+
- lib/mail-control/actor.rb
|
93
|
+
- lib/mail-control/definition.rb
|
94
|
+
- lib/mail-control/definition_dsl.rb
|
95
|
+
- lib/mail-control/errors.rb
|
96
|
+
- lib/mail-control/logged_email.rb
|
97
|
+
- lib/mail-control/version.rb
|
98
|
+
- mail-control.gemspec
|
99
|
+
- test/migrations/001_create_logged_emails.rb
|
100
|
+
- test/migrations/002_create_articles.rb
|
101
|
+
- test/migrations/003_create_users.rb
|
102
|
+
- test/migrations/004_add_nonstandard_to_activities.rb
|
103
|
+
- test/migrations/005_create_volumes.rb
|
104
|
+
- test/test_helper.rb
|
105
|
+
- test/unit/activity_test.rb
|
106
|
+
- test/unit/actor_test.rb
|
107
|
+
- test/unit/definition_dsl_test.rb
|
108
|
+
- test/unit/definition_test.rb
|
109
|
+
homepage: https://github.com/digitalplaywright/mail-control
|
110
|
+
licenses: []
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.9.3
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project: mail-control
|
128
|
+
rubygems_version: 2.0.4
|
129
|
+
signing_key:
|
130
|
+
specification_version: 4
|
131
|
+
summary: LoggedEmail Streams for rails
|
132
|
+
test_files:
|
133
|
+
- test/migrations/001_create_logged_emails.rb
|
134
|
+
- test/migrations/002_create_articles.rb
|
135
|
+
- test/migrations/003_create_users.rb
|
136
|
+
- test/migrations/004_add_nonstandard_to_activities.rb
|
137
|
+
- test/migrations/005_create_volumes.rb
|
138
|
+
- test/test_helper.rb
|
139
|
+
- test/unit/activity_test.rb
|
140
|
+
- test/unit/actor_test.rb
|
141
|
+
- test/unit/definition_dsl_test.rb
|
142
|
+
- test/unit/definition_test.rb
|
143
|
+
has_rdoc:
|