capuchin 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +6 -0
- data/bin/capuchin +47 -0
- data/capuchin.gemspec +31 -0
- data/lib/capuchin.rb +17 -0
- data/lib/capuchin/email.rb +59 -0
- data/lib/capuchin/mailchimp.rb +53 -0
- data/lib/capuchin/markdown.rb +21 -0
- data/lib/capuchin/scheduler.rb +41 -0
- data/lib/capuchin/shell.rb +53 -0
- data/lib/capuchin/version.rb +3 -0
- data/lib/sample_directory/.env +1 -0
- data/lib/sample_directory/_config.yml +6 -0
- data/lib/sample_directory/_emails/2014-01-01-a-sample-email.md +7 -0
- data/spec/capuchin/email_spec.rb +33 -0
- data/spec/capuchin/mailchimp_spec.rb +26 -0
- data/spec/capuchin/scheduler_spec.rb +36 -0
- data/spec/cassettes/Capuchin_MailChimp/_find_list/returns_a_list_for_a_newsletter_with_a_given_name.yml +37 -0
- data/spec/cassettes/Capuchin_MailChimp/_schedule/schedules_a_campaign_with_the_correct_email_subject.yml +73 -0
- data/spec/fixtures/2013-09-01-a-future-email.md +8 -0
- data/spec/fixtures/2013-09-01-a-test-email.md +7 -0
- data/spec/spec_helper.rb +18 -0
- metadata +187 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Matt Swanson
|
2
|
+
|
3
|
+
MIT License
|
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:
|
12
|
+
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Capuchin
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/capuchin)
|
4
|
+
[](https://travis-ci.org/swanson/capuchin)
|
5
|
+
[](https://gemnasium.com/swanson/capuchin)
|
6
|
+
|
7
|
+
|
8
|
+
Capuchin is a tool for writing email newsletters. It allows you to write content in
|
9
|
+
Markdown and schedule the resulting campaign for delivery via the MailChimp API.
|
10
|
+
|
11
|
+
You can think of Capuchin as "Jekyll for email newsletters".
|
12
|
+
|
13
|
+
If you want fancy HTML you probably want to stick to MailChimp's WYSIWYG editor, but
|
14
|
+
for a newsletter where the primary content is text (and some links) give Capuchin
|
15
|
+
a shot.
|
16
|
+
|
17
|
+
# Usage
|
18
|
+
|
19
|
+
The directory structure is as follows:
|
20
|
+
|
21
|
+
```
|
22
|
+
.
|
23
|
+
├── .env
|
24
|
+
├── _config.yml
|
25
|
+
└── _emails
|
26
|
+
├── 2013-09-01-selling-software-products.md
|
27
|
+
└── 2013-09-08-creating-value-as-a-consultant.md
|
28
|
+
```
|
29
|
+
|
30
|
+
# Commands
|
31
|
+
|
32
|
+
* `capuchin new PATH` will scaffold the directory structure in PATH
|
33
|
+
* `capuchin create "Email Subject"` will generate a Markdown email file with the
|
34
|
+
provided subject
|
35
|
+
|
36
|
+
* `capuchin schedule _emails/PATH_TO_EMAIL.md` will process the input email, and
|
37
|
+
schedule it for MailChimp delivery based on the date in the YAML front-matter
|
38
|
+
(defaults to the date in the filename)
|
39
|
+
|
40
|
+
# Templating
|
41
|
+
Capuchin leverages MailChimps templating system (instead of doing templating
|
42
|
+
locally). To have your content inserted into your email, make sure your template
|
43
|
+
has `mc:edit="main"` in it.
|
44
|
+
|
45
|
+
You need to set the template ID from MailChimp in `_config.yml` (see below).
|
46
|
+
|
47
|
+
# Configuration
|
48
|
+
|
49
|
+
Set your MailChimp API key (You can see your API keys here) in the `.env` file.
|
50
|
+
|
51
|
+
DO NOT CHECK THIS INTO SOURCE CODE IF YOUR REPOSITORY IS PUBLICLY ACCESSIBLE!!
|
52
|
+
|
53
|
+
```
|
54
|
+
MAILCHIMP_API_KEY=your-mailchimp-key
|
55
|
+
```
|
56
|
+
|
57
|
+
Specify Capuchin configuration in `_config.yml`
|
58
|
+
|
59
|
+
```
|
60
|
+
capuchin:
|
61
|
+
from_email: "you@example.com"
|
62
|
+
from_name: "Joe Cool"
|
63
|
+
list_id: "ee90847678"
|
64
|
+
template_id: 71337
|
65
|
+
delivery_time: "08:30:00"
|
66
|
+
```
|
67
|
+
|
68
|
+
# Example email
|
69
|
+
Given a file `_emails\2013-09-01-selling-software-products.md` with these
|
70
|
+
contents (and the same configuration as above):
|
71
|
+
|
72
|
+
```
|
73
|
+
---
|
74
|
+
subject: Selling Software Products
|
75
|
+
---
|
76
|
+
|
77
|
+
This is an email about how to sell software!
|
78
|
+
|
79
|
+
```
|
80
|
+
|
81
|
+
Run `capuchin schedule _emails\2013-09-01-selling-software-products.md` to schedule
|
82
|
+
this campaign in MailChimp to be sent on 2013-09-01 at 8:30am to all members of
|
83
|
+
the list with a Subject of "Selling Software Products".
|
84
|
+
|
85
|
+
# Colophon
|
86
|
+
A capuchin monkey are New World monkeys of the subfamily Cebinae. They are
|
87
|
+
considered the most intelligent New World monkeys, known especially for their
|
88
|
+
long-term tool usage and social structures. Capuchin monkeys are sometimes referred
|
89
|
+
to as "nature's butlers" because of their historic use as service animals.
|
90
|
+
|
91
|
+
|
92
|
+
|
data/Rakefile
ADDED
data/bin/capuchin
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'capuchin'
|
3
|
+
require 'capuchin/shell'
|
4
|
+
|
5
|
+
require 'commander/import'
|
6
|
+
|
7
|
+
program :name, 'capuchin'
|
8
|
+
program :version, Capuchin::VERSION
|
9
|
+
program :description, 'Capuchin is a tool for writing email newsletters with Markdown'
|
10
|
+
program :help, 'Author', 'Matt Swanson <matt@mdswanson.com>'
|
11
|
+
|
12
|
+
CONFIG_FILE = "_config.yml"
|
13
|
+
|
14
|
+
options = {}
|
15
|
+
|
16
|
+
if File.exists?(CONFIG_FILE)
|
17
|
+
options.merge! YAML.load_file(CONFIG_FILE)
|
18
|
+
end
|
19
|
+
|
20
|
+
shell = Capuchin::Shell.new(options['capuchin'])
|
21
|
+
|
22
|
+
command :schedule do |c|
|
23
|
+
c.syntax = "capuchin schedule <path_to_email>"
|
24
|
+
c.description = "Schedule an email to be sent via MailChimp"
|
25
|
+
|
26
|
+
c.action do |args, options|
|
27
|
+
shell.schedule(args.first)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
command :new do |c|
|
32
|
+
c.syntax = "capuchin new <directory>"
|
33
|
+
c.description = "Creates a new Capuchin directory scaffold in <directory>"
|
34
|
+
|
35
|
+
c.action do |args, options|
|
36
|
+
shell.scaffold(args.first)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
command :create do |c|
|
41
|
+
c.syntax = "capuchin create \"Email subject\""
|
42
|
+
c.description = "Create a markdown template email with the given subject"
|
43
|
+
|
44
|
+
c.action do |args, options|
|
45
|
+
shell.create(args.first)
|
46
|
+
end
|
47
|
+
end
|
data/capuchin.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'capuchin/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "capuchin"
|
8
|
+
spec.version = Capuchin::VERSION
|
9
|
+
spec.authors = ["Matt Swanson"]
|
10
|
+
spec.email = ["matt@mdswanson.com"]
|
11
|
+
spec.description = "Jekyll for email newsletters"
|
12
|
+
spec.summary = "Jekyll for email newsletters"
|
13
|
+
spec.homepage = "https://github.com/swanson/capuchin"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "redcarpet", "~> 3.0.0"
|
22
|
+
spec.add_dependency "dotenv", "~> 0.9.0"
|
23
|
+
spec.add_dependency "gibbon", "~> 1.0.2"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "pry-debugger"
|
29
|
+
spec.add_development_dependency "vcr"
|
30
|
+
spec.add_development_dependency "webmock"
|
31
|
+
end
|
data/lib/capuchin.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'dotenv'
|
2
|
+
Dotenv.load
|
3
|
+
|
4
|
+
require "capuchin/version"
|
5
|
+
|
6
|
+
require "date"
|
7
|
+
require "erb"
|
8
|
+
require "gibbon"
|
9
|
+
require "redcarpet"
|
10
|
+
require "yaml"
|
11
|
+
|
12
|
+
require "capuchin/markdown"
|
13
|
+
require "capuchin/email"
|
14
|
+
require "capuchin/scheduler"
|
15
|
+
require "capuchin/mailchimp"
|
16
|
+
|
17
|
+
module Capuchin; end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Capuchin
|
2
|
+
class Email
|
3
|
+
FILENAME_REGEX = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
4
|
+
FRONT_MATTER_REGEX = /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
5
|
+
|
6
|
+
def initialize(dir, filename)
|
7
|
+
@dir = dir
|
8
|
+
@filename = filename
|
9
|
+
|
10
|
+
@options = {}
|
11
|
+
@renderer = Capuchin::Markdown.new
|
12
|
+
|
13
|
+
parse_file_contents
|
14
|
+
end
|
15
|
+
|
16
|
+
def date
|
17
|
+
@options['date'] || @date
|
18
|
+
end
|
19
|
+
|
20
|
+
def slug
|
21
|
+
@slug
|
22
|
+
end
|
23
|
+
|
24
|
+
def subject
|
25
|
+
@options['subject']
|
26
|
+
end
|
27
|
+
|
28
|
+
def content
|
29
|
+
@content ||= @renderer.render(@markdown_content)
|
30
|
+
end
|
31
|
+
|
32
|
+
def output_filename
|
33
|
+
"#{date.strftime('%Y-%m-%d')}-#{slug}.html"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def parse_file_contents
|
38
|
+
decode_filename
|
39
|
+
split_contents_from_front_matter
|
40
|
+
end
|
41
|
+
|
42
|
+
def decode_filename
|
43
|
+
m, date_str, @slug, ext = *@filename.match(FILENAME_REGEX)
|
44
|
+
@date = Date.parse(date_str)
|
45
|
+
end
|
46
|
+
|
47
|
+
def split_contents_from_front_matter
|
48
|
+
if file_contents =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
49
|
+
@markdown_content = $' # Post match content
|
50
|
+
@options.merge!(YAML.load($1))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def file_contents
|
55
|
+
@file_contents ||= File.open(File.join(@dir, @filename)).read
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Capuchin
|
2
|
+
class MailChimp
|
3
|
+
def initialize
|
4
|
+
@client = Gibbon::API.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def find_list(list_name)
|
8
|
+
list = @client.lists.list(filters: { listname: list_name }, limit: 1)
|
9
|
+
list['data'].first
|
10
|
+
end
|
11
|
+
|
12
|
+
def schedule(email, list_id, template_id, from_name, from_email, time)
|
13
|
+
campaign = create_campaign(email, list_id, template_id, from_name, from_email)
|
14
|
+
schedule_delivery(campaign['id'], email.date, time)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def create_campaign(email, list_id, template_id, from_name, from_email)
|
19
|
+
campaign_options = build_campaign_options(email, list_id, template_id, from_name, from_email)
|
20
|
+
@client.campaigns.create(campaign_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def schedule_delivery(campaign_id, date, time)
|
24
|
+
@client.campaigns.schedule(build_schedule_options(campaign_id, date, time))
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_campaign_options(email, list_id, template_id, from_name, from_email)
|
28
|
+
{
|
29
|
+
type: "regular",
|
30
|
+
options: {
|
31
|
+
list_id: list_id,
|
32
|
+
subject: email.subject,
|
33
|
+
from_name: from_name,
|
34
|
+
from_email: from_email,
|
35
|
+
generate_text: true,
|
36
|
+
template_id: template_id
|
37
|
+
},
|
38
|
+
content: {
|
39
|
+
sections: {
|
40
|
+
main: email.content
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_schedule_options(campaign_id, date, time)
|
47
|
+
{
|
48
|
+
cid: campaign_id,
|
49
|
+
schedule_time: "#{date} #{time}"
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Capuchin
|
2
|
+
class Markdown
|
3
|
+
def initialize
|
4
|
+
options = {
|
5
|
+
autolink: true,
|
6
|
+
space_after_headers: true,
|
7
|
+
fenced_code_blocks: true,
|
8
|
+
no_intra_emphasis: true,
|
9
|
+
strikethrough: true,
|
10
|
+
underline: true,
|
11
|
+
superscript: true
|
12
|
+
}
|
13
|
+
|
14
|
+
@engine = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render(markdown)
|
18
|
+
@engine.render(markdown)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Capuchin
|
2
|
+
class Scheduler
|
3
|
+
def initialize(email, options, api = Capuchin::MailChimp.new)
|
4
|
+
@email = email
|
5
|
+
@options = options
|
6
|
+
|
7
|
+
@api = api
|
8
|
+
end
|
9
|
+
|
10
|
+
def schedule
|
11
|
+
result = @api.schedule(@email, list_id, template_id, from_name, from_email, delivery_time)
|
12
|
+
|
13
|
+
if result['complete']
|
14
|
+
"#{@email.subject} was scheduled to be sent."
|
15
|
+
else
|
16
|
+
raise "Something went wrong!"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def list_id
|
22
|
+
@options['list_id']
|
23
|
+
end
|
24
|
+
|
25
|
+
def template_id
|
26
|
+
@options['template_id']
|
27
|
+
end
|
28
|
+
|
29
|
+
def from_name
|
30
|
+
@options['from_name']
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_email
|
34
|
+
@options['from_email']
|
35
|
+
end
|
36
|
+
|
37
|
+
def delivery_time
|
38
|
+
@options['delivery_time']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Capuchin
|
2
|
+
class Shell
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def schedule(path)
|
10
|
+
dir = File.dirname(path)
|
11
|
+
filename = File.basename(path)
|
12
|
+
|
13
|
+
email = Capuchin::Email.new(dir, filename)
|
14
|
+
|
15
|
+
puts Capuchin::Scheduler.new(email, @options).schedule
|
16
|
+
end
|
17
|
+
|
18
|
+
def scaffold(path)
|
19
|
+
directory_path = File.expand_path(path, Dir.pwd)
|
20
|
+
|
21
|
+
FileUtils.mkdir_p(directory_path)
|
22
|
+
FileUtils.cp_r sample_directory + '/.', directory_path
|
23
|
+
|
24
|
+
puts "Capuchin directory structure created in #{directory_path}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def create(subject)
|
28
|
+
slug = slugify(subject)
|
29
|
+
filename = "#{Time.now.strftime('%Y-%m-%d')}-#{slug}.md"
|
30
|
+
|
31
|
+
File.open(File.join("_emails", filename), 'w') do |f|
|
32
|
+
f.write <<-eos
|
33
|
+
---
|
34
|
+
subject: "#{subject}"
|
35
|
+
---
|
36
|
+
|
37
|
+
Write your kick-ass content here!
|
38
|
+
eos
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "Created _emails/#{filename}"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def sample_directory
|
46
|
+
File.expand_path("../sample_directory", File.dirname(__FILE__))
|
47
|
+
end
|
48
|
+
|
49
|
+
def slugify(text)
|
50
|
+
text.scan(/\w+/).join("-").downcase
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
MAILCHIMP_API_KEY=YOUR KEY GOES HERE
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Capuchin::Email do
|
4
|
+
|
5
|
+
let(:email) { Capuchin::Email.new("spec/fixtures", "2013-09-01-a-test-email.md") }
|
6
|
+
let(:future_email) { Capuchin::Email.new("spec/fixtures", "2013-09-01-a-future-email.md") }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
it "parses fields from YAML front matter" do
|
11
|
+
email.subject.should eq "A Test Email"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "parses fields from filename" do
|
15
|
+
email.date.should eq Date.new(2013, 9, 1)
|
16
|
+
email.slug.should eq "a-test-email"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "overwrites filename fields with front matter" do
|
20
|
+
future_email.date.should eq Date.new(2013, 9, 8)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "converts the markdown email to HTML" do
|
24
|
+
email.content.should include("This is a test email")
|
25
|
+
email.content.should include("<em>Markdown</em>")
|
26
|
+
email.content.should include("<a href=\"http://google.com\">link</a>")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "knows the filename to export to" do
|
30
|
+
email.output_filename.should eq "2013-09-01-a-test-email.html"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Capuchin::MailChimp do
|
4
|
+
|
5
|
+
let(:mc) { Capuchin::MailChimp.new }
|
6
|
+
let(:email) { Capuchin::Email.new("spec/fixtures", "2013-09-01-a-test-email.md") }
|
7
|
+
let(:list_id) { "88300e1d4f" }
|
8
|
+
let(:template_id) { 34557 }
|
9
|
+
let(:time) { "08:00:00" }
|
10
|
+
|
11
|
+
describe ".find_list", vcr: true do
|
12
|
+
it "returns a list for a newsletter with a given name" do
|
13
|
+
list = mc.find_list("Blog newsletter")
|
14
|
+
|
15
|
+
list['id'].should eq list_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".schedule", vcr: true do
|
20
|
+
it "schedules a campaign with the correct email subject" do
|
21
|
+
result = mc.schedule(email, list_id, template_id, "Test", "matt@mdswanson.com", time)
|
22
|
+
|
23
|
+
result['complete'].should be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Capuchin::Scheduler do
|
4
|
+
|
5
|
+
let(:email) { Capuchin::Email.new("spec/fixtures", "2013-09-01-a-test-email.md") }
|
6
|
+
let(:list_id) { "123asdf" }
|
7
|
+
let(:template_id) { 1337 }
|
8
|
+
let(:from_name) { "Test" }
|
9
|
+
let(:from_email) { "test@example.com" }
|
10
|
+
let(:delivery_time) { "10:30:00" }
|
11
|
+
let(:options) {
|
12
|
+
{
|
13
|
+
'list_id' => list_id,
|
14
|
+
'template_id' => template_id,
|
15
|
+
'from_name' => from_name,
|
16
|
+
'from_email' => from_email,
|
17
|
+
'delivery_time' => delivery_time
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:fake_api) { double }
|
22
|
+
|
23
|
+
let(:scheduler) { Capuchin::Scheduler.new(email, options, fake_api) }
|
24
|
+
|
25
|
+
describe ".schedule", vcr: true do
|
26
|
+
it "schedules the email for delivery" do
|
27
|
+
fake_api.should_receive(:schedule)
|
28
|
+
.with(email, list_id, template_id, from_name, from_email, delivery_time)
|
29
|
+
.and_return({'complete' => true})
|
30
|
+
|
31
|
+
result = scheduler.schedule
|
32
|
+
|
33
|
+
result.should include "A Test Email was scheduled to be sent"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://us7.api.mailchimp.com/2.0/lists/list
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ! '{"apikey":"<API_KEY>","filters":{"listname":"Blog newsletter"},"limit":1}'
|
9
|
+
headers: {}
|
10
|
+
response:
|
11
|
+
status:
|
12
|
+
code: 200
|
13
|
+
message: OK
|
14
|
+
headers:
|
15
|
+
Server:
|
16
|
+
- nginx
|
17
|
+
Date:
|
18
|
+
- Wed, 25 Sep 2013 15:51:21 GMT
|
19
|
+
Content-Type:
|
20
|
+
- application/json; charset=utf-8
|
21
|
+
Content-Length:
|
22
|
+
- '967'
|
23
|
+
Connection:
|
24
|
+
- keep-alive
|
25
|
+
Vary:
|
26
|
+
- Accept-Encoding
|
27
|
+
Set-Cookie:
|
28
|
+
- _AVESTA_ENVIRONMENT=prod; path=/
|
29
|
+
body:
|
30
|
+
encoding: US-ASCII
|
31
|
+
string: ! '
|
32
|
+
|
33
|
+
{"total":1,"data":[{"id":"88300e1d4f","web_id":97817,"name":"Test list","date_created":"2013-09-25
|
34
|
+
15:45:30","email_type_option":false,"use_awesomebar":true,"default_from_name":"Matt","default_from_email":"swan3788@gmail.com","default_subject":"","default_language":"en","list_rating":0,"subscribe_url_short":"http:\/\/eepurl.com\/FTpmr","subscribe_url_long":"http:\/\/github.us7.list-manage2.com\/subscribe?u=97cf7c859d9879d874ba9e34e&id=88300e1d4f","beamer_address":"OTdjZjdjODU5ZDk4NzlkODc0YmE5ZTM0ZS0xYmU1NDY1NC1hZWE5LTQ3MjMtOWUxYy1lMGRhZGEzMzUzYjE@campaigns.mailchimp.com","visibility":"pub","stats":{"member_count":0,"unsubscribe_count":0,"cleaned_count":0,"member_count_since_send":0,"unsubscribe_count_since_send":0,"cleaned_count_since_send":0,"campaign_count":1,"grouping_count":0,"group_count":0,"merge_var_count":2,"avg_sub_rate":0,"avg_unsub_rate":0,"target_sub_rate":0,"open_rate":0,"click_rate":0,"date_last_campaign":null},"modules":[]}],"errors":[]}'
|
35
|
+
http_version:
|
36
|
+
recorded_at: Wed, 25 Sep 2013 15:51:21 GMT
|
37
|
+
recorded_with: VCR 2.5.0
|
@@ -0,0 +1,73 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://us7.api.mailchimp.com/2.0/campaigns/create
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ! '{"apikey":"<API_KEY>","type":"regular","options":{"list_id":"88300e1d4f","subject":"A
|
9
|
+
Test Email","from_name":"Test","from_email":"matt@mdswanson.com","generate_text":true,"template_id":34557},"content":{"sections":{"main":"<p>This
|
10
|
+
is a test email with some <em>Markdown</em> and a <a href=\"http://google.com\">link</a>.</p>\n"}}}'
|
11
|
+
headers: {}
|
12
|
+
response:
|
13
|
+
status:
|
14
|
+
code: 200
|
15
|
+
message: OK
|
16
|
+
headers:
|
17
|
+
Server:
|
18
|
+
- nginx
|
19
|
+
Date:
|
20
|
+
- Wed, 25 Sep 2013 15:51:20 GMT
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Content-Length:
|
24
|
+
- '917'
|
25
|
+
Connection:
|
26
|
+
- keep-alive
|
27
|
+
Vary:
|
28
|
+
- Accept-Encoding
|
29
|
+
Set-Cookie:
|
30
|
+
- _AVESTA_ENVIRONMENT=prod; path=/
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: ! '
|
34
|
+
|
35
|
+
{"id":"c838606511","web_id":172045,"list_id":"88300e1d4f","folder_id":0,"template_id":34557,"content_type":"template","content_edited_by":"matt
|
36
|
+
swanson","title":"A Test Email","type":"regular","create_time":"2013-09-25
|
37
|
+
15:51:19","send_time":null,"content_updated_time":"2013-09-25 15:51:20","status":"save","from_name":"Test","from_email":"matt@mdswanson.com","subject":null,"to_name":"","archive_url":"http:\/\/eepurl.com\/FTqyL","archive_url_long":"http:\/\/us7.campaign-archive2.com\/?u=97cf7c859d9879d874ba9e34e&id=c838606511","emails_sent":0,"inline_css":false,"analytics":"N","analytics_tag":"","authenticate":false,"ecomm360":false,"auto_tweet":false,"auto_fb_post":"","auto_footer":false,"timewarp":false,"timewarp_schedule":null,"tracking":{"html_clicks":true,"text_clicks":true,"opens":true},"parent_id":"","tests_sent":0,"tests_remain":12,"segment_text":"No
|
38
|
+
segment used","segment_opts":[],"type_opts":[]}'
|
39
|
+
http_version:
|
40
|
+
recorded_at: Wed, 25 Sep 2013 15:51:20 GMT
|
41
|
+
- request:
|
42
|
+
method: post
|
43
|
+
uri: https://us7.api.mailchimp.com/2.0/campaigns/schedule
|
44
|
+
body:
|
45
|
+
encoding: UTF-8
|
46
|
+
string: ! '{"apikey":"<API_KEY>","cid":"c838606511","schedule_time":"2014-12-30
|
47
|
+
20:30:00"}'
|
48
|
+
headers: {}
|
49
|
+
response:
|
50
|
+
status:
|
51
|
+
code: 200
|
52
|
+
message: OK
|
53
|
+
headers:
|
54
|
+
Server:
|
55
|
+
- nginx
|
56
|
+
Date:
|
57
|
+
- Wed, 25 Sep 2013 15:51:20 GMT
|
58
|
+
Content-Type:
|
59
|
+
- application/json; charset=utf-8
|
60
|
+
Content-Length:
|
61
|
+
- '18'
|
62
|
+
Connection:
|
63
|
+
- keep-alive
|
64
|
+
Set-Cookie:
|
65
|
+
- _AVESTA_ENVIRONMENT=prod; path=/
|
66
|
+
body:
|
67
|
+
encoding: US-ASCII
|
68
|
+
string: ! '
|
69
|
+
|
70
|
+
{"complete":true}'
|
71
|
+
http_version:
|
72
|
+
recorded_at: Wed, 25 Sep 2013 15:51:21 GMT
|
73
|
+
recorded_with: VCR 2.5.0
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "pry-debugger"
|
3
|
+
require "capuchin"
|
4
|
+
require "tempfile"
|
5
|
+
require "vcr"
|
6
|
+
|
7
|
+
VCR.configure do |config|
|
8
|
+
config.cassette_library_dir = 'spec/cassettes'
|
9
|
+
config.hook_into :webmock
|
10
|
+
config.default_cassette_options = { :record => :new_episodes }
|
11
|
+
config.configure_rspec_metadata!
|
12
|
+
|
13
|
+
config.filter_sensitive_data("<API_KEY>") { ENV['MAILCHIMP_API_KEY'] }
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.order = 'random'
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capuchin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Swanson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redcarpet
|
16
|
+
requirement: &70340657349600 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70340657349600
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dotenv
|
27
|
+
requirement: &70340657349100 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.9.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70340657349100
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: gibbon
|
38
|
+
requirement: &70340657348640 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.0.2
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70340657348640
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: &70340657348180 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70340657348180
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rake
|
60
|
+
requirement: &70340657347800 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70340657347800
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: &70340657347340 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70340657347340
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: pry-debugger
|
82
|
+
requirement: &70340657346920 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *70340657346920
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: vcr
|
93
|
+
requirement: &70340657346500 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *70340657346500
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: webmock
|
104
|
+
requirement: &70340657346080 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *70340657346080
|
113
|
+
description: Jekyll for email newsletters
|
114
|
+
email:
|
115
|
+
- matt@mdswanson.com
|
116
|
+
executables:
|
117
|
+
- capuchin
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- .gitignore
|
122
|
+
- .rspec
|
123
|
+
- .travis.yml
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE.txt
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- bin/capuchin
|
129
|
+
- capuchin.gemspec
|
130
|
+
- lib/capuchin.rb
|
131
|
+
- lib/capuchin/email.rb
|
132
|
+
- lib/capuchin/mailchimp.rb
|
133
|
+
- lib/capuchin/markdown.rb
|
134
|
+
- lib/capuchin/scheduler.rb
|
135
|
+
- lib/capuchin/shell.rb
|
136
|
+
- lib/capuchin/version.rb
|
137
|
+
- lib/sample_directory/.env
|
138
|
+
- lib/sample_directory/_config.yml
|
139
|
+
- lib/sample_directory/_emails/2014-01-01-a-sample-email.md
|
140
|
+
- spec/capuchin/email_spec.rb
|
141
|
+
- spec/capuchin/mailchimp_spec.rb
|
142
|
+
- spec/capuchin/scheduler_spec.rb
|
143
|
+
- spec/cassettes/Capuchin_MailChimp/_find_list/returns_a_list_for_a_newsletter_with_a_given_name.yml
|
144
|
+
- spec/cassettes/Capuchin_MailChimp/_schedule/schedules_a_campaign_with_the_correct_email_subject.yml
|
145
|
+
- spec/fixtures/2013-09-01-a-future-email.md
|
146
|
+
- spec/fixtures/2013-09-01-a-test-email.md
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
homepage: https://github.com/swanson/capuchin
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
hash: 3388434341836609294
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ! '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
segments:
|
171
|
+
- 0
|
172
|
+
hash: 3388434341836609294
|
173
|
+
requirements: []
|
174
|
+
rubyforge_project:
|
175
|
+
rubygems_version: 1.8.11
|
176
|
+
signing_key:
|
177
|
+
specification_version: 3
|
178
|
+
summary: Jekyll for email newsletters
|
179
|
+
test_files:
|
180
|
+
- spec/capuchin/email_spec.rb
|
181
|
+
- spec/capuchin/mailchimp_spec.rb
|
182
|
+
- spec/capuchin/scheduler_spec.rb
|
183
|
+
- spec/cassettes/Capuchin_MailChimp/_find_list/returns_a_list_for_a_newsletter_with_a_given_name.yml
|
184
|
+
- spec/cassettes/Capuchin_MailChimp/_schedule/schedules_a_campaign_with_the_correct_email_subject.yml
|
185
|
+
- spec/fixtures/2013-09-01-a-future-email.md
|
186
|
+
- spec/fixtures/2013-09-01-a-test-email.md
|
187
|
+
- spec/spec_helper.rb
|