• Stars
    star
    121
  • Rank 293,924 (Top 6 %)
  • Language
    Ruby
  • License
    Other
  • Created over 11 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The purpose of Mmailer is to allow the sending of bulk email through regular smtp providers, like gmail.

Mmailer

Rationale

The purpose of Mmailer is to allow the sending of personalized bulk email, like a newsletter, through regular SMTP providers (for example Gmail). 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.

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...

Is it any good?

Yes.

Installation

$ gem install mmailer

Usage

All functionality is invoked via the gem's binary, mmailer.

$ mmailer

Principle of operation

A server runs behind the scenes, managing the email queue, and you send it commands to start, pause, resume or stop.

### Server

You start the server in a terminal.

$ mmailer server

### Remote control

You issue commands in a separate terminal. To start sending emails, type:

$ mmailer start

To pause:

$ mmailer pause

To resume:

$ mmailer resume

To stop:

$ mmailer stop

To restart from the 56th element in your queue (more on this later).

$ mmailer start 56

Bundler

Although this gem performs as a standalone program, nothing prevents you from adding the following in a project's Gemfile:

gem 'mmailer'

And then execute:

$ bundle

In this case, you can run

bundle exec mmailer

## Configuration

mmailer doesn't require any external code to operate. Instead, you configure it. You need to provide three things in order to let mmailer send bulk email.

  • a configuration file
  • template files
  • environment variables

Configuration file

Mmailer will look for a file name config.rb in the directory where you run it. Here is what a sample configuration file looks like:

Mmailer.configure do |config|
  config.provider = :gmail
  config.from = 'Daenerys Targaryen <daenerys@house_targaryen.com>'
  config.subject = "Fire and Blood"
  config.time_interval = 6          #optional, default value is 6 seconds
  config.mail_interval = 48         #optional, default value is 48 emails
  config.sleep_time = 3600          #optional, default value is 3600 seconds
  config.template = "newsletter"
  config.collection = lambda do
    User = Struct.new(:email, :name)
    [User.new("[email protected]", "Greyjoy"), User.new("[email protected]", "Lannister"), User.new("[email protected]", "Martell")]
  end
end
  • from: The from address that will be used in your emails.
  • subject: The subject of your email.
  • provider: The name of your provider. These are presets. For the moment, one of :gmail, :zoho, :mandrill or :mailgun. Please add more providers via pull requests or by sending me mail.
  • time_interval: The number of seconds we want to wait between emails. This value is randomized, and represents thus the ceiling (maximum value).
  • mail_interval: How many emails we want to send before sleeping (see below).
  • sleep_time: How long we sleep when we reach the mail interval (see above).
  • 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.
  • 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.

Templates

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.

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 (newsletter.md.erb) might look like this:

Dear <%= user.name %>,

This is my newsletter.

Yours.

It will result in the following text/html and text/plain bodies.

<p>Dear John Doe,</p>
<p>This is my newsletter.</p>
<p>Yours.</p>
Dear John Doe,

This is my newsletter.

Yours.

### Environment variables

Ruby can load environment variables for you. It is thus convenient to put them at the top of config.rb

ENV['GMAIL_USERNAME']="username"
ENV['GMAIL_PASSWORD']="password"
ENV['MMAILER_ENV'] = "production"
  • MMAILER_ENV: In production mode, emails get sent. In development mode, they get printed to STDOUT.
  • PROVIDER_USERNAME: Username for the provider.
  • PROVIDER_PASSWORD: Password for the provider.

You can define multiple pairs of usernames and passwords for the predefined providers.

### On-the-fly configuration

Several configuration options can be changed dynamically, while the server is running.

Those are:

  • time_interval: The number of seconds we want to wait between emails. This value is randomized, and represents thus the ceiling (maximum value).
  • mail_interval: How many emails we want to send before sleeping (see below).
  • sleep_time: How long we sleep when we reach the mail interval (see above).

