mmailer 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,8 +1,15 @@
1
- ## VERSION "0.0.5"
1
+ ## Version 0.0.6
2
+
3
+ * Markdown template
4
+ * real-time configuration of the mail queue
5
+ * Retry logic
6
+ * Catch errors at_exit
7
+
8
+ ## Version 0.0.5
2
9
 
3
10
  * Zoho set to port 465
4
11
  * display version in cli
5
12
 
6
- ## VERSION "0.0.4"
13
+ ## Version 0.0.4
7
14
 
8
15
  * Initial release
data/LICENSE.txt CHANGED
@@ -1,22 +1,10 @@
1
1
  Copyright (c) 2013 Daniel Szmulewicz
2
2
 
3
- MIT License
3
+ LGPLv3 license
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
5
+ Mmailer is an Open Source project licensed under the terms of
6
+ the LGPLv3 license. Please see <http://www.gnu.org/licenses/lgpl-3.0.html>
7
+ for license text.
12
8
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ Mmailer can be made available with a commercial-friendly license allowing private forks
10
+ and modifications. Please mail me for inquiries: daniel.szmulewicz@gmail.com
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  The purpose of Mmailer is to allow the sending of personalized bulk email, like a newsletter, through regular SMTP providers (for example Gmail).
6
6
  Regular SMTP providers imposes restrictions on how much mail you can send. Because various throttling strategies are used, and because they are not always explicit, it is sometimes difficult to evaluate whether you will succeed in sending that newsletter of yours to all of your users.
7
7
 
8
- Mmailer is flexible, and well help you make sure you stay within those limits, whatever they may be. Mmailer is backend agnostic. Nor does it make any assumptions on data formats. It will process the objects you feed it. You can tell Mmailer to randomize the interval between the sending of emails, how long it should wait after a number of emails have been sent, pause the mail queue, resume it at will...
8
+ Mmailer is flexible, and will help you make sure you stay within those limits, whatever they may be. Mmailer is backend agnostic. Nor does it make any assumptions on data formats. It will process the objects you feed it. You can tell Mmailer to randomize the interval between the sending of emails, how long it should wait after a number of emails have been sent, pause the mail queue, resume it at will...
9
9
 
10
10
  Is it any good?
11
11
  ---
@@ -103,33 +103,44 @@ end
103
103
  * `from`: The from address that will be used in your emails.
104
104
  * `subject`: The subject of your email.
105
105
  * `provider`: The name of your provider. These are preset. For the moment, one of `:gmail`, `:zoho` or `:mandrill`. Please add more providers via pull requests or by sending me mail.
106
- * `time_interval`: The number of seconds we want to wait between emails. We use this value as a ceiling when randomizing.
107
- * `mail_interval`: After how many emails we wait before continuing.
108
- * `sleep_time`: How long we wait when we reach the mail interval/threshold.
106
+ * `time_interval`: The number of seconds we want to wait between emails. This value is randomized, and represents thus the ceiling (maximum value).
107
+ * `mail_interval`: How many emails we want to send before sleeping (see below).
108
+ * `sleep_time`: How long we sleep when we reach the mail interval (see above).
109
109
  * `collection`: An array of objects that respond to an `email` message. In the above example, the objects also respond to a `name` message. This will prove handy in templates. Instead of directly providing the array, it is recommended to specify a lambda that returns said array. You will then be able to make expensive calls to your database, bringing as many objects as memory permits, without impacting the server startup time.
110
- * `template`: The path (relative to the current directory) and filename to the ERB templates for your mail, without suffix. For example, "newsletter". This means your template files are actually "newsletter.txt.erb" and "newsletter.html.erb" in the current directory.
110
+ * `template`: The path (relative to the current directory) and filename to the markdown/ERB template for your mail, without suffix. For example, "newsletter". This means your template file is actually "newsletter.md.erb" in the current directory.
111
111
 
112
112
  ### Templates
