mr_poole 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +131 -0
- data/Rakefile +6 -0
- data/bin/poole +8 -0
- data/lib/mr_poole/cli.rb +85 -0
- data/lib/mr_poole/commands.rb +110 -0
- data/lib/mr_poole/helper.rb +113 -0
- data/lib/mr_poole/version.rb +3 -0
- data/lib/mr_poole.rb +8 -0
- data/mr_poole.gemspec +33 -0
- data/spec/cli_spec.rb +328 -0
- data/spec/command_spec.rb +290 -0
- data/spec/spec_helper.rb +57 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30428d35eafd9d58388483a2a69f299e7014f89a
|
4
|
+
data.tar.gz: b1f25c5ba6677ddc1568f3d62310a751f0a6b68a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 83cbc63e84de3ac30014a48b4ba1d376ea12add90644c3c320d9456737d26931a03050f09b38cbd1b1f642b20da5f654740e2b286d0e0cf24e423c8e4c0b30b1
|
7
|
+
data.tar.gz: 0bc63bef197d7b5d5d5e4f9a8840e9faaf42aab26f0fbdd2b2ff334380d50ac1b6b5b51dd0debdf0c161fcc330606165e2d62d6a7a88634f3a757581ba6a36a0
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Michael McClimon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Mr. Poole
|
2
|
+
|
3
|
+
A butler for Jekyll. Provides a command-line interface (called `poole`) for
|
4
|
+
creating and publishing posts and drafts for [Jekyll](http://jekyllrb.com)
|
5
|
+
blogs.
|
6
|
+
|
7
|
+
The literary Mr. Poole is Jekyll's butler, who "serves Jekyll faithfully, and
|
8
|
+
attempts to do a good job and be loyal to his master"
|
9
|
+
[Wikipedia](http://en.wikipedia.org/wiki/Jekyll_and_hyde#Mr._Poole), and the
|
10
|
+
Mr. Poole gem looks to be the same thing.
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
Mr. Poole is primarily a command-line application: the gem installs an
|
15
|
+
executable called `poole` in your path. It has four subcommands: post, draft,
|
16
|
+
publish, and unpublish.
|
17
|
+
|
18
|
+
### Post
|
19
|
+
|
20
|
+
poole post [OPTIONS] TITLE
|
21
|
+
|
22
|
+
Generates a timestamped post in your `_posts` directory, with the format
|
23
|
+
`YYYY-MM-DD-slug.md` (other formats to be suppored in the future). With no
|
24
|
+
options, will generate a slug based on your title by replacing spaces with
|
25
|
+
underscores, downcasing, and removing any special character. With option
|
26
|
+
`--slug` (or `-s`), you can provide a custom slug.
|
27
|
+
|
28
|
+
Poole generates a simple file (in the future, this will be customizable) that
|
29
|
+
looks like this:
|
30
|
+
|
31
|
+
```yaml
|
32
|
+
---
|
33
|
+
title: (your title automatically inserted here)
|
34
|
+
layout: post
|
35
|
+
date: (current date automatically inserted here)
|
36
|
+
---
|
37
|
+
```
|
38
|
+
|
39
|
+
### Draft
|
40
|
+
|
41
|
+
poole draft [OPTIONS] TITLE
|
42
|
+
|
43
|
+
Just like `poole post`, except that it creates an untimestamped post in your
|
44
|
+
`_drafts` directory (creating it if it doesn't exist yet). Also takes
|
45
|
+
`--slug`/`-s` as an option. In the generated file, no date is inserted.
|
46
|
+
|
47
|
+
### Publish
|
48
|
+
|
49
|
+
poole publish DRAFT_PATH
|
50
|
+
|
51
|
+
Publishes a draft from your _drafts folder to your _posts folder, renaming the
|
52
|
+
file and updating the date in the header.
|
53
|
+
|
54
|
+
Given this file (called `_drafts/test_draft.md`):
|
55
|
+
|
56
|
+
```
|
57
|
+
---
|
58
|
+
title: My awesome blog post
|
59
|
+
layout: post
|
60
|
+
date:
|
61
|
+
---
|
62
|
+
|
63
|
+
The life, universe, and everything.
|
64
|
+
```
|
65
|
+
|
66
|
+
A call to `poole publish` will generate a file named
|
67
|
+
`_posts/yyyy-mm-dd-test_draft.md` and delete the draft. (TODO: add flags for
|
68
|
+
no-delete drafts, and no-update timstamp.) Also updates the date filed in the
|
69
|
+
header with a date, and HH:MM, producing this file:
|
70
|
+
|
71
|
+
```
|
72
|
+
---
|
73
|
+
title: My awesome blog post
|
74
|
+
layout: post
|
75
|
+
date: 2010-01-02 16:00
|
76
|
+
---
|
77
|
+
|
78
|
+
The life, universe, and everything.
|
79
|
+
```
|
80
|
+
|
81
|
+
### Unpublish
|
82
|
+
|
83
|
+
poole unpublish POST_PATH
|
84
|
+
|
85
|
+
The reverse of publish: moves a file from your _posts folder to the _drafts
|
86
|
+
folder, renaming the file and removing the date in the header. This will
|
87
|
+
rename a file called `_posts/yyyy-mm-dd-test_post.md` to
|
88
|
+
`_drafts/test_post.md`. (TODO: add flags for no-delete post, no-update
|
89
|
+
timestamp, and custom slug for unpublished draft (?))
|
90
|
+
|
91
|
+
|
92
|
+
### Script usage
|
93
|
+
|
94
|
+
The actual work is done in `MrPoole::Commands`: calls into that class return
|
95
|
+
the path name for newly created files, so you can do something useful with
|
96
|
+
them if you want to. This should get better in the future.
|
97
|
+
|
98
|
+
|
99
|
+
## To do
|
100
|
+
|
101
|
+
- Configuration: custom templates, hooking into jekyll's `_config.yml`
|
102
|
+
- Support for multiple output formats (right now, only markdown is supported)
|
103
|
+
- Better option handling (allow custom templates, more flexible date
|
104
|
+
substitution)
|
105
|
+
- Better documentation (this is an open source project, after all)
|
106
|
+
|
107
|
+
## Installation
|
108
|
+
|
109
|
+
Add this line to your application's Gemfile:
|
110
|
+
|
111
|
+
gem 'mr_poole'
|
112
|
+
|
113
|
+
And then execute:
|
114
|
+
|
115
|
+
$ bundle
|
116
|
+
|
117
|
+
Or install it yourself as:
|
118
|
+
|
119
|
+
$ gem install mr_poole
|
120
|
+
|
121
|
+
## Contact
|
122
|
+
|
123
|
+
Contact me on Github, at michael@mcclimon.org, or on twitter, @mmcclimon.
|
124
|
+
|
125
|
+
## Contributing
|
126
|
+
|
127
|
+
1. Fork it
|
128
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
129
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
130
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
131
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/poole
ADDED
data/lib/mr_poole/cli.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module MrPoole
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@helper = Helper.new
|
9
|
+
@helper.ensure_jekyll_dir
|
10
|
+
|
11
|
+
@params = args
|
12
|
+
@commands = Commands.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(action)
|
16
|
+
case action
|
17
|
+
when 'post' then handle_post
|
18
|
+
when 'draft' then handle_draft
|
19
|
+
when 'publish' then handle_publish
|
20
|
+
when 'unpublish' then handle_unpublish
|
21
|
+
else @helper.gen_usage
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_post
|
26
|
+
options = do_creation_options
|
27
|
+
options.title ||= @params.first
|
28
|
+
|
29
|
+
@helper.post_usage unless options.title
|
30
|
+
@commands.post(options.title, options.slug)
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_draft
|
34
|
+
options = do_creation_options
|
35
|
+
options.title ||= @params.first
|
36
|
+
|
37
|
+
@helper.draft_usage unless options.title
|
38
|
+
@commands.draft(options.title, options.slug)
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_publish
|
42
|
+
options = OpenStruct.new
|
43
|
+
opt_parser = OptionParser.new do |opts|
|
44
|
+
# eventually there will be options...not yet
|
45
|
+
end
|
46
|
+
opt_parser.parse! @params
|
47
|
+
|
48
|
+
path = @params.first
|
49
|
+
@helper.publish_usage unless path
|
50
|
+
@commands.publish(path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_unpublish
|
54
|
+
options = OpenStruct.new
|
55
|
+
opt_parser = OptionParser.new do |opts|
|
56
|
+
# eventually there will be options...not yet
|
57
|
+
end
|
58
|
+
opt_parser.parse! @params
|
59
|
+
|
60
|
+
path = @params.first
|
61
|
+
@helper.unpublish_usage unless path
|
62
|
+
@commands.unpublish(path)
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_creation_options
|
66
|
+
options = OpenStruct.new
|
67
|
+
options.slug = nil
|
68
|
+
options.title = nil
|
69
|
+
|
70
|
+
opt_parser = OptionParser.new do |opts|
|
71
|
+
opts.on('-s', '--slug [SLUG]', "Use custom slug") do |s|
|
72
|
+
options.slug = s
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on('-t', '--title [TITLE]', "Specifiy title") do |t|
|
76
|
+
options.title = t
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
opt_parser.parse! @params
|
81
|
+
options
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module MrPoole
|
5
|
+
class Commands
|
6
|
+
|
7
|
+
POSTS_FOLDER = '_posts'
|
8
|
+
DRAFTS_FOLDER = '_drafts'
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@helper = Helper.new
|
12
|
+
@default_layout = @helper.get_default_layout
|
13
|
+
end
|
14
|
+
|
15
|
+
# Generate a timestamped post
|
16
|
+
def post(title, slug='')
|
17
|
+
date = @helper.get_date_stamp
|
18
|
+
|
19
|
+
# still want to escape any garbage in the slug
|
20
|
+
slug = title if slug.nil? || slug.empty?
|
21
|
+
slug = @helper.get_slug_for(slug)
|
22
|
+
|
23
|
+
# put the metadata into the layout header
|
24
|
+
head = @default_layout
|
25
|
+
head.sub!(/^title:\s*$/, "title: #{title}")
|
26
|
+
head.sub!(/^date:\s*$/, "date: #{date}")
|
27
|
+
|
28
|
+
path = File.join(POSTS_FOLDER, "#{date}-#{slug}.md")
|
29
|
+
f = File.open(path, "w")
|
30
|
+
f.write(head)
|
31
|
+
f.close
|
32
|
+
|
33
|
+
path # return the path, in case we want to do anything useful
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate a non-timestamped draft
|
37
|
+
def draft(title, slug='')
|
38
|
+
# the drafts folder might not exist yet...create it just in case
|
39
|
+
FileUtils.mkdir_p(DRAFTS_FOLDER)
|
40
|
+
|
41
|
+
slug = title if slug.nil? || slug.empty?
|
42
|
+
slug = @helper.get_slug_for(slug)
|
43
|
+
|
44
|
+
head = @default_layout
|
45
|
+
head.sub!(/^title:\s*$/, "title: #{title}")
|
46
|
+
|
47
|
+
path = File.join(DRAFTS_FOLDER, "#{slug}.md")
|
48
|
+
f = File.open(path, "w")
|
49
|
+
f.write(head)
|
50
|
+
f.close
|
51
|
+
|
52
|
+
path # return the path, in case we want to do anything useful
|
53
|
+
end
|
54
|
+
|
55
|
+
# Todo make this take a path instead?
|
56
|
+
def publish(draftpath)
|
57
|
+
slug = File.basename(draftpath, '.md')
|
58
|
+
|
59
|
+
begin
|
60
|
+
infile = File.open(draftpath, "r")
|
61
|
+
rescue Errno::ENOENT
|
62
|
+
@helper.bad_path(draftpath)
|
63
|
+
end
|
64
|
+
|
65
|
+
date = @helper.get_date_stamp
|
66
|
+
time = @helper.get_time_stamp
|
67
|
+
|
68
|
+
outpath = File.join(POSTS_FOLDER, "#{date}-#{slug}.md")
|
69
|
+
outfile = File.open(outpath, "w")
|
70
|
+
|
71
|
+
infile.each_line do |line|
|
72
|
+
l = line.sub(/^date:\s*$/, "date: #{date} #{time}\n")
|
73
|
+
outfile.write(l)
|
74
|
+
end
|
75
|
+
|
76
|
+
infile.close
|
77
|
+
outfile.close
|
78
|
+
FileUtils.rm(draftpath)
|
79
|
+
|
80
|
+
outpath
|
81
|
+
end
|
82
|
+
|
83
|
+
def unpublish(inpath)
|
84
|
+
# the drafts folder might not exist yet...create it just in case
|
85
|
+
FileUtils.mkdir_p(DRAFTS_FOLDER)
|
86
|
+
|
87
|
+
begin
|
88
|
+
infile = File.open(inpath, "r")
|
89
|
+
rescue Errno::ENOENT
|
90
|
+
@helper.bad_path(inpath)
|
91
|
+
end
|
92
|
+
|
93
|
+
slug = inpath.sub(/.*?\d{4}-\d{2}-\d{2}-(.*)/, '\1')
|
94
|
+
outpath = File.join(DRAFTS_FOLDER, slug)
|
95
|
+
outfile = File.open(outpath, "w")
|
96
|
+
|
97
|
+
infile.each_line do |line|
|
98
|
+
l = line.sub(/^date:\s*.*$/, "date:")
|
99
|
+
outfile.write(l)
|
100
|
+
end
|
101
|
+
|
102
|
+
infile.close
|
103
|
+
outfile.close
|
104
|
+
FileUtils.rm(inpath)
|
105
|
+
|
106
|
+
outpath
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module MrPoole
|
2
|
+
class Helper
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
# nothing to do here
|
6
|
+
end
|
7
|
+
|
8
|
+
# Check for a _posts directory in current directory
|
9
|
+
# If we don't find one, puke an error message and die
|
10
|
+
def ensure_jekyll_dir
|
11
|
+
unless Dir.exists?('./_posts')
|
12
|
+
puts 'ERROR: Cannot locate _posts directory. Double check to make sure'
|
13
|
+
puts ' that you are in a jekyll directory.'
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Configure the default layout.
|
19
|
+
#
|
20
|
+
# If a user has $HOME/.poole_default_layout, will use the contents of
|
21
|
+
# that file, otherwise will use a simple template
|
22
|
+
def get_default_layout
|
23
|
+
config_path = File.join(Dir.home, '.poole_default_layout')
|
24
|
+
|
25
|
+
if File.exists?(config_path)
|
26
|
+
return File.open(config_path, 'r').read
|
27
|
+
else
|
28
|
+
s = "---\n"
|
29
|
+
s << "title:\n"
|
30
|
+
s << "layout: post\n"
|
31
|
+
s << "date:\n"
|
32
|
+
s << "---\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Given a post title (mixed case, spaces, etc.), generates a slug for
|
37
|
+
# This clobbers any non-ASCII text (TODO don't do that)
|
38
|
+
def get_slug_for(title)
|
39
|
+
title.downcase.gsub(/[^a-z0-9_\s-]/, '').gsub(/\s+/, '_')
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_date_stamp
|
43
|
+
Time.now.strftime("%Y-%m-%d")
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_time_stamp
|
47
|
+
Time.now.strftime("%H:%M")
|
48
|
+
end
|
49
|
+
|
50
|
+
def bad_path(path)
|
51
|
+
puts "Error: could not open #{path}"
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
|
55
|
+
# Print a usage message and exit
|
56
|
+
def gen_usage
|
57
|
+
puts 'Usage:'
|
58
|
+
puts ' poole [ACTION] [ARG]'
|
59
|
+
puts ''
|
60
|
+
puts 'Actions:'
|
61
|
+
puts ' draft Create a new draft in _drafts with title SLUG'
|
62
|
+
puts ' post Create a new timestamped post in _posts with title SLUG'
|
63
|
+
puts ' publish Publish the draft with SLUG, timestamping appropriately'
|
64
|
+
puts ' unpublish Move a post to _drafts, untimestamping appropriately'
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
|
68
|
+
def post_usage
|
69
|
+
puts 'Usage:'
|
70
|
+
puts ' poole post [OPTION] [ARG] TITLE'
|
71
|
+
puts ''
|
72
|
+
puts 'Options:'
|
73
|
+
puts ' --slug Define a custom slug for post, used for generated file name'
|
74
|
+
puts ' (also available with -s)'
|
75
|
+
puts ' --title Define a title for post (also available with -t)'
|
76
|
+
puts ' This option may be omitted provided that TITLE is given as'
|
77
|
+
puts ' the last argument to poole'
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
def draft_usage
|
82
|
+
puts 'Usage:'
|
83
|
+
puts ' poole draft [OPTION] [ARG] TITLE'
|
84
|
+
puts ''
|
85
|
+
puts 'Options:'
|
86
|
+
puts ' --slug Define a custom slug for post, used for generated file name'
|
87
|
+
puts ' (also available with -s)'
|
88
|
+
puts ' --title Define a title for post (also available with -t)'
|
89
|
+
puts ' This option may be omitted provided that TITLE is given as'
|
90
|
+
puts ' the last argument to poole'
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
|
94
|
+
def publish_usage
|
95
|
+
puts 'Usage:'
|
96
|
+
puts ' poole publish PATH_TO_DRAFT'
|
97
|
+
puts ''
|
98
|
+
puts 'Options:'
|
99
|
+
puts ' (coming soon)'
|
100
|
+
exit
|
101
|
+
end
|
102
|
+
|
103
|
+
def unpublish_usage
|
104
|
+
puts 'Usage:'
|
105
|
+
puts ' poole unpublish PATH_TO_POST'
|
106
|
+
puts ''
|
107
|
+
puts 'Options:'
|
108
|
+
puts ' (coming soon)'
|
109
|
+
exit
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
data/lib/mr_poole.rb
ADDED
data/mr_poole.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mr_poole/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mr_poole"
|
8
|
+
spec.version = MrPoole::VERSION
|
9
|
+
spec.authors = ["Michael McClimon"]
|
10
|
+
spec.email = ["michael@mcclimon.org"]
|
11
|
+
spec.description = %q{A butler for Jekyll, provides interface for creating posts/drafts}
|
12
|
+
spec.summary = <<-EOF
|
13
|
+
A butler for Jekyll. Provides a command-line interface (called `poole`) for
|
14
|
+
creating and publishing posts and drafts for Jekyll (http://jekyllrb.com)
|
15
|
+
blogs.
|
16
|
+
|
17
|
+
The literary Mr. Poole is Jekyll's butler, who "serves Jekyll faithfully, and
|
18
|
+
attempts to do a good job and be loyal to his master"
|
19
|
+
(http://en.wikipedia.org/wiki/Jekyll_and_hyde#Mr._Poole), and the
|
20
|
+
Mr. Poole gem looks to be the same thing.
|
21
|
+
EOF
|
22
|
+
spec.homepage = "http://github.com/mmcclimon/mr_poole"
|
23
|
+
spec.license = "MIT"
|
24
|
+
|
25
|
+
spec.files = `git ls-files`.split($/)
|
26
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
27
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
31
|
+
spec.add_development_dependency "rspec"
|
32
|
+
spec.add_development_dependency "rake"
|
33
|
+
end
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'mr_poole'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
module MrPoole
|
8
|
+
describe CLI do
|
9
|
+
|
10
|
+
context 'should determine jekyll dir correctly' do
|
11
|
+
|
12
|
+
it 'should exit with no _posts directory' do
|
13
|
+
olddir, tmpdir = make_no_jekyll_dir
|
14
|
+
|
15
|
+
argv = []
|
16
|
+
output = capture_stdout do
|
17
|
+
begin
|
18
|
+
cli = CLI.new(argv)
|
19
|
+
rescue SystemExit => e
|
20
|
+
e.should be_instance_of(SystemExit)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
clean_tmp_files(tmpdir, olddir)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should not exit with _posts directory' do
|
28
|
+
olddir, tmpdir = make_jekyll_dir
|
29
|
+
|
30
|
+
argv = []
|
31
|
+
lambda { cli = CLI.new(argv) }.should_not raise_error
|
32
|
+
|
33
|
+
clean_tmp_files(tmpdir, olddir)
|
34
|
+
end
|
35
|
+
|
36
|
+
end # end context determine jekyll dir
|
37
|
+
|
38
|
+
describe "action 'post'" do
|
39
|
+
|
40
|
+
before :each do
|
41
|
+
@olddir, @tmpdir = make_jekyll_dir
|
42
|
+
end
|
43
|
+
|
44
|
+
after :each do
|
45
|
+
clean_tmp_files(@tmpdir, @olddir)
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'error handling' do
|
49
|
+
|
50
|
+
it 'should fail with no arguments' do
|
51
|
+
argv = ['post']
|
52
|
+
|
53
|
+
expect {
|
54
|
+
poole_with_args_no_stdout(argv).call
|
55
|
+
}.to raise_error(SystemExit)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should fail with no title (with slug)' do
|
59
|
+
argv = ['post', '-s', 'post_slug']
|
60
|
+
|
61
|
+
expect {
|
62
|
+
poole_with_args_no_stdout(argv).call
|
63
|
+
}.to raise_error(SystemExit)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should not fail with a title (no switch)' do
|
67
|
+
argv = ['post', 'Here is a title']
|
68
|
+
|
69
|
+
expect {
|
70
|
+
poole_with_args_no_stdout(argv).call
|
71
|
+
}.not_to raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should not fail with a title (long switch)' do
|
75
|
+
argv = ['post', '--title', 'Here is a title']
|
76
|
+
|
77
|
+
expect {
|
78
|
+
poole_with_args_no_stdout(argv).call
|
79
|
+
}.not_to raise_error
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should not fail with a title (short switch)' do
|
83
|
+
argv = ['post', '-t', 'Here is a title']
|
84
|
+
|
85
|
+
expect {
|
86
|
+
poole_with_args_no_stdout(argv).call
|
87
|
+
}.not_to raise_error
|
88
|
+
end
|
89
|
+
|
90
|
+
end # context error handling
|
91
|
+
|
92
|
+
context 'exit message' do
|
93
|
+
|
94
|
+
it 'should exit with a usage message' do
|
95
|
+
argv = ['post']
|
96
|
+
|
97
|
+
output = capture_stdout do
|
98
|
+
begin
|
99
|
+
poole_with_args(argv).call
|
100
|
+
rescue SystemExit => e
|
101
|
+
# this will fail, but we want the exit message
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
output.should match(/Usage:\s+poole post/)
|
106
|
+
end
|
107
|
+
|
108
|
+
end # context exit message
|
109
|
+
|
110
|
+
end # end describe post
|
111
|
+
|
112
|
+
describe "action 'draft'" do
|
113
|
+
|
114
|
+
before :each do
|
115
|
+
@olddir, @tmpdir = make_jekyll_dir
|
116
|
+
end
|
117
|
+
|
118
|
+
after :each do
|
119
|
+
clean_tmp_files(@tmpdir, @olddir)
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'error handling' do
|
123
|
+
|
124
|
+
it 'should fail with no arguments' do
|
125
|
+
argv = ['draft']
|
126
|
+
|
127
|
+
expect {
|
128
|
+
poole_with_args_no_stdout(argv).call
|
129
|
+
}.to raise_error(SystemExit)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should fail with no title (with slug)' do
|
133
|
+
argv = ['draft', '-s', 'draft_slug']
|
134
|
+
|
135
|
+
expect {
|
136
|
+
poole_with_args_no_stdout(argv).call
|
137
|
+
}.to raise_error(SystemExit)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should not fail with a title (no switch)' do
|
141
|
+
argv = ['draft', 'Here is a title']
|
142
|
+
|
143
|
+
expect {
|
144
|
+
poole_with_args_no_stdout(argv).call
|
145
|
+
}.not_to raise_error
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should not fail with a title (long switch)' do
|
149
|
+
argv = ['draft', '--title', 'Here is a title']
|
150
|
+
|
151
|
+
expect {
|
152
|
+
poole_with_args_no_stdout(argv).call
|
153
|
+
}.not_to raise_error
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should not fail with a title (short switch)' do
|
157
|
+
argv = ['draft', '-t', 'Here is a title']
|
158
|
+
|
159
|
+
expect {
|
160
|
+
poole_with_args_no_stdout(argv).call
|
161
|
+
}.not_to raise_error
|
162
|
+
end
|
163
|
+
|
164
|
+
end # context error handling
|
165
|
+
|
166
|
+
context 'exit message' do
|
167
|
+
|
168
|
+
it 'should exit with a usage message' do
|
169
|
+
argv = ['draft']
|
170
|
+
|
171
|
+
output = capture_stdout do
|
172
|
+
begin
|
173
|
+
poole_with_args(argv).call
|
174
|
+
rescue SystemExit
|
175
|
+
# this will fail, but we want the exit message
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
output.should match(/Usage:\s+poole draft/)
|
180
|
+
end
|
181
|
+
|
182
|
+
end # context exit message
|
183
|
+
|
184
|
+
end # end describe draft
|
185
|
+
|
186
|
+
describe "action 'publish'" do
|
187
|
+
before :each do
|
188
|
+
@olddir, @tmpdir = make_jekyll_dir
|
189
|
+
@c = Commands.new
|
190
|
+
@d_path = @c.draft('test_draft')
|
191
|
+
end
|
192
|
+
|
193
|
+
after :each do
|
194
|
+
clean_tmp_files(@tmpdir, @olddir)
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'error handling' do
|
198
|
+
|
199
|
+
it 'should fail with no arguments' do
|
200
|
+
argv = ['publish']
|
201
|
+
|
202
|
+
expect {
|
203
|
+
poole_with_args_no_stdout(argv).call
|
204
|
+
}.to raise_error(SystemExit)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should fail with a bad path' do
|
208
|
+
argv = ['publish', '_drafts/does_not_exist.md']
|
209
|
+
|
210
|
+
expect {
|
211
|
+
poole_with_args_no_stdout(argv).call
|
212
|
+
}.to raise_error(SystemExit)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should not fail with a good path' do
|
216
|
+
argv = ['publish', @d_path]
|
217
|
+
|
218
|
+
expect {
|
219
|
+
poole_with_args_no_stdout(argv).call
|
220
|
+
}.not_to raise_error
|
221
|
+
end
|
222
|
+
|
223
|
+
end # context error handling
|
224
|
+
|
225
|
+
context 'exit message' do
|
226
|
+
|
227
|
+
it 'should exit with usage with no arguments' do
|
228
|
+
argv = ['publish']
|
229
|
+
|
230
|
+
output = capture_stdout do
|
231
|
+
begin
|
232
|
+
poole_with_args(argv).call
|
233
|
+
rescue SystemExit
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
output.should match(/Usage:\s+poole publish/)
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should exit with a description of bad path' do
|
241
|
+
argv = ['publish', '_drafts/does_not_exist.md']
|
242
|
+
|
243
|
+
output = capture_stdout do
|
244
|
+
begin
|
245
|
+
poole_with_args(argv).call
|
246
|
+
rescue SystemExit
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
output.should match(/Error:\s+could not open/)
|
251
|
+
end
|
252
|
+
end # context exit message
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "action 'unpublish'" do
|
257
|
+
before :each do
|
258
|
+
@olddir, @tmpdir = make_jekyll_dir
|
259
|
+
@c = Commands.new
|
260
|
+
@p_path = @c.post('test_post')
|
261
|
+
end
|
262
|
+
|
263
|
+
after :each do
|
264
|
+
clean_tmp_files(@tmpdir, @olddir)
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'error handling' do
|
268
|
+
|
269
|
+
it 'should fail with no arguments' do
|
270
|
+
argv = ['unpublish']
|
271
|
+
|
272
|
+
expect {
|
273
|
+
poole_with_args_no_stdout(argv).call
|
274
|
+
}.to raise_error(SystemExit)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'should fail with a bad path' do
|
278
|
+
argv = ['unpublish', '_posts/does_not_exist.md']
|
279
|
+
|
280
|
+
expect {
|
281
|
+
poole_with_args_no_stdout(argv).call
|
282
|
+
}.to raise_error(SystemExit)
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'should not fail with a good path' do
|
286
|
+
argv = ['unpublish', @p_path]
|
287
|
+
|
288
|
+
expect {
|
289
|
+
poole_with_args_no_stdout(argv).call
|
290
|
+
}.not_to raise_error
|
291
|
+
end
|
292
|
+
|
293
|
+
end # context error handling
|
294
|
+
|
295
|
+
context 'exit message' do
|
296
|
+
|
297
|
+
it 'should exit with usage with no arguments' do
|
298
|
+
argv = ['unpublish']
|
299
|
+
|
300
|
+
output = capture_stdout do
|
301
|
+
begin
|
302
|
+
poole_with_args(argv).call
|
303
|
+
rescue SystemExit
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
output.should match(/Usage:\s+poole unpublish/)
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'should exit with a description of bad path' do
|
311
|
+
argv = ['unpublish', '_posts/does_not_exist.md']
|
312
|
+
|
313
|
+
output = capture_stdout do
|
314
|
+
begin
|
315
|
+
poole_with_args(argv).call
|
316
|
+
rescue SystemExit
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
output.should match(/Error:\s+could not open/)
|
321
|
+
end
|
322
|
+
end # context exit message
|
323
|
+
|
324
|
+
|
325
|
+
end # action unpublish
|
326
|
+
|
327
|
+
end
|
328
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'mr_poole'
|
4
|
+
|
5
|
+
module MrPoole
|
6
|
+
describe Commands do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@date_regex = %r{\d{4}-\d{2}-\d{2}}
|
10
|
+
end
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
@c = Commands.new
|
14
|
+
@olddir, @tmpdir = make_jekyll_dir
|
15
|
+
end
|
16
|
+
|
17
|
+
after :each do
|
18
|
+
clean_tmp_files(@tmpdir, @olddir)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#post" do
|
22
|
+
context 'title only' do
|
23
|
+
|
24
|
+
it "should create a new post in the _posts directory" do
|
25
|
+
@c.post("test_post")
|
26
|
+
Dir.glob("_posts/*.md").length.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create a timestamped post in the _posts directory" do
|
30
|
+
@c.post("test_post")
|
31
|
+
fn = Dir.glob("_posts/*.md").first
|
32
|
+
fn.should match(/#{@date_regex}-test_post[.]md$/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return path to the newly created post" do
|
36
|
+
returned = @c.post("test_post")
|
37
|
+
determined = Dir.glob("_posts/*.md").first
|
38
|
+
returned.should == determined
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should downcase a title" do
|
42
|
+
@c.post("Test_Post_With_Uppercase")
|
43
|
+
fn = Dir.glob("_posts/*.md").first
|
44
|
+
fn.should match(/#{@date_regex}-test_post_with_uppercase[.]md/)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should sub underscores for spaces in title" do
|
48
|
+
@c.post("Test Post with Spaces")
|
49
|
+
fn = Dir.glob("_posts/*.md").first
|
50
|
+
fn.should match(/#{@date_regex}-test_post_with_spaces[.]md/)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should remove non-word characters for slug" do
|
54
|
+
@c.post("On (function() {}()) in JavaScript")
|
55
|
+
fn = Dir.glob("_posts/*.md").first
|
56
|
+
fn.should match(/#{@date_regex}-on_function_in_javascript[.]md/)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should update the title in the file itself" do
|
60
|
+
@c.post("Testing Post {}")
|
61
|
+
fn = Dir.glob("_posts/*.md").first
|
62
|
+
content = File.open(fn, 'r').read
|
63
|
+
content.should match(/title: Testing Post {}/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should update the date in the file itself" do
|
67
|
+
@c.post("Date test post")
|
68
|
+
fn = Dir.glob("_posts/*.md").first
|
69
|
+
|
70
|
+
# date in filename should match date in file itself
|
71
|
+
date = fn.match(/(#{@date_regex})-date_test_post[.]md/)[1]
|
72
|
+
content = File.open(fn, 'r').read
|
73
|
+
content.should match(/date: #{date}/)
|
74
|
+
end
|
75
|
+
|
76
|
+
end # end context title only
|
77
|
+
|
78
|
+
context 'title and slug' do
|
79
|
+
|
80
|
+
it "should create a post named for slug" do
|
81
|
+
@c.post("Test Post", 'unique_slug')
|
82
|
+
fn = Dir.glob("_posts/*.md").first
|
83
|
+
fn.should match(/#{@date_regex}-unique_slug[.]md$/)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should sub any weird characters in slug" do
|
87
|
+
@c.post("Test Post with Spaces", "(stupid] {slüg/")
|
88
|
+
fn = Dir.glob("_posts/*.md").first
|
89
|
+
fn.should match(/#{@date_regex}-stupid_slg[.]md/)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should update the title in the file itself" do
|
93
|
+
@c.post("Testing Post {}", 'shouldnt_be_in_title')
|
94
|
+
fn = Dir.glob("_posts/*.md").first
|
95
|
+
content = File.open(fn, 'r').read
|
96
|
+
content.should match(/title: Testing Post {}/)
|
97
|
+
end
|
98
|
+
|
99
|
+
end # end context title & slug
|
100
|
+
|
101
|
+
end # end describe post
|
102
|
+
|
103
|
+
describe "#draft" do
|
104
|
+
context 'title only' do
|
105
|
+
|
106
|
+
it "should create a _drafts directory" do
|
107
|
+
@c.draft('draft post')
|
108
|
+
Dir.exists?('_drafts').should be_true
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should create a new draft in the _drafts directory" do
|
112
|
+
@c.draft('draft post')
|
113
|
+
Dir.glob("_drafts/*.md").length.should == 1
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should return path to the newly created draft" do
|
117
|
+
returned = @c.draft("test_draft")
|
118
|
+
determined = Dir.glob("_drafts/*.md").first
|
119
|
+
returned.should == determined
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should create a non-timestamped draft" do
|
123
|
+
@c.draft('draft post')
|
124
|
+
fn = Dir.glob("_drafts/*.md").first
|
125
|
+
fn.should_not match(/#{@date_regex}/)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should downcase and underscore title for slug" do
|
129
|
+
@c.draft("Test Post with Spaces")
|
130
|
+
fn = Dir.glob("_drafts/*.md").first
|
131
|
+
fn.should match(/test_post_with_spaces[.]md/)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should remove non-word characters for slug" do
|
135
|
+
@c.draft("On (function() {}()) in JavaScript")
|
136
|
+
fn = Dir.glob("_drafts/*.md").first
|
137
|
+
fn.should match(/on_function_in_javascript[.]md/)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should update the title in the file itself" do
|
141
|
+
@c.draft("Testing Draft {}")
|
142
|
+
fn = Dir.glob("_drafts/*.md").first
|
143
|
+
content = File.open(fn, 'r').read
|
144
|
+
content.should match(/title: Testing Draft {}/)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should not update the date in the file itself" do
|
148
|
+
@c.draft("Date test post")
|
149
|
+
fn = Dir.glob("_drafts/*.md").first
|
150
|
+
|
151
|
+
# date in filename should match date in file itself
|
152
|
+
content = File.open(fn, 'r').read
|
153
|
+
content.should match(/date:\s*\n/)
|
154
|
+
end
|
155
|
+
|
156
|
+
end # end context title only
|
157
|
+
|
158
|
+
context 'title and slug' do
|
159
|
+
|
160
|
+
it "should create a draft named for slug" do
|
161
|
+
@c.draft("Test Draft", 'unique_slug')
|
162
|
+
fn = Dir.glob("_drafts/*.md").first
|
163
|
+
fn.should match(/unique_slug[.]md$/)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should sub any weird characters in slug" do
|
167
|
+
@c.draft("Test Post with Spaces", "(stupid] {slüg/")
|
168
|
+
fn = Dir.glob("_drafts/*.md").first
|
169
|
+
fn.should match(/stupid_slg[.]md/)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should update the title in the file itself" do
|
173
|
+
@c.draft("Testing Post {}", 'shouldnt_be_in_title')
|
174
|
+
fn = Dir.glob("_drafts/*.md").first
|
175
|
+
content = File.open(fn, 'r').read
|
176
|
+
content.should match(/title: Testing Post {}/)
|
177
|
+
end
|
178
|
+
|
179
|
+
end # end context title & slug
|
180
|
+
|
181
|
+
end # end describe draft
|
182
|
+
|
183
|
+
describe "#publish" do
|
184
|
+
|
185
|
+
before :each do
|
186
|
+
@d_path = @c.draft('test_draft')
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should create a timestamped post in the _posts folder' do
|
190
|
+
@c.publish(@d_path)
|
191
|
+
fn = Dir.glob("_posts/*.md").first
|
192
|
+
fn.should match(/#{@date_regex}-test_draft[.]md$/)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should remove file in the _drafts folder' do
|
196
|
+
@c.publish(@d_path)
|
197
|
+
File.exist?(@d_path).should be_false
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should return path to newly created post' do
|
201
|
+
returned = @c.publish(@d_path)
|
202
|
+
determined = Dir.glob("_posts/*.md").first
|
203
|
+
returned.should == determined
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should create post with matching slug' do
|
207
|
+
post = @c.publish(@d_path)
|
208
|
+
|
209
|
+
draft_slug = File.basename(@d_path, '.md')
|
210
|
+
post_slug = post.match(/#{@date_regex}-(.*)[.]md/)[1]
|
211
|
+
|
212
|
+
post_slug.should == draft_slug
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should update timestamp in actual file' do
|
216
|
+
post = @c.publish(@d_path)
|
217
|
+
content = File.open(post, 'r').read
|
218
|
+
content.should match(/date: #{@date_regex} \d{2}:\d{2}\n/)
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should copy contents of draft into post' do
|
222
|
+
# first add some content to the draft
|
223
|
+
f = File.open(@d_path, 'a')
|
224
|
+
f.write("Some new content for my blog\n")
|
225
|
+
f.close
|
226
|
+
|
227
|
+
post = @c.publish(@d_path)
|
228
|
+
content = File.open(post, 'r').read
|
229
|
+
content.should match(/Some new content for my blog/)
|
230
|
+
end
|
231
|
+
|
232
|
+
end # end describe publish
|
233
|
+
|
234
|
+
describe "#unpublish" do
|
235
|
+
|
236
|
+
before :each do
|
237
|
+
@p_path = @c.post('test_post')
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should create a _drafts directory' do
|
241
|
+
@c.unpublish(@p_path)
|
242
|
+
Dir.exists?('_drafts').should be_true
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should create an untimestamped draft in the _drafts folder' do
|
246
|
+
@c.unpublish(@p_path)
|
247
|
+
fn = Dir.glob("_drafts/*.md").first
|
248
|
+
fn.should_not match(/#{@date_regex}/)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'should remove file in the _posts folder' do
|
252
|
+
@c.unpublish(@p_path)
|
253
|
+
File.exist?(@p_path).should be_false
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should return path to newly created draft' do
|
257
|
+
returned = @c.unpublish(@p_path)
|
258
|
+
determined = Dir.glob("_drafts/*.md").first
|
259
|
+
returned.should == determined
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should create draft with matching slug' do
|
263
|
+
draft = @c.unpublish(@p_path)
|
264
|
+
|
265
|
+
post_slug = @p_path.match(/#{@date_regex}-(.*)[.]md$/)[1]
|
266
|
+
draft_slug = File.basename(draft, '.md')
|
267
|
+
|
268
|
+
draft_slug.should == post_slug
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should delete timestamp in actual file' do
|
272
|
+
draft = @c.unpublish(@p_path)
|
273
|
+
content = File.open(draft, 'r').read
|
274
|
+
content.should match(/date:\s*\n/)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'should copy contents of post into draft' do
|
278
|
+
# first add some content to the draft
|
279
|
+
f = File.open(@p_path, 'a')
|
280
|
+
f.write("Some new content for my blog\n")
|
281
|
+
f.close
|
282
|
+
|
283
|
+
draft = @c.unpublish(@p_path)
|
284
|
+
content = File.open(draft, 'r').read
|
285
|
+
content.should match(/Some new content for my blog/)
|
286
|
+
end
|
287
|
+
|
288
|
+
end # end describe unpublish
|
289
|
+
end
|
290
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
def capture_stdout(&block)
|
8
|
+
stdout = $stdout
|
9
|
+
fake_out = StringIO.new
|
10
|
+
$stdout = fake_out
|
11
|
+
begin
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
$stdout = stdout
|
15
|
+
end
|
16
|
+
fake_out.string
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_no_jekyll_dir
|
20
|
+
olddir = Dir.pwd()
|
21
|
+
newdir = Dir.mktmpdir('nojekyll')
|
22
|
+
Dir.chdir(newdir)
|
23
|
+
return olddir, newdir
|
24
|
+
end
|
25
|
+
|
26
|
+
def make_jekyll_dir
|
27
|
+
olddir = Dir.pwd()
|
28
|
+
newdir = Dir.mktmpdir('jekyll')
|
29
|
+
posts = File.join(newdir, '_posts')
|
30
|
+
Dir.mkdir(posts)
|
31
|
+
Dir.chdir(newdir)
|
32
|
+
return olddir, newdir
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def clean_tmp_files(tmpdir, restoredir)
|
37
|
+
Dir.chdir(restoredir)
|
38
|
+
FileUtils.rm_rf(tmpdir)
|
39
|
+
end
|
40
|
+
|
41
|
+
def poole_with_args(argv)
|
42
|
+
return Proc.new do
|
43
|
+
action = argv.shift
|
44
|
+
cli = MrPoole::CLI.new(argv)
|
45
|
+
cli.execute(action)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def poole_with_args_no_stdout(argv)
|
50
|
+
return Proc.new do
|
51
|
+
capture_stdout do
|
52
|
+
action = argv.shift
|
53
|
+
cli = MrPoole::CLI.new(argv)
|
54
|
+
cli.execute(action)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mr_poole
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael McClimon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A butler for Jekyll, provides interface for creating posts/drafts
|
56
|
+
email:
|
57
|
+
- michael@mcclimon.org
|
58
|
+
executables:
|
59
|
+
- poole
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/poole
|
69
|
+
- lib/mr_poole.rb
|
70
|
+
- lib/mr_poole/cli.rb
|
71
|
+
- lib/mr_poole/commands.rb
|
72
|
+
- lib/mr_poole/helper.rb
|
73
|
+
- lib/mr_poole/version.rb
|
74
|
+
- mr_poole.gemspec
|
75
|
+
- spec/cli_spec.rb
|
76
|
+
- spec/command_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
homepage: http://github.com/mmcclimon/mr_poole
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.1.4
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: A butler for Jekyll. Provides a command-line interface (called `poole`) for
|
102
|
+
creating and publishing posts and drafts for Jekyll (http://jekyllrb.com) blogs. The
|
103
|
+
literary Mr. Poole is Jekyll's butler, who "serves Jekyll faithfully, and attempts
|
104
|
+
to do a good job and be loyal to his master" (http://en.wikipedia.org/wiki/Jekyll_and_hyde#Mr._Poole),
|
105
|
+
and the Mr. Poole gem looks to be the same thing.
|
106
|
+
test_files:
|
107
|
+
- spec/cli_spec.rb
|
108
|
+
- spec/command_spec.rb
|
109
|
+
- spec/spec_helper.rb
|