For usage instructions, type:

$ mmailer help config

Real world examples

Mongodb

This will show you how to use Mmailer when your data lives in Mongodb. We are going to use mongoid to make the queries.

Make a directory and create the configuration file and template files like previously described.

The config.rb would look like this:

ENV['GMAIL_USERNAME']="username"
ENV['GMAIL_PASSWORD']="password"
ENV['MMAILER_ENV'] = "development"

require "rubygems"
require "mongoid"
require_relative "mongo_helper"

Mmailer.configure do |config|
  config.provider = :gmail
  config.subject = "My newsletter"
  config.template = "newsletter"
  config.collection = lambda { User.all.entries }
  config.from = 'John Doe <[email protected]>'
end

Copy your mongoid.yml from your production system in the current directory. And create a mongo_helper.rb with your domain models.

Mongoid.load!(File.join(Dir.pwd, "mongoid.yml"), :production)

class User
  include Mongoid::Document
  has_many :profiles
  ... #the rest of your relations
end

class Profile
    include Mongoid::Document
end

... #the rest of the model classes that User references

The content of your directory would thus look something like this:

ls -l
total 40
-rw-r--r--  1 daniel  1000   424 יול 14 03:43 config.rb
-rw-r--r--  1 daniel  1000  3587 יול 10 04:08 mongo_helper.rb
-rw-r--r--  1 daniel  1000  3027 יול 10 03:39 mongoid.yml
-rw-r--r--  1 daniel  1000    81 יול 14 03:44 newsletter.md.erb

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.

### More examples