113
113
 
114
- Templates are the body of your mail. They use the ERB templating system. Each element in your collection is available from within the template. (Much like Rails passes the instance variables from the controller to the views). Based on the collection in the previous example, a sample template would look like this:
114
+ Best practices for HTML email prescribe that you send email in both `text/html` and `text/plain`. Since it is tedious to write and maintain two formats for the same content, Mmailer uses one markdown template that is used as-is for the textual part, and converts to HTML for its sister part.
115
+
116
+ Prior to the markdown conversion, your template gets compiled by ERB. Each element in your collection is available from within the template. (Much like Rails passes the instance variables from the controller to the views). Based on the collection in the previous example, a sample template
117
+ (`newsletter.md.erb`) might look like this:
118
+
115
119
 
116
120
  ```ruby
117
- Dear <%= user.name %>
121
+ Dear <%= user.name %>,
118
122
 
119
123
  This is my newsletter.
120
124
 
121
125
  Yours.
122
-
123
126
  ```
124
127
 
125
- And the equivalent html template.
128
+ It will result in the following `text/html` and `text/plain` bodies.
126
129
 
127
130
  ```ruby
128
- <p>Dear <em><%= user.name %></em></p>
131
+ <p>Dear John Doe,</p>
129
132
  <p>This is my newsletter.</p>
130
133
  <p>Yours.</p>
131
134
  ```
132
135
 
136
+ ```ruby
137
+ Dear John Doe,
138
+
139
+ This is my newsletter.
140
+
141
+ Yours.
142
+ ```
143
+
133
144
  ### Environment variables
134
145
 
135
146
  Ruby can load environment variables for you. It is thus convenient to put them at the top of `config.rb`
@@ -145,6 +156,21 @@ ENV['MMAILER_ENV'] = "production"
145
156
 
146
157
  You can define multiple pairs of usernames and passwords for the predefined providers.
147
158
 
159
+ ### On-the-fly configuration
160
+
161
+ Several configuration options can be changed dynamically, while the server is running.
162
+
163
+ Those are:
164
+
165
+ * `time_interval`: The number of seconds we want to wait between emails. This value is randomized, and represents thus the ceiling (maximum value).
166
+ * `mail_interval`: How many emails we want to send before sleeping (see below).
167
+ * `sleep_time`: How long we sleep when we reach the mail interval (see above).
168
+
169
+ For usage instructions, type:
170
+
171
+ $ mmailer help config
172
+
173
+
148
174
  ## Real world examples
149
175
 
150
176
  ### Mongodb
@@ -169,7 +195,6 @@ Mmailer.configure do |config|
169
195
  config.subject = "My newsletter"
170
196
  config.template = "newsletter"
171
197
  config.collection = lambda { User.all.entries }
172
- config.time_interval = 6
173
198
  config.from = 'John Doe <john@example.com>'
174
199
  end
175
200
  ```
@@ -200,8 +225,7 @@ total 40
200
225
  -rw-r--r-- 1 daniel 1000 424 יול 14 03:43 config.rb
201
226
  -rw-r--r-- 1 daniel 1000 3587 יול 10 04:08 mongo_helper.rb
202
227
  -rw-r--r-- 1 daniel 1000 3027 יול 10 03:39 mongoid.yml
203
- -rw-r--r-- 1 daniel 1000 81 יול 14 03:44 newsletter.html.erb
204
- -rw-r--r-- 1 daniel 1000 60 יול 14 03:44 newsletter.txt.erb
228
+ -rw-r--r-- 1 daniel 1000 81 יול 14 03:44 newsletter.md.erb
205
229
  ```
206
230
 
207
231
  You are now ready to send your newsletter. In one terminal, type `mmailer server`, in another type `mmailer start`. Output will be displayed in the server terminal.
@@ -230,7 +254,7 @@ We used Thor to provide a command line interface.
230
254
 
231
255
  ### Web interface
