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