ruhoh 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -3
- data/README.md +40 -0
- data/dash.html +225 -0
- data/history.txt +15 -0
- data/lib/ruhoh/client/client.rb +39 -60
- data/lib/ruhoh/client/help.yml +5 -9
- data/lib/ruhoh/compiler.rb +6 -3
- data/lib/ruhoh/db.rb +7 -13
- data/lib/ruhoh/page.rb +19 -22
- data/lib/ruhoh/parsers/layouts.rb +8 -2
- data/lib/ruhoh/parsers/pages.rb +19 -18
- data/lib/ruhoh/parsers/posts.rb +45 -49
- data/lib/ruhoh/previewer.rb +17 -38
- data/lib/ruhoh/program.rb +28 -0
- data/lib/ruhoh/utils.rb +17 -3
- data/lib/ruhoh/version.rb +2 -2
- data/lib/ruhoh/watch.rb +1 -5
- data/lib/ruhoh.rb +23 -27
- data/ruhoh.gemspec +3 -1
- data/scaffolds/post.html +2 -0
- data/spec/db_spec.rb +2 -9
- data/spec/page_spec.rb +0 -25
- data/spec/parsers/layouts_spec.rb +23 -2
- data/spec/parsers/pages_spec.rb +24 -10
- data/spec/parsers/posts_spec.rb +15 -7
- data/spec/setup_spec.rb +6 -20
- data/spec/spec_helper.rb +3 -1
- metadata +23 -11
- data/lib/ruhoh/parsers/drafts.rb +0 -54
data/Gemfile
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
gemspec
|
3
3
|
|
4
|
-
gem 'rake'
|
5
4
|
gem 'rack', "~> 1.4"
|
6
5
|
gem 'directory_watcher', "~> 1.4"
|
7
6
|
gem 'mustache', "~> 0.99"
|
8
7
|
gem 'maruku', "~> 0.6"
|
9
|
-
gem '
|
8
|
+
gem 'psych', "~> 1.3"
|
10
9
|
|
11
10
|
group :development do
|
12
11
|
gem 'rspec'
|
13
|
-
|
12
|
+
gem 'rake'
|
13
|
+
end
|
data/README.md
CHANGED
@@ -7,3 +7,43 @@
|
|
7
7
|
|
8
8
|
$ gem install ruhoh
|
9
9
|
$ ruhoh help
|
10
|
+
|
11
|
+
|
12
|
+
- Ruhoh.setup :after_setup
|
13
|
+
- Ruhoh::DB.update\_all :after_db_update
|
14
|
+
- @page = Ruhoh::Page.new :after_page_initialize
|
15
|
+
- templater
|
16
|
+
- converter
|
17
|
+
|
18
|
+
|
19
|
+
- setup
|
20
|
+
- database
|
21
|
+
- page
|
22
|
+
- templater
|
23
|
+
- converter
|
24
|
+
- preview
|
25
|
+
- compiler
|
26
|
+
|
27
|
+
### Plugins
|
28
|
+
|
29
|
+
|
30
|
+
1. Mustache method additions
|
31
|
+
Extend the templating language
|
32
|
+
* Mounted onto the page object
|
33
|
+
|
34
|
+
|
35
|
+
2. Add additional converter support (textile)
|
36
|
+
* Mounted onto the page object
|
37
|
+
|
38
|
+
3. Generators ? compose extra pages?
|
39
|
+
I'm leaning toward why? any page should be able to be made
|
40
|
+
by calling a custom mustache method within a given textfile.
|
41
|
+
Jekyll's relevant example is to create mass-pages per each category.
|
42
|
+
Generating extra pages can be mounted onto the compiler
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
data/dash.html
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<style type="text/css">
|
5
|
+
/*!
|
6
|
+
* Bootstrap v2.0.2
|
7
|
+
*
|
8
|
+
* Copyright 2012 Twitter, Inc
|
9
|
+
* Licensed under the Apache License v2.0
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
*
|
12
|
+
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
13
|
+
*/
|
14
|
+
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
|
15
|
+
.clearfix:after{clear:both;}
|
16
|
+
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
|
17
|
+
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
|
18
|
+
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
|
19
|
+
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
|
20
|
+
audio:not([controls]){display:none;}
|
21
|
+
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
|
22
|
+
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
23
|
+
a:hover,a:active{outline:0;}
|
24
|
+
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
|
25
|
+
sup{top:-0.5em;}
|
26
|
+
sub{bottom:-0.25em;}
|
27
|
+
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
|
28
|
+
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
|
29
|
+
button,input{*overflow:visible;line-height:normal;}
|
30
|
+
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
|
31
|
+
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
|
32
|
+
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
|
33
|
+
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
|
34
|
+
textarea{overflow:auto;vertical-align:top;}
|
35
|
+
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
|
36
|
+
a{color:#0088cc;text-decoration:none;}
|
37
|
+
a:hover{color:#005580;text-decoration:underline;}
|
38
|
+
|
39
|
+
/* deep purple: #2E2633 */
|
40
|
+
/* deep red #99173C */
|
41
|
+
body, html {
|
42
|
+
font-size:14px;
|
43
|
+
height:100%;
|
44
|
+
color: #2E2633;
|
45
|
+
}
|
46
|
+
a, a:hover {
|
47
|
+
color: #2E2633;
|
48
|
+
text-decoration:none;
|
49
|
+
}
|
50
|
+
ul {
|
51
|
+
list-style:none;
|
52
|
+
margin:0; padding:0;
|
53
|
+
}
|
54
|
+
#wrapper {
|
55
|
+
margin:auto;
|
56
|
+
width:500px;
|
57
|
+
min-height:100%;
|
58
|
+
position:relative;
|
59
|
+
border-left:2px solid #99173C;
|
60
|
+
}
|
61
|
+
.page-pane {
|
62
|
+
padding:20px 0;
|
63
|
+
width:500px;
|
64
|
+
}
|
65
|
+
|
66
|
+
/* ----------------------------------------- */
|
67
|
+
#nav-wrapper {
|
68
|
+
position:absolute;
|
69
|
+
top:20px;
|
70
|
+
left:-202px;
|
71
|
+
}
|
72
|
+
#nav {
|
73
|
+
position:fixed;
|
74
|
+
line-height:2em;
|
75
|
+
width:200px;
|
76
|
+
}
|
77
|
+
#nav li {margin-bottom:3px;}
|
78
|
+
#nav li a {
|
79
|
+
display:block;
|
80
|
+
padding:0 25px;
|
81
|
+
line-height:50px;
|
82
|
+
border-radius:5px 0 0 5px;
|
83
|
+
font-size:20px;
|
84
|
+
font-weight:bold;
|
85
|
+
font-family:verdana;
|
86
|
+
text-align:right;
|
87
|
+
}
|
88
|
+
#nav li a:hover {
|
89
|
+
background:#DCE9BE;
|
90
|
+
}
|
91
|
+
#nav li a.active {
|
92
|
+
background:#99173C;
|
93
|
+
color:#FFF;
|
94
|
+
}
|
95
|
+
|
96
|
+
/* ----------------------------------------- */
|
97
|
+
ul.page-list {
|
98
|
+
line-height:1.5em;
|
99
|
+
}
|
100
|
+
ul.page-list li {
|
101
|
+
margin-bottom:3px;
|
102
|
+
}
|
103
|
+
ul.page-list li a{
|
104
|
+
display:block;
|
105
|
+
overflow:hidden;
|
106
|
+
padding:10px 25px;
|
107
|
+
font-family:courier;
|
108
|
+
border-radius:0 5px 5px 0;
|
109
|
+
line-height:24px;
|
110
|
+
}
|
111
|
+
ul.page-list li a:hover {
|
112
|
+
background:#DCE9BE;
|
113
|
+
}
|
114
|
+
ul.page-list li a span.title{
|
115
|
+
font-size:18px;
|
116
|
+
font-weight:bold;
|
117
|
+
}
|
118
|
+
ul.page-list li a span.date{
|
119
|
+
position:absolute;
|
120
|
+
right:25px;
|
121
|
+
font-size:12px;
|
122
|
+
}
|
123
|
+
ul.page-list li a span.id {
|
124
|
+
font-size:12px;
|
125
|
+
}
|
126
|
+
</style>
|
127
|
+
</head>
|
128
|
+
<body>
|
129
|
+
<div id="wrapper">
|
130
|
+
|
131
|
+
<div id="nav-wrapper">
|
132
|
+
<ul id="nav">
|
133
|
+
<li><a href="#drafts" class="active">Drafts</a></li>
|
134
|
+
<li><a href="#public">Posts</a></li>
|
135
|
+
<li><a href="#pages">Pages</a></li>
|
136
|
+
</ul>
|
137
|
+
</div>
|
138
|
+
|
139
|
+
<div id="drafts" class="page-pane">
|
140
|
+
<ul class="page-list">
|
141
|
+
{{# db.posts.drafts?to_posts }}
|
142
|
+
<li>
|
143
|
+
<a href="{{url}}">
|
144
|
+
<span class="title">{{title}}</span>
|
145
|
+
<span class="date">{{date}}</span>
|
146
|
+
<br><span class="id">{{id}}</span></small>
|
147
|
+
</a>
|
148
|
+
</li>
|
149
|
+
{{/ db.posts.drafts?to_posts }}
|
150
|
+
</ul>
|
151
|
+
</div>
|
152
|
+
|
153
|
+
<div id="public" class="page-pane" style="display:none">
|
154
|
+
<ul class="page-list">
|
155
|
+
{{#posts}}
|
156
|
+
{{^ type }}
|
157
|
+
<li>
|
158
|
+
<a href="{{url}}">
|
159
|
+
<span class="title">{{title}}</span>
|
160
|
+
<span class="date">{{date}}</span>
|
161
|
+
<br><span class="id">{{id}}</span></small>
|
162
|
+
</a>
|
163
|
+
</li>
|
164
|
+
{{/ type }}
|
165
|
+
{{/posts}}
|
166
|
+
</ul>
|
167
|
+
</div>
|
168
|
+
|
169
|
+
<div id="pages" class="page-pane" style="display:none">
|
170
|
+
<ul class="page-list">
|
171
|
+
{{#pages}}
|
172
|
+
<li>
|
173
|
+
<a href="{{url}}">
|
174
|
+
<span class="title">{{title}}</span>
|
175
|
+
<span class="date">{{date}}</span>
|
176
|
+
<br><span class="id">{{id}}</span></small>
|
177
|
+
</a>
|
178
|
+
</li>
|
179
|
+
{{/pages}}
|
180
|
+
</ul>
|
181
|
+
</div>
|
182
|
+
|
183
|
+
</div>
|
184
|
+
|
185
|
+
<script>
|
186
|
+
// Tabs is a small script for showing/hiding the different page lists.
|
187
|
+
// This mostly likely only works in modern browsers but
|
188
|
+
// I'd rather not add jQuery as a dependency until it's really warranted.
|
189
|
+
var Tabs = {
|
190
|
+
panes : document.getElementsByClassName('page-pane'),
|
191
|
+
nav : document.getElementById('nav'),
|
192
|
+
listEntries : this.nav.children,
|
193
|
+
links : [],
|
194
|
+
|
195
|
+
init : function(){
|
196
|
+
for (var i = 0; i < Tabs.listEntries.length; ++i) {
|
197
|
+
Tabs.links.push(Tabs.listEntries[i].firstChild);
|
198
|
+
}
|
199
|
+
|
200
|
+
Tabs.nav.addEventListener('click', function(e){
|
201
|
+
e.preventDefault();
|
202
|
+
if (e.target.tagName.toLowerCase() !== 'a') return;
|
203
|
+
|
204
|
+
Tabs.clearActive();
|
205
|
+
Tabs.hidePanes();
|
206
|
+
e.target.className = 'active';
|
207
|
+
var id = e.target.href.split('#')[1];
|
208
|
+
document.getElementById(id).style.display = 'block';
|
209
|
+
}, false)
|
210
|
+
},
|
211
|
+
|
212
|
+
clearActive : function(){
|
213
|
+
for (var i = 0; i < Tabs.links.length; ++i)
|
214
|
+
Tabs.links[i].className = '';
|
215
|
+
},
|
216
|
+
|
217
|
+
hidePanes : function(){
|
218
|
+
for (var i = 0; i < Tabs.panes.length; ++i)
|
219
|
+
Tabs.panes[i].style.display = 'none';
|
220
|
+
}
|
221
|
+
}
|
222
|
+
Tabs.init();
|
223
|
+
</script>
|
224
|
+
</body>
|
225
|
+
</html>
|
data/history.txt
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
|
2
|
+
4.5.2012
|
3
|
+
0.2.0 - API changes:
|
4
|
+
- [change] Dates in post filenames are now optional but will be required in metadata.
|
5
|
+
- [remove] _draft folder. Drafts are now simply a 'type' of post.
|
6
|
+
- [remove] publish and un-publish in favor of specifying 'type' meta attribute.
|
7
|
+
- [add] titleize method to client which renames draft filenames to their titles if set.
|
8
|
+
- [change] /_draft panel is now /dash with updated UI.
|
9
|
+
- [change] - rackup configuration is now through Ruhoh::Program.preview.
|
10
|
+
- FEATURES:
|
11
|
+
- Maintain file extensions for files that don't respond to a converter.
|
12
|
+
- Add 'development'/'production' environment configuration flag.
|
13
|
+
- @ben-biddington Introduces local temporary directory as SampleSitePath.
|
14
|
+
- @ben-biddington adds Travis integration.
|
15
|
+
0.1.4 - BUG: Fix invalid byte sequence in US-ASCII by forcing UTF-8
|
1
16
|
19.4.2012
|
2
17
|
0.1.3 - BUG: Fix drafts not maintaining file extension when publishing
|
3
18
|
BUG: tags helper should use tags database.
|
data/lib/ruhoh/client/client.rb
CHANGED
@@ -7,6 +7,7 @@ class Ruhoh
|
|
7
7
|
BlogScaffold = 'git://github.com/ruhoh/blog.git'
|
8
8
|
|
9
9
|
def initialize(data)
|
10
|
+
@iterator = 0
|
10
11
|
self.setup_paths
|
11
12
|
self.setup_options(data)
|
12
13
|
|
@@ -58,68 +59,24 @@ class Ruhoh
|
|
58
59
|
# Public: Create a new draft file.
|
59
60
|
# Requires no settings as it is meant to be fastest way to create content.
|
60
61
|
def draft
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
62
|
+
begin
|
63
|
+
filename = File.join(Ruhoh.paths.posts, "untitled-#{@iterator}.#{@options.ext}")
|
64
|
+
@iterator += 1
|
65
|
+
end while File.exist?(filename)
|
65
66
|
|
66
67
|
FileUtils.mkdir_p File.dirname(filename)
|
67
|
-
File.open(@paths.post_template) do |template|
|
68
|
-
File.open(filename, 'w') do |post|
|
69
|
-
post.puts template.read
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
Ruhoh::Friend.say {
|
74
|
-
green "New draft:"
|
75
|
-
green Ruhoh.relative_path(filename)
|
76
|
-
green 'View drafts at the URL: /_drafts'
|
77
|
-
}
|
78
|
-
end
|
79
|
-
|
80
|
-
# Public: Publishes the last active draft file.
|
81
|
-
def publish
|
82
|
-
id = self.last('draft')
|
83
|
-
Ruhoh::Friend.say { yellow "No draft to publish." ; exit } if id.nil?
|
84
|
-
Ruhoh::Friend.say { plain "Publishing draft: #{id}" }
|
85
|
-
draft = Ruhoh::Parsers::Posts.process_file(id)
|
86
|
-
|
87
|
-
Ruhoh::Friend.say { red "Draft title cannot be blank." ; exit } unless draft['data']['title']
|
88
|
-
Ruhoh::Friend.say {
|
89
|
-
red "Invalid date format: #{draft['data']['date']}"
|
90
|
-
red "Date format must be YYYY-MM-DD."
|
91
|
-
exit
|
92
|
-
} unless draft['data']['date']
|
93
|
-
|
94
|
-
draft['data']['ext'] = File.extname(id).gsub('.','')
|
95
|
-
filename = Ruhoh::Parsers::Posts.to_filename(draft['data'])
|
96
68
|
|
97
|
-
|
98
|
-
|
99
|
-
|
69
|
+
output = File.open(@paths.post_template) { |f| f.read }
|
70
|
+
output = output.gsub('{{DATE}}', Ruhoh::Parsers::Posts.formatted_date(Time.now))
|
71
|
+
File.open(filename, 'w') {|f| f.puts output }
|
100
72
|
|
101
|
-
FileUtils.mkdir_p File.dirname(filename)
|
102
|
-
FileUtils.mv id, filename
|
103
|
-
|
104
73
|
Ruhoh::Friend.say {
|
105
|
-
green "
|
74
|
+
green "New draft:"
|
106
75
|
green Ruhoh.relative_path(filename)
|
76
|
+
green 'View drafts at the URL: /dash'
|
107
77
|
}
|
108
78
|
end
|
109
|
-
|
110
|
-
# Public: Unpublishes the last active post file.
|
111
|
-
def unpublish
|
112
|
-
post = self.last('post')
|
113
|
-
Ruhoh::Friend.say { yellow "No post to unpublish." ; exit } if post.nil?
|
114
|
-
Ruhoh::Friend.say { plain "Unpublishing post: #{post}" }
|
115
|
-
|
116
|
-
FileUtils.mv post, File.join(Ruhoh.paths.drafts, File.basename(post))
|
117
|
-
|
118
|
-
Ruhoh::Friend.say {
|
119
|
-
yellow "Unpublished post:"
|
120
|
-
yellow Ruhoh.relative_path(post)
|
121
|
-
}
|
122
|
-
end
|
79
|
+
alias_method :post, :draft
|
123
80
|
|
124
81
|
# Public: Create a new page file.
|
125
82
|
def page
|
@@ -149,6 +106,19 @@ class Ruhoh
|
|
149
106
|
}
|
150
107
|
end
|
151
108
|
|
109
|
+
# Public: Update draft filenames to their corresponding titles.
|
110
|
+
def titleize
|
111
|
+
Ruhoh::Parsers::Posts.files.each do |file|
|
112
|
+
next unless File.basename(file) =~ /^untitled/
|
113
|
+
parsed_page = Ruhoh::Utils.parse_file(file)
|
114
|
+
next unless parsed_page['data']['title']
|
115
|
+
new_name = Ruhoh::Parsers::Posts.to_slug(parsed_page['data']['title'])
|
116
|
+
new_file = File.join(File.dirname(file), "#{new_name}#{File.extname(file)}")
|
117
|
+
FileUtils.mv(file, new_file)
|
118
|
+
Ruhoh::Friend.say { green "Renamed #{file} to: #{new_file}" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
152
122
|
# Public: Compile to static website.
|
153
123
|
def compile
|
154
124
|
Ruhoh::Compiler.new(@args[1]).compile
|
@@ -256,7 +226,7 @@ class Ruhoh
|
|
256
226
|
# Return the payload hash for inspection/study.
|
257
227
|
def payload
|
258
228
|
require 'pp'
|
259
|
-
Ruhoh::DB.
|
229
|
+
Ruhoh::DB.update_all
|
260
230
|
Ruhoh::Friend.say {
|
261
231
|
plain Ruhoh::Templaters::Base.build_payload.pretty_inspect
|
262
232
|
}
|
@@ -264,9 +234,20 @@ class Ruhoh
|
|
264
234
|
|
265
235
|
# Internal: Outputs a list of the given data-type to the terminal.
|
266
236
|
def list(type)
|
267
|
-
|
268
|
-
|
269
|
-
|
237
|
+
data = case type
|
238
|
+
when :posts
|
239
|
+
Ruhoh::DB.update(:posts)
|
240
|
+
Ruhoh::DB.posts['dictionary']
|
241
|
+
when :drafts
|
242
|
+
Ruhoh::DB.update(:posts)
|
243
|
+
drafts = Ruhoh::DB.posts['drafts']
|
244
|
+
h = {}
|
245
|
+
drafts.each {|id| h[id] = Ruhoh::DB.posts['dictionary'][id]}
|
246
|
+
h
|
247
|
+
when :pages
|
248
|
+
Ruhoh::DB.update(:pages)
|
249
|
+
Ruhoh::DB.pages
|
250
|
+
end
|
270
251
|
|
271
252
|
if @options.verbose
|
272
253
|
Ruhoh::Friend.say {
|
@@ -298,8 +279,6 @@ class Ruhoh
|
|
298
279
|
case type
|
299
280
|
when 'post'
|
300
281
|
Ruhoh::Parsers::Posts.files
|
301
|
-
when 'draft'
|
302
|
-
Ruhoh::Parsers::Drafts.files
|
303
282
|
when 'page'
|
304
283
|
Ruhoh::Parsers::Pages.files
|
305
284
|
else
|
data/lib/ruhoh/client/help.yml
CHANGED
@@ -15,22 +15,18 @@ commands:
|
|
15
15
|
"desc" : |
|
16
16
|
Compile to static website.
|
17
17
|
-
|
18
|
-
"command" : "draft"
|
18
|
+
"command" : "post | draft"
|
19
19
|
"desc" : |
|
20
20
|
Create a new draft.
|
21
|
-
-
|
22
|
-
"command" : "publish"
|
23
|
-
"desc" : |
|
24
|
-
Publish the last active draft file.
|
25
|
-
-
|
26
|
-
"command" : "unpublish"
|
27
|
-
"desc" : |
|
28
|
-
UnPublish the last active post file.
|
29
21
|
-
|
30
22
|
"command" : "page <path>"
|
31
23
|
"desc" : |
|
32
24
|
Create a new page at the given path.
|
33
25
|
-
|
26
|
+
"command" : "titleize"
|
27
|
+
"desc" : |
|
28
|
+
Update draft filenames to their corresponding titles. Drafts without titles are ignored.
|
29
|
+
-
|
34
30
|
"command" : "drafts"
|
35
31
|
"desc" : |
|
36
32
|
List all drafts.
|
data/lib/ruhoh/compiler.rb
CHANGED
@@ -3,7 +3,10 @@ class Ruhoh
|
|
3
3
|
class Compiler
|
4
4
|
|
5
5
|
def initialize(target_directory)
|
6
|
-
Ruhoh
|
6
|
+
Ruhoh.config.env ||= 'production'
|
7
|
+
Ruhoh::Friend.say { plain "Compiling for environment: '#{Ruhoh.config.env}'" }
|
8
|
+
|
9
|
+
Ruhoh::DB.update_all
|
7
10
|
@target = target_directory || "./#{Ruhoh.folders.compiled}"
|
8
11
|
@page = Ruhoh::Page.new
|
9
12
|
end
|
@@ -21,11 +24,11 @@ class Ruhoh
|
|
21
24
|
|
22
25
|
def pages
|
23
26
|
FileUtils.cd(@target) {
|
24
|
-
Ruhoh::DB.
|
27
|
+
Ruhoh::DB.all_pages.each_value do |p|
|
25
28
|
@page.change(p['id'])
|
26
29
|
|
27
30
|
FileUtils.mkdir_p File.dirname(@page.compiled_path)
|
28
|
-
File.open(@page.compiled_path, 'w') { |p| p.puts @page.render }
|
31
|
+
File.open(@page.compiled_path, 'w:UTF-8') { |p| p.puts @page.render }
|
29
32
|
|
30
33
|
Ruhoh::Friend.say { green "processed: #{p['id']}" }
|
31
34
|
end
|
data/lib/ruhoh/db.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
require "observer"
|
2
|
-
|
3
1
|
class Ruhoh
|
4
|
-
|
5
2
|
# Public: Database class for interacting with "data" in Ruhoh.
|
6
3
|
class DB
|
7
4
|
class << self
|
8
|
-
|
9
|
-
WhiteList = [:site, :posts, :drafts, :pages, :routes, :layouts, :partials]
|
5
|
+
WhiteList = [:site, :posts, :pages, :routes, :layouts, :partials]
|
10
6
|
self.__send__ :attr_reader, *WhiteList
|
11
7
|
|
12
8
|
def update(name)
|
@@ -18,8 +14,6 @@ class Ruhoh
|
|
18
14
|
Ruhoh::Parsers::Routes.generate
|
19
15
|
when :posts
|
20
16
|
Ruhoh::Parsers::Posts.generate
|
21
|
-
when :drafts
|
22
|
-
Ruhoh::Parsers::Drafts.generate
|
23
17
|
when :pages
|
24
18
|
Ruhoh::Parsers::Pages.generate
|
25
19
|
when :layouts
|
@@ -30,18 +24,18 @@ class Ruhoh
|
|
30
24
|
raise "Data type: '#{name}' is not a valid data type."
|
31
25
|
end
|
32
26
|
)
|
33
|
-
changed
|
34
|
-
notify_observers(name)
|
35
27
|
end
|
36
|
-
|
37
|
-
def
|
28
|
+
|
29
|
+
def all_pages
|
30
|
+
self.posts['dictionary'].merge(self.pages)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_all
|
38
34
|
WhiteList.each do |var|
|
39
35
|
self.__send__ :update, var
|
40
36
|
end
|
41
37
|
end
|
42
38
|
|
43
39
|
end #self
|
44
|
-
|
45
40
|
end #DB
|
46
|
-
|
47
41
|
end #Ruhoh
|
data/lib/ruhoh/page.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Ruhoh
|
2
|
-
|
3
2
|
class Page
|
4
3
|
attr_reader :id, :data, :content, :sub_layout, :master_layout
|
5
4
|
attr_accessor :templater, :converter
|
@@ -11,12 +10,10 @@ class Ruhoh
|
|
11
10
|
|
12
11
|
# Public: Change this page using an id.
|
13
12
|
def change(id)
|
14
|
-
|
13
|
+
self.reset
|
15
14
|
@path = id
|
16
15
|
@data = if id =~ Regexp.new("^#{Ruhoh.folders.posts}")
|
17
16
|
Ruhoh::DB.posts['dictionary'][id]
|
18
|
-
elsif id =~ Regexp.new("^#{Ruhoh.folders.drafts}")
|
19
|
-
Ruhoh::DB.drafts[id]
|
20
17
|
else
|
21
18
|
@path = "#{Ruhoh.folders.pages}/#{id}"
|
22
19
|
Ruhoh::DB.pages[id]
|
@@ -26,26 +23,15 @@ class Ruhoh
|
|
26
23
|
@id = id
|
27
24
|
end
|
28
25
|
|
29
|
-
# Public: Change this page using a URL.
|
30
|
-
def change_with_url(url)
|
31
|
-
id = if url =~ Regexp.new("^/#{Ruhoh.folders.drafts}")
|
32
|
-
url.gsub(/^\//,'')
|
33
|
-
else
|
34
|
-
Ruhoh::DB.routes[url]
|
35
|
-
end
|
36
|
-
raise "Page id not found for url: #{url}" unless id
|
37
|
-
self.change(id)
|
38
|
-
end
|
39
|
-
|
40
26
|
def render
|
41
|
-
|
27
|
+
self.ensure_id
|
42
28
|
self.process_layouts
|
43
29
|
self.process_content
|
44
30
|
@templater.render(self)
|
45
31
|
end
|
46
32
|
|
47
33
|
def process_layouts
|
48
|
-
|
34
|
+
self.ensure_id
|
49
35
|
if @data['layout']
|
50
36
|
@sub_layout = Ruhoh::DB.layouts[@data['layout']]
|
51
37
|
raise "Layout does not exist: #{@data['layout']}" unless @sub_layout
|
@@ -61,7 +47,7 @@ class Ruhoh
|
|
61
47
|
# in order to invoke converters on the result.
|
62
48
|
# Converters (markdown) always choke on the templating language.
|
63
49
|
def process_content
|
64
|
-
|
50
|
+
self.ensure_id
|
65
51
|
data = Ruhoh::Utils.parse_file(Ruhoh.paths.site_source, @path)
|
66
52
|
raise "Invalid Frontmatter in page: #{@path}" if data.empty?
|
67
53
|
|
@@ -72,7 +58,7 @@ class Ruhoh
|
|
72
58
|
# Public: Return page attributes suitable for inclusion in the
|
73
59
|
# 'payload' of the given templater.
|
74
60
|
def attributes
|
75
|
-
|
61
|
+
self.ensure_id
|
76
62
|
@data['content'] = @content
|
77
63
|
@data
|
78
64
|
end
|
@@ -81,13 +67,24 @@ class Ruhoh
|
|
81
67
|
#
|
82
68
|
# Returns: [String] The relative path to the compiled file for this page.
|
83
69
|
def compiled_path
|
84
|
-
|
70
|
+
self.ensure_id
|
85
71
|
path = CGI.unescape(@data['url']).gsub(/^\//, '') #strip leading slash.
|
86
72
|
path = "index.html" if path.empty?
|
87
|
-
path += '/index.html' unless path =~
|
73
|
+
path += '/index.html' unless path =~ /\.\w+$/
|
88
74
|
path
|
89
75
|
end
|
90
76
|
|
77
|
+
def reset
|
78
|
+
@id = nil
|
79
|
+
@data = nil
|
80
|
+
@content = nil
|
81
|
+
@sub_layout = nil
|
82
|
+
@master_layout = nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def ensure_id
|
86
|
+
raise '@page ID is null: ID must be set via page.change(id) or page.change_with_url(url)' if @id.nil?
|
87
|
+
end
|
88
|
+
|
91
89
|
end #Page
|
92
|
-
|
93
90
|
end #Ruhoh
|
@@ -5,13 +5,18 @@ class Ruhoh
|
|
5
5
|
# Generate layouts only from the active theme.
|
6
6
|
def self.generate
|
7
7
|
layouts = {}
|
8
|
-
|
8
|
+
invalid = []
|
9
9
|
self.files.each do |filename|
|
10
10
|
id = File.basename(filename, File.extname(filename))
|
11
11
|
layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
|
12
|
-
|
12
|
+
|
13
|
+
if layouts[id].empty?
|
14
|
+
error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
|
15
|
+
invalid << [filename, error]
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
19
|
+
Ruhoh::Utils.report('Layouts', layouts, invalid)
|
15
20
|
layouts
|
16
21
|
end
|
17
22
|
|
@@ -25,6 +30,7 @@ class Ruhoh
|
|
25
30
|
}
|
26
31
|
end
|
27
32
|
|
33
|
+
|
28
34
|
end #Layouts
|
29
35
|
end #Parsers
|
30
36
|
end #Ruhoh
|