232
256
 
233
- This program will be best served with some sort of GUI. A web-based interface (using Sinatra) is under consideration.
257
+ This program will be best served with some sort of GUI. A web-based interface is under consideration. (Sinatra could be a good fit).
234
258
 
235
259
  ## Status
236
260
 
@@ -244,6 +268,14 @@ This is an initial release. Currently, no checks or sanitation is done when pars
244
268
  * [] Test suite
245
269
  * [] Generic template engine (Tilt, https://github.com/rtomayko/tilt)
246
270
 
271
+ ## License
272
+
273
+ This software is released as open source under the LGPLv3 license. If you need a commercial license for private forks and modifications, we will provide you with a custom URL to a privately hosted gem with a commercial-friendly license. Please mail me for further inquiries.
274
+
275
+ ## Donations
276
+
277
+ As most developers, I'm working on multiple projects in parallel. If this project is important to you, you're welcome to signal it to me by sending me a donation via paypal (or gittip). To send money via paypal, use the email address in my github profile and specify in the subject it's for mmailer. On [gittip](http://www.gittip.com/danielsz/ "Gittip"), my username is danielsz. Thank you in advance.
278
+
247
279
  ## Spam
248
280
 
249
281
  Mmailer is a bulk mail sending tool. Don't use it for spamming purposes. Spam is evil.
@@ -0,0 +1,18 @@
1
+ module Mmailer
2
+ module Client
3
+ def client(cmd, args=nil)
4
+ require 'drb/drb'
5
+ uri = 'druby://localhost:12345'
6
+ begin
7
+ obj = DRbObject.new_with_uri(uri)
8
+ if args
9
+ obj.send(cmd, args)
10
+ else
11
+ obj.send(cmd)
12
+ end
13
+ rescue DRb::DRbConnError => e
14
+ puts e.message + "\nIs the server running? (You can start the server with `mmailer server`)"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,56 +1,50 @@
1
+ require 'mmailer/client'
2
+
1
3
  class MyCLI < Thor
2
- desc "server", "Start server."
4
+ include Mmailer::Client
3
5
 
6
+ desc "server", "Start server."
4
7
  def server
5
8
  require 'mmailer'
6
9
  Mmailer.start_server
7
10
  end
8
11
 
9
12
  desc "start FROM", "Start an email run from FROM (default is 0, start of the collection)."
10
-
11
13
  def start(from=0)
12
14
  client(:start, from.to_i)
13
15
  end
14
16
 
15
17
  desc "pause", "Pause an email run."
16
-
17
18
  def pause
18
19
  client(:pause)
19
20
  end
20
21
 
21
22
  desc "resume", "Resume an email run."
22
-
23
23
  def resume
24
24
  client(:resume)
25
25
  end
26
26
 
27
27
  desc "stop", "Stop an email run and exit server."
28
-
29
28
  def stop
30
29
  client(:stop)
31
30
  end
32
31
 
33
32
  desc "version", "Show mmailer version"
34
-
35
33
  def version
36
- require 'mmailer'
34
+ require 'mmailer/version'
37
35
  puts Mmailer::VERSION
38
36
  end
39
37
 
40
- private
41
-
42
- def client(cmd, args=nil)
43
- require 'drb/drb'
44
- uri = 'druby://localhost:12345'
45
- begin
46
- obj = DRbObject.new_with_uri(uri)
47
- if args
48
- obj.send(cmd, args)
49
- else
50
- obj.send(cmd)
51
- end
52
- rescue DRb::DRbConnError => e
53
- puts e.message + "\nIs the server running? (You can start the server with `mmailer server`)"
38
+ desc "config", "Real-time mail queue configuration"
39
+ option :time_interval, :type => :numeric
40
+ option :mail_interval, :type => :numeric
41
+ option :sleep_time, :type => :numeric
42
+ def config
43
+ if options.empty?
44
+ client(:config)
45
+ else
46
+ client(:config, options)
54
47
  end
55
48
  end
49
+
56
50
  end
@@ -0,0 +1,32 @@
1
+ module Mmailer
2
+ module ErrorHandling
3
+
4
+ def try(number_of_times=3)
5
+ retry_count = 0
6
+ begin
7
+ yield
8
+ rescue Net::OpenTimeout, EOFError => e
9
+ retry_count += 1
10
+ puts "#{e.class}: #{e.message}: #{retry_count} retries"
11
+ sleep retry_count
12
+ if retry_count < number_of_times
13
+ retry
14
+ else
15
+ puts "Too many errors. Pausing mail queue."
16
+ client(:pause)
17
+ end
18
+ nil
19
+ rescue Net::SMTPUnknownError => e
20
+ puts e.message
21
+ client(:pause)
22
+ end
23
+ end
24
+
25
+ at_exit do
26
+ if $!
27
+ puts "We're going down: #{$!.class} \n #{$!.message} \n #{$!.backtrace}"
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -1,5 +1,6 @@
1
1
  module Mmailer
2
2
  class MailHelper
3
+ include ErrorHandling
3
4
  attr_reader :template, :subject, :from
4
5
 
5
6
  def initialize(args)
@@ -22,12 +23,14 @@ module Mmailer
22
23
  mail.from = from
23
24
  mail.subject = subject
24
25
 
26
+ compiled_source=ERB.new(File.read(Dir.pwd + "/" + Mmailer.configuration.template + ".md.erb")).result(binding)
27
+
25
28
  text_part = Mail::Part.new
26
- text_part.body=ERB.new(File.read(Dir.pwd + "/" + Mmailer.configuration.template + ".txt.erb")).result(binding)
29
+ text_part.body=compiled_source
27
30
 
28
31
  html_part = Mail::Part.new
29
32
  html_part.content_type='text/html; charset=UTF-8'
30
- html_part.body=ERB.new(File.read(Dir.pwd + "/" + Mmailer.configuration.template + ".html.erb")).result(binding)
33
+ html_part.body=Kramdown::Document.new(compiled_source).to_html
31
34
 
32
35
  mail.text_part = text_part
33
36
  mail.html_part = html_part
@@ -36,11 +39,11 @@ module Mmailer
36
39
 
37
40
  case ENV['MMAILER_ENV']
38
41
  when "production"
39
- mail.deliver!
42
+ try { mail.deliver! }
40
43
  when "development"
41
44
  puts mail.to_s
42
45
  else
43
- mail.deliver!
46
+ mail.to_s
44
47
  end
45
48
  end
46
49
  end
@@ -58,5 +58,16 @@ module Mmailer
58
58
  end
59
59
  end
60
60
 
61
+ def config(options=nil)
62
+ if options.nil?
63
+ puts "I will send emails every #{Mmailer.configuration.time_interval} seconds. After #{Mmailer.configuration.mail_interval} emails, I will sleep for #{Mmailer.configuration.sleep_time} seconds."
64
+ else
65
+ Mmailer.configuration.sleep_time = options.fetch("sleep_time", Mmailer.configuration.sleep_time)
66
+ Mmailer.configuration.mail_interval = options.fetch("mail_interval", Mmailer.configuration.mail_interval)
67
+ Mmailer.configuration.time_interval = options.fetch("time_interval", Mmailer.configuration.time_interval)
68
+ puts "#{options}. OK."
69
+ end
70
+ end
71
+
61
72
  end
62
73
  end
@@ -1,3 +1,3 @@
1
1
  module Mmailer
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -8,24 +8,28 @@ module Mmailer
8
8
  @obj = DRbObject.new_with_uri('druby://localhost:12345')
9
9
  meta = { subject: Mmailer.configuration.subject, from: Mmailer.configuration.from, template: Mmailer.configuration.template, provider: Mmailer.configuration.provider }
10
10
  @mailHelper = MailHelper.new(meta)
11
- @time_interval = Mmailer.configuration.time_interval
12
- @mail_interval = Mmailer.configuration.mail_interval
13
- @sleep_time = Mmailer.configuration.sleep_time
14
11
  load_collection
15
12
  exec
16
13
  end
17
14
 
15
+ private
16
+
18
17
  def exec
19
18
  while not collection.empty? do
20
19
  case obj.state
21
20
  when :paused
22
21
  sleep 1
23
22
  when :started
23
+ load_config
24
24
  index ||= from; index += 1
25
25
  user = collection.shift
26
- obj.puts "#{index}: #{user.email}"
27
- mailHelper.send_email(user) if not user.email.nil?
28
- sleep rand(time_interval)
26
+ if not user.email.nil?
27
+ obj.puts "#{index}: #{user.email}"
28
+ mailHelper.send_email(user)
29
+ sleep rand(time_interval)
30
+ else
31
+ obj.puts "#{index}: No email found. Skipping."
32
+ end
29
33
  if index % mail_interval == 0
30
34
  obj.puts "#{mail_interval} element, going to sleep for #{sleep_time} seconds"
31
35
  sleep sleep_time
@@ -45,5 +49,10 @@ module Mmailer
45
49
  obj.puts "Loaded #{collection.count} entries"
46
50
  end
47
51
 
52
+ def load_config
53
+ @time_interval = Mmailer.configuration.time_interval
54
+ @mail_interval = Mmailer.configuration.mail_interval
55
+ @sleep_time = Mmailer.configuration.sleep_time
56
+ end
48
57
  end
49
58
  end
data/lib/mmailer.rb CHANGED
@@ -4,12 +4,14 @@ module Mmailer
4
4
 
5
5
  require 'mail'
6
6
  require 'micromachine'
7
+ require 'kramdown'
7
8
  require 'erb'
8
9
  require 'drb/drb'
9
10
  require 'mmailer/config'
10
11
  require 'mmailer/providers'
11
12
  require 'mmailer/server'
12
13
  require 'mmailer/server_helper'
14
+ require 'mmailer/error_handling'
13
15
  require 'mmailer/mail_helper'
14
16
  require 'mmailer/worker'
15
17
 
data/mmailer.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.description = %q{Bulk mailer with remote control (drb server)}
12
12
  spec.summary = %q{Bulk mailing the Ruby way}
13
13
  spec.homepage = "https://github.com/danielsz/mmailer"
14
- spec.license = "MIT"
14
+ spec.license = "LGPL.v3"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency 'thor'
26
26
  spec.add_runtime_dependency 'mail'
27
27
  spec.add_runtime_dependency 'micromachine'
28
+ spec.add_runtime_dependency 'kramdown'
28
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mmailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-16 00:00:00.000000000 Z
12
+ date: 2013-08-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -107,6 +107,22 @@ dependencies:
107
107
  - - ! '>='
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: kramdown
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
110
126
  description: Bulk mailer with remote control (drb server)
111
127
  email:
112
128
  - danielsz@freeshell.org
@@ -123,8 +139,10 @@ files:
123
139
  - Rakefile
124
140
  - bin/mmailer
125
141
  - lib/mmailer.rb
142
+ - lib/mmailer/client.rb
126
143
  - lib/mmailer/commands.rb
127
144
  - lib/mmailer/config.rb
145
+ - lib/mmailer/error_handling.rb
128
146
  - lib/mmailer/mail_helper.rb
129
147
  - lib/mmailer/providers.rb
130
148
  - lib/mmailer/server.rb
@@ -135,7 +153,7 @@ files:
135
153
  - spec/spec_helper.rb
136
154
  homepage: https://github.com/danielsz/mmailer
137
155
  licenses:
138
- - MIT
156
+ - LGPL.v3
139
157
  post_install_message:
140
158
  rdoc_options: []
141
159
  require_paths: