capuchin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /.env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
+ [![Gem Version](https://badge.fury.io/rb/capuchin.png)](http://badge.fury.io/rb/capuchin)
4
+ [![Build Status](https://travis-ci.org/swanson/capuchin.png)](https://travis-ci.org/swanson/capuchin)
5
+ [![Dependency Status](https://gemnasium.com/swanson/capuchin.png)](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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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,3 @@
1
+ module Capuchin
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1 @@
1
+ MAILCHIMP_API_KEY=YOUR KEY GOES HERE
@@ -0,0 +1,6 @@
1
+ capuchin:
2
+ from_email: YOUR_EMAIL
3
+ from_name: YOUR_NAME
4
+ list_id: YOUR_ID
5
+ template_id: YOUR_TEMPLATE_ID
6
+ delivery_time: "08:30:00"
@@ -0,0 +1,7 @@
1
+ ---
2
+ subject: "New Year's Resolution: Start a newsletter!"
3
+ ---
4
+
5
+ Whelp. Here is a sample post for your hypothetical newsletter.
6
+
7
+ Make sure you go fill in *your* information in `.env` and `_config.yml`!
@@ -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
@@ -0,0 +1,8 @@
1
+ ---
2
+ subject: A Test Email
3
+ date: 2013-09-08
4
+ ---
5
+
6
+ This is a test email with some *Markdown* and a [link][l].
7
+
8
+ [l]: http://google.com
@@ -0,0 +1,7 @@
1
+ ---
2
+ subject: A Test Email
3
+ ---
4
+
5
+ This is a test email with some *Markdown* and a [link][l].
6
+
7
+ [l]: http://google.com
@@ -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