mail-control 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
+ SHA1:
3
+ metadata.gz: caaf2a07f4cecd59194d9daf3e6d0e04ee9dedf7
4
+ data.tar.gz: 4c943c088a90bd54ad7e7f3ae62eaf943325ebce
5
+ SHA512:
6
+ metadata.gz: 776a0118e140c9692d61cb15b728a04b932697ac4091606fa537ae61eb097792140ebc12c11a6044dc990ecbc80d26d5b37b7d67d2a102ec4b4798bcd2f79663
7
+ data.tar.gz: 219ff9a4a205aea11a8144ea63164b575405dae75347dfb41275d7dae6a41a123dcd5889ce71446405b97063834500ac9e3710285964a9cfae137c399d1f6344
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ services: SQLite3
3
+ gemfile:
4
+ - Gemfile
5
+ rvm:
6
+ - 1.9.3
7
+ - 2.0.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.1.0
4
+
5
+ First version of gem
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development, :test do
4
+ gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
5
+ gem "jdbc-sqlite3", :platform => :jruby
6
+ end
7
+
8
+ # Specify your gem's dependencies in mail-control.gemspec
9
+ gemspec
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
+ [![Build Status](https://secure.travis-ci.org/digitalplaywright/mail-control.png)](http://travis-ci.org/digitalplaywright/mail-control) [![Dependency Status](https://gemnasium.com/digitalplaywright/mail-control.png)](https://gemnasium.com/digitalplaywright/mail-control) [![Code Climate](https://codeclimate.com/github/digitalplaywright/mail-control.png)](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,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "test"
9
+ t.test_files = FileList['test/test*.rb', 'test/unit/*_test.rb']
10
+ end
11
+
@@ -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,5 @@
1
+ # LoggedEmail model for customisation & custom methods
2
+ class LoggedEmail < ActiveRecord::Base
3
+ include MailControl::LoggedEmail
4
+
5
+ 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
@@ -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,3 @@
1
+ module MailControl
2
+ VERSION = "0.1.0"
3
+ 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
@@ -0,0 +1,11 @@
1
+ class CreateArticles < ActiveRecord::Migration
2
+ def self.up
3
+ puts "creating"
4
+ create_table :articles do |t|
5
+ t.string :name
6
+ t.boolean :published
7
+ t.belongs_to :user
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :users do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ class AddNonstandardToActivities < ActiveRecord::Migration
2
+ def change
3
+ change_table :logged_emails do |t|
4
+ t.string :nonstandard
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class CreateVolumes < ActiveRecord::Migration
2
+ def self.up
3
+ puts "creating"
4
+ create_table :volumes do |t|
5
+ t.belongs_to :user
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -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: