ottogen 0.2.0 → 1.0.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 +4 -4
- data/README.md +59 -43
- data/bin/otto +1 -0
- data/lib/ottogen/collection.rb +35 -0
- data/lib/ottogen/collection_item.rb +61 -0
- data/lib/ottogen/config.rb +126 -0
- data/lib/ottogen/front_matter.rb +30 -0
- data/lib/ottogen/layout.rb +65 -0
- data/lib/ottogen/otto.rb +27 -13
- data/lib/ottogen/ottogen.rb +124 -53
- data/lib/ottogen/page.rb +58 -0
- data/lib/ottogen/permalink.rb +35 -0
- data/lib/ottogen/post.rb +108 -0
- data/lib/ottogen/scaffold.rb +50 -0
- data/spec/ottogen/collection_spec.rb +61 -0
- data/spec/ottogen/config_spec.rb +249 -0
- data/spec/ottogen/layout_spec.rb +150 -0
- data/spec/ottogen/ottogen_spec.rb +430 -5
- data/spec/ottogen/page_spec.rb +101 -0
- data/spec/ottogen/permalink_spec.rb +49 -0
- data/spec/ottogen/post_spec.rb +172 -0
- data/spec/spec_helper.rb +51 -0
- metadata +176 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f607f53a4e24feac72ea03754c6b81917fcbe0301afb76d5dcf95f46d5d1d01
|
|
4
|
+
data.tar.gz: b87bc716dc2b0810c0de784aef321c1e774c3fbc0071e919b2cf3f78990c3f75
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb1148fac045000ea0e7d62fb11782162a6109fd14ae79c5ea4b36e687acd58f5f28f0954a76cdd79adae9b95c779f5e9f5c19e16fedd46530359817840bf030
|
|
7
|
+
data.tar.gz: a22313f2c238d677d10b4f43e778dc432ff3e4966f0455c0949933fece794d53e7830fb0a3f968e84edfcc02e6c0dfccbe4d2fb5041c04a330dab29cebafcc14
|
data/README.md
CHANGED
|
@@ -1,74 +1,90 @@
|
|
|
1
1
|
# Otto
|
|
2
2
|
|
|
3
|
-
AsciiDoc static site generator.
|
|
3
|
+
AsciiDoc-powered static site generator with Jekyll-style conventions: layouts, includes, data files, posts, drafts, permalinks, and custom collections.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
``` sh
|
|
7
|
+
```sh
|
|
10
8
|
gem install ottogen
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Requires Ruby 3.0 or newer.
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
14
|
|
|
15
|
-
```
|
|
15
|
+
```sh
|
|
16
16
|
mkdir mysite && cd mysite
|
|
17
17
|
otto init
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
**Build the site**
|
|
21
|
-
|
|
22
|
-
``` sh
|
|
23
18
|
otto build
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**Serve the site**
|
|
27
|
-
|
|
28
|
-
``` sh
|
|
29
19
|
otto serve
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**View the site**
|
|
33
|
-
|
|
34
|
-
``` sh
|
|
35
20
|
open http://127.0.0.1:8778/
|
|
36
21
|
```
|
|
37
22
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
TODO
|
|
41
|
-
|
|
42
|
-
**Paragraphs**
|
|
23
|
+
For a longer walkthrough including AsciiDoc syntax, see [GUIDE.md](GUIDE.md).
|
|
43
24
|
|
|
44
|
-
|
|
25
|
+
## Commands
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
| Command | Description |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `otto init [DIR]` | Scaffold a new site (current dir if omitted) |
|
|
30
|
+
| `otto build` | Render the site to `_build/` |
|
|
31
|
+
| `otto build --drafts` | Include posts from `_drafts/` |
|
|
32
|
+
| `otto watch` | Rebuild on file change |
|
|
33
|
+
| `otto serve` | Serve `_build/` on port 8778 |
|
|
34
|
+
| `otto generate PAGE` | Create a new page in `pages/` |
|
|
35
|
+
| `otto post "Title"` | Create a new dated post in `_posts/` |
|
|
36
|
+
| `otto clean` | Delete `_build/` |
|
|
37
|
+
| `otto doctor` | Sanity-check project layout |
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
## Project layout
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
```
|
|
42
|
+
my-site/
|
|
43
|
+
├── .otto # marker
|
|
44
|
+
├── config.yml # site config
|
|
45
|
+
├── assets/ # copied verbatim into _build/
|
|
46
|
+
├── pages/ # AsciiDoc pages, output mirrors path
|
|
47
|
+
├── _layouts/ # ERB layouts (.html.erb)
|
|
48
|
+
├── _includes/ # ERB partials
|
|
49
|
+
├── _data/ # YAML/JSON files exposed as site.data.*
|
|
50
|
+
├── _posts/ # YYYY-MM-DD-slug.adoc
|
|
51
|
+
└── _drafts/ # undated drafts (excluded by default)
|
|
52
|
+
```
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
## Configuration (`config.yml`)
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
```yaml
|
|
57
|
+
title: My Otto Site
|
|
58
|
+
description: Things I write
|
|
59
|
+
url: https://example.com
|
|
60
|
+
baseurl: ""
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
permalink: /:year/:month/:day/:slug/
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
collections:
|
|
65
|
+
recipes:
|
|
66
|
+
output: true
|
|
67
|
+
```
|
|
59
68
|
|
|
60
|
-
|
|
69
|
+
`permalink` accepts these tokens: `:year`, `:month`, `:day`, `:slug`, `:title`. Templates ending in `/` produce pretty URLs (`<path>/index.html`).
|
|
61
70
|
|
|
62
|
-
|
|
71
|
+
## Pages and posts
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
Both support YAML front matter:
|
|
65
74
|
|
|
66
|
-
|
|
75
|
+
```adoc
|
|
76
|
+
---
|
|
77
|
+
layout: default
|
|
78
|
+
title: Hello
|
|
79
|
+
tags: [ruby, cli]
|
|
80
|
+
---
|
|
81
|
+
= Hello
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
Welcome to {site_title}. This page is at {page_url}.
|
|
84
|
+
```
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
Pages live under `pages/`; posts under `_posts/` with `YYYY-MM-DD-slug.adoc` names. Layouts wrap rendered AsciiDoc; partials in `_includes/` are pulled in via `<%= partial 'header.html' %>`.
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
## License
|
|
73
89
|
|
|
74
|
-
|
|
90
|
+
MIT
|
data/bin/otto
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'collection_item'
|
|
4
|
+
|
|
5
|
+
module Ottogen
|
|
6
|
+
class Collection
|
|
7
|
+
DIR_PREFIX = '_'
|
|
8
|
+
|
|
9
|
+
def self.from_config(name, settings)
|
|
10
|
+
output = settings.is_a?(Hash) && settings['output'] == true
|
|
11
|
+
new(name: name, output: output)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :name, :items
|
|
15
|
+
|
|
16
|
+
def initialize(name:, output:)
|
|
17
|
+
@name = name
|
|
18
|
+
@output = output
|
|
19
|
+
@items = discover_items
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def output?
|
|
23
|
+
@output
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def discover_items
|
|
29
|
+
dir = "#{DIR_PREFIX}#{@name}"
|
|
30
|
+
return [] unless Dir.exist?(dir)
|
|
31
|
+
|
|
32
|
+
Dir.glob(File.join(dir, '*.adoc')).map { |path| CollectionItem.read(path, @name) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'front_matter'
|
|
4
|
+
|
|
5
|
+
module Ottogen
|
|
6
|
+
class CollectionItem
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
def self.read(path, collection_name)
|
|
10
|
+
raw = File.read(path)
|
|
11
|
+
front_matter, body = FrontMatter.split(raw, path)
|
|
12
|
+
new(path: path, collection_name: collection_name, front_matter: front_matter, body: body)
|
|
13
|
+
rescue FrontMatter::Error => e
|
|
14
|
+
raise Error, e.message
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :path, :collection_name, :front_matter, :body
|
|
18
|
+
attr_accessor :permalink
|
|
19
|
+
|
|
20
|
+
def initialize(path:, collection_name:, front_matter:, body:)
|
|
21
|
+
@path = path
|
|
22
|
+
@collection_name = collection_name
|
|
23
|
+
@front_matter = front_matter
|
|
24
|
+
@body = body
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def slug
|
|
28
|
+
File.basename(@path, '.adoc')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def url
|
|
32
|
+
return @permalink.url if @permalink
|
|
33
|
+
|
|
34
|
+
"/#{@collection_name}/#{slug}.html"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def output_path(build_dir)
|
|
38
|
+
return @permalink.output_path(build_dir) if @permalink
|
|
39
|
+
|
|
40
|
+
File.join(build_dir, @collection_name, "#{slug}.html")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def asciidoctor_attributes
|
|
44
|
+
attrs = @front_matter.transform_keys { |key| "page_#{key}" }
|
|
45
|
+
attrs['page_url'] = url
|
|
46
|
+
attrs['page_slug'] = slug
|
|
47
|
+
attrs
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def respond_to_missing?(name, include_private = false)
|
|
51
|
+
@front_matter.key?(name.to_s) || super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def method_missing(name, *args)
|
|
55
|
+
key = name.to_s
|
|
56
|
+
return @front_matter[key] if @front_matter.key?(key)
|
|
57
|
+
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
require_relative 'collection'
|
|
7
|
+
require_relative 'post'
|
|
8
|
+
|
|
9
|
+
module Ottogen
|
|
10
|
+
class Config
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
|
|
13
|
+
DEFAULT_PATH = 'config.yml'
|
|
14
|
+
DATA_DIR = '_data'
|
|
15
|
+
DATA_EXTENSIONS = %w[.yml .yaml .json].freeze
|
|
16
|
+
|
|
17
|
+
def self.load(path = DEFAULT_PATH, drafts: false)
|
|
18
|
+
raise Error, "config.yml not found at #{path}" unless File.exist?(path)
|
|
19
|
+
|
|
20
|
+
values = YAML.safe_load_file(path) || {}
|
|
21
|
+
new(values, load_data_files, load_posts(drafts: drafts), load_collections(values['collections']))
|
|
22
|
+
rescue Psych::SyntaxError => e
|
|
23
|
+
raise Error, "malformed YAML in #{path}: #{e.message}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.load_collections(config)
|
|
27
|
+
return {} unless config.is_a?(Hash)
|
|
28
|
+
|
|
29
|
+
config.to_h { |name, settings| [name, Collection.from_config(name, settings || {})] }
|
|
30
|
+
end
|
|
31
|
+
private_class_method :load_collections
|
|
32
|
+
|
|
33
|
+
def self.load_posts(drafts: false)
|
|
34
|
+
posts = Post.discover
|
|
35
|
+
posts += Post.discover_drafts if drafts
|
|
36
|
+
posts.sort_by(&:date).reverse
|
|
37
|
+
rescue Post::Error => e
|
|
38
|
+
raise Error, e.message
|
|
39
|
+
end
|
|
40
|
+
private_class_method :load_posts
|
|
41
|
+
|
|
42
|
+
def self.load_data_files
|
|
43
|
+
return {} unless Dir.exist?(DATA_DIR)
|
|
44
|
+
|
|
45
|
+
Dir.glob(File.join(DATA_DIR, '*.{yml,yaml,json}')).to_h do |file|
|
|
46
|
+
[File.basename(file, '.*'), parse_data_file(file)]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.parse_data_file(file)
|
|
51
|
+
if file.end_with?('.json')
|
|
52
|
+
JSON.parse(File.read(file))
|
|
53
|
+
else
|
|
54
|
+
YAML.safe_load_file(file)
|
|
55
|
+
end
|
|
56
|
+
rescue Psych::SyntaxError, JSON::ParserError => e
|
|
57
|
+
raise Error, "malformed data file at #{file}: #{e.message}"
|
|
58
|
+
end
|
|
59
|
+
private_class_method :load_data_files, :parse_data_file
|
|
60
|
+
|
|
61
|
+
def initialize(values, data_files = {}, posts = [], collections = {})
|
|
62
|
+
@values = values
|
|
63
|
+
@data = Data.new(data_files)
|
|
64
|
+
@posts = posts
|
|
65
|
+
@collections = collections
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
attr_reader :data, :posts, :collections
|
|
69
|
+
|
|
70
|
+
def tags
|
|
71
|
+
group_posts_by(&:tags)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def categories
|
|
75
|
+
group_posts_by(&:categories)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def group_posts_by(&block)
|
|
79
|
+
@posts.each_with_object({}) do |post, acc|
|
|
80
|
+
block.call(post).each { |key| (acc[key] ||= []) << post }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
private :group_posts_by
|
|
84
|
+
|
|
85
|
+
def [](key)
|
|
86
|
+
@values[key.to_s]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def asciidoctor_attributes
|
|
90
|
+
@values.transform_keys { |key| "site_#{key}" }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def respond_to_missing?(name, include_private = false)
|
|
94
|
+
key = name.to_s
|
|
95
|
+
@values.key?(key) || @collections.key?(key) || super
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def method_missing(name, *args)
|
|
99
|
+
key = name.to_s
|
|
100
|
+
return @collections[key].items if @collections.key?(key)
|
|
101
|
+
return @values[key] if @values.key?(key)
|
|
102
|
+
|
|
103
|
+
super
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class Data
|
|
107
|
+
def initialize(files)
|
|
108
|
+
@files = files
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def [](key)
|
|
112
|
+
@files[key.to_s]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def respond_to_missing?(name, include_private = false)
|
|
116
|
+
@files.key?(name.to_s) || super
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def method_missing(name, *args)
|
|
120
|
+
return @files[name.to_s] if @files.key?(name.to_s)
|
|
121
|
+
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Ottogen
|
|
6
|
+
module FrontMatter
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
OPENERS = ["---\n", "---\r\n"].freeze
|
|
10
|
+
|
|
11
|
+
def self.split(raw, path)
|
|
12
|
+
return [{}, raw] unless OPENERS.any? { |opener| raw.start_with?(opener) }
|
|
13
|
+
|
|
14
|
+
lines = raw.lines
|
|
15
|
+
closing = lines[1..].index { |line| line.chomp == '---' }
|
|
16
|
+
raise Error, "unclosed front matter in #{path}" if closing.nil?
|
|
17
|
+
|
|
18
|
+
yaml_text = lines[1..closing].join
|
|
19
|
+
body = (lines[(closing + 2)..] || []).join
|
|
20
|
+
[parse_yaml(yaml_text, path), body]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.parse_yaml(text, path)
|
|
24
|
+
YAML.safe_load(text) || {}
|
|
25
|
+
rescue Psych::SyntaxError => e
|
|
26
|
+
raise Error, "malformed YAML front matter in #{path}: #{e.message}"
|
|
27
|
+
end
|
|
28
|
+
private_class_method :parse_yaml
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'erb'
|
|
4
|
+
|
|
5
|
+
require_relative 'front_matter'
|
|
6
|
+
|
|
7
|
+
module Ottogen
|
|
8
|
+
class Layout
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
LAYOUTS_DIR = '_layouts'
|
|
12
|
+
EXTENSION = '.html.erb'
|
|
13
|
+
|
|
14
|
+
def self.find(name)
|
|
15
|
+
path = File.join(LAYOUTS_DIR, "#{name}#{EXTENSION}")
|
|
16
|
+
raise Error, "layout '#{name}' not found at #{path}" unless File.exist?(path)
|
|
17
|
+
|
|
18
|
+
raw = File.read(path)
|
|
19
|
+
front_matter, body = FrontMatter.split(raw, path)
|
|
20
|
+
new(name: name, front_matter: front_matter, body: body)
|
|
21
|
+
rescue FrontMatter::Error => e
|
|
22
|
+
raise Error, e.message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :name, :front_matter, :body
|
|
26
|
+
|
|
27
|
+
def initialize(name:, front_matter:, body:)
|
|
28
|
+
@name = name
|
|
29
|
+
@front_matter = front_matter
|
|
30
|
+
@body = body
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render(content:, site:, page:)
|
|
34
|
+
context = Context.new(content: content, site: site, page: page)
|
|
35
|
+
result = ERB.new(@body, trim_mode: '-').result(context.binding_for_erb)
|
|
36
|
+
parent_name = @front_matter['layout']
|
|
37
|
+
return result unless parent_name
|
|
38
|
+
|
|
39
|
+
Layout.find(parent_name).render(content: result, site: site, page: page)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Context
|
|
43
|
+
INCLUDES_DIR = '_includes'
|
|
44
|
+
|
|
45
|
+
attr_reader :content, :site, :page
|
|
46
|
+
|
|
47
|
+
def initialize(content:, site:, page:)
|
|
48
|
+
@content = content
|
|
49
|
+
@site = site
|
|
50
|
+
@page = page
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def binding_for_erb
|
|
54
|
+
binding
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def partial(name)
|
|
58
|
+
path = File.join(INCLUDES_DIR, name)
|
|
59
|
+
raise Error, "include '#{name}' not found at #{path}" unless File.exist?(path)
|
|
60
|
+
|
|
61
|
+
ERB.new(File.read(path), trim_mode: '-').result(binding_for_erb)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/ottogen/otto.rb
CHANGED
|
@@ -1,42 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'ottogen'
|
|
3
5
|
|
|
4
6
|
module Ottogen
|
|
5
7
|
class Otto < Thor
|
|
6
|
-
desc
|
|
7
|
-
def init(dir=nil)
|
|
8
|
+
desc 'init [DIR]', 'Initialize a new otto static site in DIR (defaults to the current directory)'
|
|
9
|
+
def init(dir = nil)
|
|
8
10
|
Ottogen.init(dir)
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
desc
|
|
13
|
+
desc 'build', 'Build the static site'
|
|
14
|
+
option :drafts, type: :boolean, default: false, desc: 'Include drafts from _drafts/'
|
|
12
15
|
def build
|
|
13
|
-
Ottogen.build
|
|
16
|
+
Ottogen.build(drafts: options[:drafts])
|
|
14
17
|
end
|
|
15
18
|
|
|
16
|
-
map
|
|
19
|
+
map 'b' => :build
|
|
17
20
|
|
|
18
|
-
desc
|
|
21
|
+
desc 'generate PAGE', 'Generate a new page'
|
|
19
22
|
def generate(page)
|
|
20
23
|
Ottogen.generate(page)
|
|
21
24
|
end
|
|
22
25
|
|
|
23
|
-
map
|
|
26
|
+
map 'g' => :generate
|
|
24
27
|
|
|
25
|
-
desc
|
|
28
|
+
desc 'clean', 'Clean the static site'
|
|
26
29
|
def clean
|
|
27
30
|
Ottogen.clean
|
|
28
31
|
end
|
|
29
32
|
|
|
30
|
-
desc
|
|
33
|
+
desc 'watch', 'Watch changes to static site'
|
|
34
|
+
option :drafts, type: :boolean, default: false, desc: 'Include drafts from _drafts/'
|
|
31
35
|
def watch
|
|
32
|
-
Ottogen.watch
|
|
36
|
+
Ottogen.watch(drafts: options[:drafts])
|
|
33
37
|
end
|
|
34
38
|
|
|
35
|
-
desc
|
|
39
|
+
desc 'serve', 'Serve the static site'
|
|
36
40
|
def serve
|
|
37
41
|
Ottogen.serve
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
map
|
|
44
|
+
map 's' => :serve
|
|
45
|
+
|
|
46
|
+
desc 'post TITLE', "Generate a new post in _posts/ with today's date"
|
|
47
|
+
def post(title)
|
|
48
|
+
Ottogen.new_post(title)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'doctor', 'Check the project for common configuration problems'
|
|
52
|
+
def doctor
|
|
53
|
+
Ottogen.doctor
|
|
54
|
+
end
|
|
41
55
|
end
|
|
42
56
|
end
|