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