mail_spy 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -0
- data/lib/mail_spy/manager.rb +3 -9
- data/lib/mail_spy/version.rb +1 -1
- data/lib/mail_spy.rb +1 -1
- metadata +28 -18
- data/lib/mail_spy/thread_pool.rb +0 -119
data/README.rdoc
CHANGED
@@ -15,6 +15,9 @@ Add gem "mailspy" to your Gemfile
|
|
15
15
|
If you haven't already create a mongoid.yml file for the mongodb connection
|
16
16
|
rails generate mongoid:config
|
17
17
|
|
18
|
+
If you are going to be sending on one machine make sure to scale up the pool
|
19
|
+
from the default of 1 to 10+ to get good performance. Timeouts are also a good idea
|
20
|
+
|
18
21
|
Create the mongoid configuration for the mongo db
|
19
22
|
|
20
23
|
rails g mail_spy:initializer
|
data/lib/mail_spy/manager.rb
CHANGED
@@ -73,7 +73,7 @@ module MailSpy
|
|
73
73
|
def send_outstanding_emails(step=100, num_threads=50)
|
74
74
|
raise "No Email service providers installed" unless MailSpy.esps.present?
|
75
75
|
|
76
|
-
|
76
|
+
wq = WorkQueue.new(num_threads, step*2)
|
77
77
|
offset = 0
|
78
78
|
sent = 0
|
79
79
|
|
@@ -84,14 +84,13 @@ module MailSpy
|
|
84
84
|
pony_hash[pony_key] = value if value.present?
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
87
|
while true
|
89
88
|
emails = MailSpy::Email.
|
90
89
|
limit(step).offset(offset).
|
91
90
|
where(:schedule_at.lte => DateTime.now, :sent => false, :failed => false).all
|
92
91
|
break if emails.blank?
|
93
92
|
emails.each do |email|
|
94
|
-
|
93
|
+
wq.enqueue_b do
|
95
94
|
begin
|
96
95
|
MailSpy::CoreMailer.template(email).deliver
|
97
96
|
email.update_attribute(:sent, true)
|
@@ -103,16 +102,11 @@ module MailSpy
|
|
103
102
|
email.save!
|
104
103
|
end
|
105
104
|
end
|
106
|
-
|
107
|
-
while(pool.job_queue_size > (2 * num_threads))
|
108
|
-
sleep(1)
|
109
|
-
end
|
110
|
-
|
111
105
|
end
|
112
106
|
offset += step
|
113
107
|
end
|
114
108
|
|
115
|
-
|
109
|
+
wq.join
|
116
110
|
sent
|
117
111
|
end
|
118
112
|
|
data/lib/mail_spy/version.rb
CHANGED
data/lib/mail_spy.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: mail_spy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.13
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Timothy Cardenas
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
type: :runtime
|
69
69
|
version_requirements: *id005
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: work_queue
|
72
72
|
prerelease: false
|
73
73
|
requirement: &id006 !ruby/object:Gem::Requirement
|
74
74
|
none: false
|
@@ -79,7 +79,7 @@ dependencies:
|
|
79
79
|
type: :runtime
|
80
80
|
version_requirements: *id006
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
|
-
name:
|
82
|
+
name: jquery-rails
|
83
83
|
prerelease: false
|
84
84
|
requirement: &id007 !ruby/object:Gem::Requirement
|
85
85
|
none: false
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: "0"
|
90
|
-
type: :
|
90
|
+
type: :runtime
|
91
91
|
version_requirements: *id007
|
92
92
|
- !ruby/object:Gem::Dependency
|
93
|
-
name:
|
93
|
+
name: sqlite3
|
94
94
|
prerelease: false
|
95
95
|
requirement: &id008 !ruby/object:Gem::Requirement
|
96
96
|
none: false
|
@@ -101,7 +101,7 @@ dependencies:
|
|
101
101
|
type: :development
|
102
102
|
version_requirements: *id008
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
104
|
+
name: turn
|
105
105
|
prerelease: false
|
106
106
|
requirement: &id009 !ruby/object:Gem::Requirement
|
107
107
|
none: false
|
@@ -112,7 +112,7 @@ dependencies:
|
|
112
112
|
type: :development
|
113
113
|
version_requirements: *id009
|
114
114
|
- !ruby/object:Gem::Dependency
|
115
|
-
name:
|
115
|
+
name: rr
|
116
116
|
prerelease: false
|
117
117
|
requirement: &id010 !ruby/object:Gem::Requirement
|
118
118
|
none: false
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
type: :development
|
124
124
|
version_requirements: *id010
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: factory_girl
|
127
127
|
prerelease: false
|
128
128
|
requirement: &id011 !ruby/object:Gem::Requirement
|
129
129
|
none: false
|
@@ -134,29 +134,29 @@ dependencies:
|
|
134
134
|
type: :development
|
135
135
|
version_requirements: *id011
|
136
136
|
- !ruby/object:Gem::Dependency
|
137
|
-
name:
|
137
|
+
name: factory_girl_rails
|
138
138
|
prerelease: false
|
139
139
|
requirement: &id012 !ruby/object:Gem::Requirement
|
140
140
|
none: false
|
141
141
|
requirements:
|
142
|
-
- - "
|
142
|
+
- - ">="
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 0
|
144
|
+
version: "0"
|
145
145
|
type: :development
|
146
146
|
version_requirements: *id012
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
|
-
name: spork
|
148
|
+
name: spork
|
149
149
|
prerelease: false
|
150
150
|
requirement: &id013 !ruby/object:Gem::Requirement
|
151
151
|
none: false
|
152
152
|
requirements:
|
153
|
-
- - "
|
153
|
+
- - ">"
|
154
154
|
- !ruby/object:Gem::Version
|
155
|
-
version:
|
155
|
+
version: 0.9.0.rc
|
156
156
|
type: :development
|
157
157
|
version_requirements: *id013
|
158
158
|
- !ruby/object:Gem::Dependency
|
159
|
-
name:
|
159
|
+
name: spork-testunit
|
160
160
|
prerelease: false
|
161
161
|
requirement: &id014 !ruby/object:Gem::Requirement
|
162
162
|
none: false
|
@@ -167,7 +167,7 @@ dependencies:
|
|
167
167
|
type: :development
|
168
168
|
version_requirements: *id014
|
169
169
|
- !ruby/object:Gem::Dependency
|
170
|
-
name:
|
170
|
+
name: ruby-prof
|
171
171
|
prerelease: false
|
172
172
|
requirement: &id015 !ruby/object:Gem::Requirement
|
173
173
|
none: false
|
@@ -178,7 +178,7 @@ dependencies:
|
|
178
178
|
type: :development
|
179
179
|
version_requirements: *id015
|
180
180
|
- !ruby/object:Gem::Dependency
|
181
|
-
name:
|
181
|
+
name: minitest
|
182
182
|
prerelease: false
|
183
183
|
requirement: &id016 !ruby/object:Gem::Requirement
|
184
184
|
none: false
|
@@ -188,6 +188,17 @@ dependencies:
|
|
188
188
|
version: "0"
|
189
189
|
type: :development
|
190
190
|
version_requirements: *id016
|
191
|
+
- !ruby/object:Gem::Dependency
|
192
|
+
name: timecop
|
193
|
+
prerelease: false
|
194
|
+
requirement: &id017 !ruby/object:Gem::Requirement
|
195
|
+
none: false
|
196
|
+
requirements:
|
197
|
+
- - ">="
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: "0"
|
200
|
+
type: :development
|
201
|
+
version_requirements: *id017
|
191
202
|
description: Mailspy allows for quick and easy creation, sending and tracking of email campaigns
|
192
203
|
email:
|
193
204
|
- trcarden@gmail.com
|
@@ -225,7 +236,6 @@ files:
|
|
225
236
|
- lib/mail_spy/engine.rb
|
226
237
|
- lib/mail_spy/manager.rb
|
227
238
|
- lib/mail_spy/sendgrid/smtp_api_header.rb
|
228
|
-
- lib/mail_spy/thread_pool.rb
|
229
239
|
- lib/mail_spy/version.rb
|
230
240
|
- lib/mail_spy.rb
|
231
241
|
- lib/tasks/mail_spy_tasks.rake
|
data/lib/mail_spy/thread_pool.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# Ruby Thread ThreadPool
|
2
|
-
# ================
|
3
|
-
# A thread pool is useful when you wish to do some work in a thread, but do
|
4
|
-
# not know how much work you will be doing in advance. Spawning one thread
|
5
|
-
# for each task is potentially expensive, as threads are not free.
|
6
|
-
#
|
7
|
-
# In this case, it might be more beneficial to start a predefined set of
|
8
|
-
# threads and then hand off work to them as it becomes available. This is
|
9
|
-
# the pure essence of what a thread pool is: an array of threads, all just
|
10
|
-
# waiting to do some work for you!
|
11
|
-
#
|
12
|
-
# Prerequisites
|
13
|
-
# -------------
|
14
|
-
|
15
|
-
# We need the [Queue](http://rdoc.info/stdlib/thread/1.9.2/Queue), as our
|
16
|
-
# thread pool is largely dependent on it. Thanks to this, the implementation
|
17
|
-
# becomes very simple!
|
18
|
-
require 'thread'
|
19
|
-
|
20
|
-
module MailSpy
|
21
|
-
# Public Interface
|
22
|
-
# ----------------
|
23
|
-
|
24
|
-
# `ThreadPool` is our thread pool class. It will allow us to do three operations:
|
25
|
-
#
|
26
|
-
# - `.new(size)` creates a thread pool of a given size
|
27
|
-
# - `#schedule(*args, &job)` schedules a new job to be executed
|
28
|
-
# - `#shutdown` shuts down all threads (after letting them finish working, of course)
|
29
|
-
class ThreadPool
|
30
|
-
|
31
|
-
# ### initialization, or `ThreadPool.new(size)`
|
32
|
-
# Creating a new `ThreadPool` involves a certain amount of work. First, however,
|
33
|
-
# we need to define its’ `size`. It defines how many threads we will have
|
34
|
-
# working internally.
|
35
|
-
#
|
36
|
-
# Which size is best for you is hard to answer. You do not want it to be
|
37
|
-
# too low, as then you won’t be able to do as many things concurrently.
|
38
|
-
# However, if you make it too high Ruby will spend too much time switching
|
39
|
-
# between threads, and that will also degrade performance!
|
40
|
-
def initialize(size)
|
41
|
-
# Before we do anything else, we need to store some information about
|
42
|
-
# our pool. `@size` is useful later, when we want to shut our pool down,
|
43
|
-
# and `@jobs` is the heart of our pool that allows us to schedule work.
|
44
|
-
@size = size
|
45
|
-
@jobs = Queue.new
|
46
|
-
|
47
|
-
# #### Creating our pool of threads
|
48
|
-
# Once preparation is done, it’s time to create our pool of threads.
|
49
|
-
# Each thread store its’ index in a thread-local variable, in case we
|
50
|
-
# need to know which thread a job is executing in later on.
|
51
|
-
@pool = Array.new(@size) do |i|
|
52
|
-
Thread.new do
|
53
|
-
Thread.current[:id] = i
|
54
|
-
|
55
|
-
# We start off by defining a `catch` around our worker loop. This
|
56
|
-
# way we’ve provided a method for graceful shutdown of our threads.
|
57
|
-
# Shutting down is merely a `#schedule { throw :exit }` away!
|
58
|
-
catch(:exit) do
|
59
|
-
# The worker thread life-cycle is very simple. We continuously wait
|
60
|
-
# for tasks to be put into our job `Queue`. If the `Queue` is empty,
|
61
|
-
# we will wait until it’s not.
|
62
|
-
loop do
|
63
|
-
# Once we have a piece of work to be done, we will pull out the
|
64
|
-
# information we need and get to work.
|
65
|
-
job, args = @jobs.pop
|
66
|
-
job.call(*args)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def job_queue_size
|
74
|
-
@jobs.size
|
75
|
-
end
|
76
|
-
|
77
|
-
# ### Work scheduling
|
78
|
-
|
79
|
-
# To schedule a piece of work to be done is to say to the `ThreadPool` that you
|
80
|
-
# want something done.
|
81
|
-
def schedule(*args, &block)
|
82
|
-
# Your given task will not be run immediately; rather, it will be put
|
83
|
-
# into the work `Queue` and executed once a thread is ready to work.
|
84
|
-
@jobs << [block, args]
|
85
|
-
end
|
86
|
-
|
87
|
-
# ### Graceful shutdown
|
88
|
-
|
89
|
-
# If you ever wish to close down your application, I took the liberty of
|
90
|
-
# making it easy for you to wait for any currently executing jobs to finish
|
91
|
-
# before you exit.
|
92
|
-
def shutdown
|
93
|
-
# A graceful shutdown involves threads exiting cleanly themselves, and
|
94
|
-
# since we’ve defined a `catch`-handler around the threads’ worker loop
|
95
|
-
# it is simply a matter of throwing `:exit`. Thus, if we throw one `:exit`
|
96
|
-
# for each thread in our pool, they will all exit eventually!
|
97
|
-
@size.times do
|
98
|
-
schedule { throw :exit }
|
99
|
-
end
|
100
|
-
|
101
|
-
# And now one final thing: wait for our `throw :exit` jobs to be run on
|
102
|
-
# all our worker threads. This call will not return until all worker threads
|
103
|
-
# have exited.
|
104
|
-
@pool.map(&:join)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
# example
|
109
|
-
#if $0 == __FILE__
|
110
|
-
# p = Pool.new(10)
|
111
|
-
#
|
112
|
-
# 20.times do |i|
|
113
|
-
# p.schedule do
|
114
|
-
# sleep rand(4) + 2
|
115
|
-
# puts "Job #{i} finished by thread #{Thread.current[:id]}"
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
# at_exit { p.shutdown }
|
119
|
-
#end
|