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 +9 -2
- data/LICENSE.txt +6 -18
- data/README.md +46 -14
- data/lib/mmailer/client.rb +18 -0
- data/lib/mmailer/commands.rb +15 -21
- data/lib/mmailer/error_handling.rb +32 -0
- data/lib/mmailer/mail_helper.rb +7 -4
- data/lib/mmailer/server_helper.rb +11 -0
- data/lib/mmailer/version.rb +1 -1
- data/lib/mmailer/worker.rb +15 -6
- data/lib/mmailer.rb +2 -0
- data/mmailer.gemspec +2 -1
- metadata +21 -3
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
-
##
|
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
|
-
##
|
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
|
-
|
3
|
+
LGPLv3 license
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
14
|
-
|
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
|
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.
|
107
|
-
* `mail_interval`:
|
108
|
-
* `sleep_time`: How long we
|
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
|
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
|
-
|
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
|
-
|
128
|
+
It will result in the following `text/html` and `text/plain` bodies.
|
126
129
|
|
127
130
|
```ruby
|
128
|
-
<p>Dear
|
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.
|
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
|
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
|
data/lib/mmailer/commands.rb
CHANGED
@@ -1,56 +1,50 @@
|
|
1
|
+
require 'mmailer/client'
|
2
|
+
|
1
3
|
class MyCLI < Thor
|
2
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
data/lib/mmailer/mail_helper.rb
CHANGED
@@ -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=
|
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=
|
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.
|
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
|
data/lib/mmailer/version.rb
CHANGED
data/lib/mmailer/worker.rb
CHANGED
@@ -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
|
-
|
27
|
-
|
28
|
-
|
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 = "
|
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.
|
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-
|
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
|
-
-
|
156
|
+
- LGPL.v3
|
139
157
|
post_install_message:
|
140
158
|
rdoc_options: []
|
141
159
|
require_paths:
|