More configuration examples soon. (Please don't hesitate to contribute your configurations.)

## Architecture & Implementation

DRb

The server exposes an object representing the state of your queue (started/stopped/paused). When the client asks the server to start sending email, the server spawns a thread which will subsequently check on that state object after each email sending, thus knowing if it should proceed, halt, or change behavior in other ways. DRb is used to implement this model.

State machine

We use MicroMachine, a minimal finite state machine, to help with the state transitioning.

Mail

We leverage the ubiquitous Mail gem to do the actual sending of email.

CLI

We used Thor to provide a command line interface.

Web interface

This program will be best served with some sort of GUI. A web-based interface is under consideration. (Sinatra could be a good fit).

Status

This is an initial release. Currently, no checks or sanitation is done when parsing the configuration. Mmailer will just blow up when an error is encountered. At this early stage, the project targets power users and contributors. Others may want to wait for a later release that will hopefully sport a web interface with better usability.

Roadmap

License

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.

## Donations

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, my username is danielsz. Thank you in advance.

## Spam

Mmailer is a bulk mail sending tool. Don't use it for spamming purposes. Spam is evil.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

More Repositories

1

system

Reloaded components à la carte
Clojure
607
star
2

holygrail

Demo of a restartable back-end in the REPL + hot-reloadable front-end in the browser.
Clojure
131
star
3

certificaat

General-purpose ACME client
Clojure
104
star
4

meyvn

Next-gen builds for Clojure
85
star
5

android-oauth2-client

Android OAuth2 client for the Resource Owner Password Grant
Java
57
star
6

Palimpsest

Palimpsest, an Emacs minor mode providing various strategies when deleting text
Emacs Lisp
47
star
7

cohere-clojure

Unofficial port of the Cohere SDK
Clojure
43
star
8

benjamin

Idempotency with side-effects
Clojure
31
star
9

system-dependency-injection

Example for dependency injection with system
Clojure
29
star
10

kampbell

Entity management on top of key-value stores
Clojure
21
star
11

oauth2-client

Java OAuth2 client for the Resource Owner Password Grant
Java
16
star
12

om-mode

Insert Om component template with life cycle.
Emacs Lisp
14
star
13

boot-autoprefixer

Boot task for css autoprefixer
Clojure
12
star
14

sente-boot

Sente's example app for Boot
Clojure
11
star
15

back-end-template

Clojure back end template with components
Clojure
11
star
16

sente-system

Sente example app for system
Clojure
9
star
17

system-websockets

Wiring example for a system with Sente.
Clojure
9
star
18

om-pageslider

Om implementation of Christophe Coenraets's experiment with page transitions in React.js
CSS
9
star
19

lein-runit

Leiningen plugin providing runit integration, a UNIX init scheme with service supervision.
Clojure
9
star
20

etsy-clojure-api

Etsy API library for Clojure
Clojure
7
star
21

sketching-with-css-in-clojure

How to sketch css in Clojure - live coding in emacs and the browser.
Clojure
6
star
22

boot-environ

Compatibility layer for Boot and environ.
Clojure
6
star
23

blat

A library to fetch results concurrently from pagination APIs
Clojure
6
star
24

boot-shell

Runs a shell script straight from your Boot fileset, as root if needed.
Clojure
5
star
25

meyvn-el

This package provides an Emacs client for the Meyvn build tool, https://meyvn.org
Emacs Lisp
5
star
26

ring-websockets-meyvn

A fully fleshed example that shows how to piece everything together in Meyvn
Clojure
5
star
27

boot-runit

A boot task for runit, an init scheme with service supervision
Clojure
4
star
28

RubyonRails

Mergers and acquisitions
Ruby
3
star
29

system-advanced-example

Database and web handler example for system
Clojure
3
star
30

om-flash-bootstrap

Flash widget for Om and Bootstrap
Clojure
2
star
31

blog

Early blog
JavaScript
2
star
32

boot-binstub

Stub an executable in boot/bin directory for boot-clj projects .
Clojure
2
star
33

Circles.dart

Circles DART version
Dart
2
star
34

javafxdemo

Demo of the meyvn javafx template
Clojure
2
star
35

interactiveprogrammingtalk

Slides for the interactive programming talk
HTML
2
star
36

Circles

Josef Müller-Brockmann copycat
2
star
37

cljs-utils

General purpose utilities for clojurescript development.
Clojure
1
star
38

scramblies

Flexiana's scramblies test
Clojure
1
star
39

helloworld

Testing Meyvn builds via github actions
Clojure
1
star
40

underground_talk_dec_19

Ruby metaprogramming from scratch
JavaScript
1
star
41

kryptos

Cryptographic helper library
Clojure
1
star
42

newspeak-mode

Major mode for the Newspeak language
Emacs Lisp
1
star
43

meyvn-installer

The official Meyvn installer
Clojure
1
star
44

om-meetup

Demo of Om by way of RSVP'ed users for a meetup (Meetup.com)
Clojure
1
star
45

system-duct-style-example

This demonstrates a project with two endpoints and middleware common to both
Clojure
1
star
46

gerbil-gumbo

Gumbo parser bindings for Gerbil scheme
Scheme
1
star
47

appimagetool-maven-plugin

Will take jpackage output and use it as input for appimagetool
Clojure
1
star
48

bytecoder-clojure

Hello world in Clojure - bytecoder
Clojure
1
star
49

telefunken

Send email, contact form, fully integrated with system
Clojure
1
star
50

shade-edn-transformer

Shade plugin transformer to merge data readers (Clojure) when building jars
Java
1
star
51

detijd

Time utils (syntactic sugar)
Clojure
1
star
52

lang-utils

Language utilities, complementing Clojure to do useful things with the data structures. No dependencies. Clojure/Clojurescript
Clojure
1
star
53

Metaprogramming

Ruby underground's metaprogramming from scratch complementary code
Ruby
1
star
54

ring-utils

Ring utils (status, logout)
Clojure
1
star
55

ghosts-in-the-machine

Accompanying code for http://danielsz.github.io/2016/05/06/Ghosts-in-the-machine
Clojure
1
star
56

hashtags

GraalVM-based script to shuffle and display hashtags for Instagram posting
Clojure
1
star