zenweb 2.18.1 → 3.0.0.b1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.autotest +18 -0
- data/.gemtest +0 -0
- data/History.txt +6 -0
- data/Manifest.txt +52 -50
- data/README.txt +27 -22
- data/Rakefile +8 -9
- data/example-site/.isolate.rb +8 -0
- data/example-site/Rakefile +32 -0
- data/example-site/_config.yml +3 -0
- data/example-site/_includes/analytics.html.erb +11 -0
- data/example-site/_includes/header.html.erb +3 -0
- data/example-site/_includes/page_list_item.html +5 -0
- data/example-site/_includes/post_list_item.html +5 -0
- data/example-site/_layouts/post.erb +13 -0
- data/example-site/_layouts/project.erb +39 -0
- data/example-site/_layouts/site.erb +62 -0
- data/example-site/about/index.html.md +8 -0
- data/example-site/atom.xml.erb +43 -0
- data/example-site/blog/2012-01-02-page1.html.md +5 -0
- data/example-site/blog/2012-01-03-page2.html.md +5 -0
- data/example-site/blog/2012-01-04-page3.html.md +5 -0
- data/example-site/blog/_config.yml +1 -0
- data/example-site/blog/index.html.erb +19 -0
- data/example-site/config.ru +33 -0
- data/example-site/css/colors.css.less +71 -0
- data/example-site/css/styles.css +223 -0
- data/example-site/css/syntax.css +171 -0
- data/example-site/img/bg.png +0 -0
- data/example-site/index.html.erb +27 -0
- data/example-site/js/jquery.js +154 -0
- data/example-site/js/site.js +37 -0
- data/example-site/pages/index.html.erb +17 -0
- data/example-site/pages/nonblogpage.html.md +8 -0
- data/example-site/projects/index.html.erb +18 -0
- data/example-site/projects/zenweb.html.erb +9 -0
- data/example-site/sitemap.xml.erb +24 -0
- data/lib/zenweb.rb +12 -0
- data/lib/zenweb/config.rb +126 -0
- data/lib/zenweb/extensions.rb +51 -0
- data/lib/zenweb/page.rb +409 -0
- data/lib/zenweb/plugins/disqus.rb +18 -0
- data/lib/zenweb/plugins/erb.rb +43 -0
- data/lib/zenweb/plugins/google.rb +20 -0
- data/lib/zenweb/plugins/less.rb +10 -0
- data/lib/zenweb/plugins/markdown.rb +121 -0
- data/lib/zenweb/site.rb +237 -0
- data/lib/zenweb/tasks.rake +165 -0
- data/test/helper.rb +53 -0
- data/test/test_zenweb_config.rb +90 -0
- data/test/test_zenweb_extensions.rb +33 -0
- data/test/test_zenweb_page.rb +381 -0
- data/test/test_zenweb_plugins_disqus.rb +41 -0
- data/test/test_zenweb_plugins_erb.rb +60 -0
- data/test/test_zenweb_plugins_google.rb +38 -0
- data/test/test_zenweb_plugins_less.rb +33 -0
- data/test/test_zenweb_plugins_markdown.rb +227 -0
- data/test/test_zenweb_site.rb +294 -0
- metadata +230 -86
- metadata.gz.sig +0 -0
- data/bin/zenweb +0 -27
- data/bin/zenwebpage +0 -66
- data/bin/zenwebsite +0 -39
- data/design/REQUIREMENTS.txt +0 -52
- data/design/ZENWEB_2.txt +0 -69
- data/design/heirarchy.png +0 -0
- data/design/heirarchy.tgif +0 -311
- data/docs/Customizing +0 -76
- data/docs/FAQ +0 -12
- data/docs/Features +0 -128
- data/docs/Presentation +0 -88
- data/docs/QuickStart +0 -32
- data/docs/Renderers +0 -85
- data/docs/SiteMap +0 -13
- data/docs/YourOwnWebsite +0 -32
- data/docs/index +0 -14
- data/docs/metadata.txt +0 -10
- data/lib/ZenWeb.rb +0 -850
- data/lib/ZenWeb/CalendarRenderer.rb +0 -162
- data/lib/ZenWeb/CompactRenderer.rb +0 -45
- data/lib/ZenWeb/CompositeRenderer.rb +0 -63
- data/lib/ZenWeb/FileAttachmentRenderer.rb +0 -57
- data/lib/ZenWeb/FooterRenderer.rb +0 -38
- data/lib/ZenWeb/GenericRenderer.rb +0 -143
- data/lib/ZenWeb/HeaderRenderer.rb +0 -52
- data/lib/ZenWeb/HtmlRenderer.rb +0 -81
- data/lib/ZenWeb/HtmlTableRenderer.rb +0 -94
- data/lib/ZenWeb/HtmlTemplateRenderer.rb +0 -173
- data/lib/ZenWeb/MetadataRenderer.rb +0 -83
- data/lib/ZenWeb/RelativeRenderer.rb +0 -97
- data/lib/ZenWeb/RubyCodeRenderer.rb +0 -56
- data/lib/ZenWeb/SitemapRenderer.rb +0 -56
- data/lib/ZenWeb/StandardRenderer.rb +0 -40
- data/lib/ZenWeb/StupidRenderer.rb +0 -91
- data/lib/ZenWeb/SubpageRenderer.rb +0 -45
- data/lib/ZenWeb/TextToHtmlRenderer.rb +0 -219
- data/lib/ZenWeb/TocRenderer.rb +0 -60
- data/lib/ZenWeb/XXXRenderer.rb +0 -32
- data/test/SiteMap +0 -14
- data/test/Something +0 -4
- data/test/include.txt +0 -3
- data/test/index +0 -8
- data/test/metadata.txt +0 -10
- data/test/ryand/SiteMap +0 -10
- data/test/ryand/blah +0 -4
- data/test/ryand/blah-blah +0 -4
- data/test/ryand/index +0 -52
- data/test/ryand/metadata.txt +0 -2
- data/test/ryand/stuff/index +0 -4
- data/test/test_zenweb.rb +0 -1619
@@ -0,0 +1,51 @@
|
|
1
|
+
##
|
2
|
+
# Walk each parent directory of dir looking for file. Yield each file
|
3
|
+
# found to caller.
|
4
|
+
|
5
|
+
def File.each_parent dir, file
|
6
|
+
until dir == "." do
|
7
|
+
dir = File.dirname dir
|
8
|
+
yield File.join(dir, file).sub(/^\.\//, "")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class File # :nodoc:
|
13
|
+
RUBY19 = "<3".respond_to? :encoding # :nodoc:
|
14
|
+
|
15
|
+
class << self
|
16
|
+
alias :binread :read unless RUBY19
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class String # :nodoc:
|
21
|
+
def valid_encoding? # :nodoc:
|
22
|
+
true
|
23
|
+
end unless File::RUBY19
|
24
|
+
end
|
25
|
+
|
26
|
+
class Time # :nodoc:
|
27
|
+
##
|
28
|
+
# Format as YYYY-MM-DD
|
29
|
+
|
30
|
+
def date
|
31
|
+
strftime "%Y-%m-%d"
|
32
|
+
end
|
33
|
+
|
34
|
+
def ym
|
35
|
+
strftime "%Y-%m"
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Format as YYYY-MM-DD @ HH:MM
|
40
|
+
|
41
|
+
def datetime
|
42
|
+
strftime "%Y-%m-%d @ %H:%M"
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Format as HH:MM
|
47
|
+
|
48
|
+
def time
|
49
|
+
strftime "%H:%M"
|
50
|
+
end
|
51
|
+
end
|
data/lib/zenweb/page.rb
ADDED
@@ -0,0 +1,409 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
gem "rake"
|
4
|
+
require "rake"
|
5
|
+
|
6
|
+
module Zenweb
|
7
|
+
|
8
|
+
##
|
9
|
+
# Page represents pretty much any type of file that goes on your
|
10
|
+
# website or is needed by other pages to build your website. Each
|
11
|
+
# page can have a YAML header that contains configuration data or
|
12
|
+
# variables used in the page.
|
13
|
+
|
14
|
+
class Page
|
15
|
+
include Rake::DSL
|
16
|
+
|
17
|
+
##
|
18
|
+
# The shared site instance.
|
19
|
+
|
20
|
+
attr_reader :site
|
21
|
+
|
22
|
+
##
|
23
|
+
# The path to this source file.
|
24
|
+
|
25
|
+
attr_reader :path
|
26
|
+
|
27
|
+
##
|
28
|
+
# The pages directly below this page. Can be empty.
|
29
|
+
|
30
|
+
attr_reader :subpages
|
31
|
+
|
32
|
+
##
|
33
|
+
# The parent page of this page. Can be nil.
|
34
|
+
|
35
|
+
attr_accessor :parent
|
36
|
+
|
37
|
+
##
|
38
|
+
# Returns a regexp that will match file extensions for all known
|
39
|
+
# renderer types.
|
40
|
+
|
41
|
+
def self.renderers_re
|
42
|
+
@renderers_re ||=
|
43
|
+
begin
|
44
|
+
ext = instance_methods.grep(/^render_/).map { |s|
|
45
|
+
s.to_s.sub(/render_/, '')
|
46
|
+
}
|
47
|
+
/(?:\.(#{ext.join "|"}))+$/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize site, path, config = nil # :nodoc:
|
52
|
+
# TODO: make sure that creating page /a.html strips leading / from path
|
53
|
+
@site, @path = site, path
|
54
|
+
@config = config if config
|
55
|
+
@subpages = []
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Helper method to access the config value named +k+.
|
60
|
+
|
61
|
+
def [] k
|
62
|
+
config[k] or warn("#{self.inspect} does not define #{k.inspect}")
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# All pages below this page, recursively.
|
67
|
+
|
68
|
+
def all_subpages
|
69
|
+
subpages.map { |p| [p, p.all_subpages] }.flatten
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Returns the actual content of the file minus the optional YAML header.
|
74
|
+
|
75
|
+
def body
|
76
|
+
# TODO: add a test for something with --- without a yaml header.
|
77
|
+
@body ||= begin
|
78
|
+
_, body = Zenweb::Config.split path
|
79
|
+
body.strip
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Returns the parent url of a particular url (or self).
|
85
|
+
|
86
|
+
def parent_url url = self.url
|
87
|
+
url = File.dirname url if File.basename(url) == "index.html"
|
88
|
+
File.join File.dirname(url), "index.html"
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Returns an array of all parent pages of this page, including self.
|
93
|
+
|
94
|
+
def breadcrumbs
|
95
|
+
pages = [self]
|
96
|
+
loop do
|
97
|
+
parent = pages.first.parent
|
98
|
+
break unless parent and parent != pages.first
|
99
|
+
pages.unshift parent
|
100
|
+
end
|
101
|
+
pages.pop # take self back off
|
102
|
+
pages
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Return the url as users normally enter them (ie, no index.html).
|
107
|
+
|
108
|
+
def clean_url
|
109
|
+
url.sub(/\/index.html$/, '/')
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Returns the closest Config instance for this file. That could be
|
114
|
+
# the YAML prefix in the file or it could be a _config.yml file in
|
115
|
+
# the file's directory or above.
|
116
|
+
|
117
|
+
def config
|
118
|
+
unless defined? @config then
|
119
|
+
@config = Config.new site, path
|
120
|
+
@config = @config.parent unless content.start_with? "---"
|
121
|
+
end
|
122
|
+
@config
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns the entire (raw) content of the file.
|
127
|
+
|
128
|
+
def content
|
129
|
+
# TODO: this has the same drawbacks as Config.split
|
130
|
+
@content ||= File.read path
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Returns either:
|
135
|
+
#
|
136
|
+
# + The value of the +date+ config value
|
137
|
+
# + The date embedded in the filename itself (eg: 2012-01-02-blah.html).
|
138
|
+
# + The last modified timestamp of the file itself.
|
139
|
+
|
140
|
+
def date
|
141
|
+
config['date'] || date_from_path || File.stat(path).mtime
|
142
|
+
end
|
143
|
+
|
144
|
+
def date_from_path # :nodoc:
|
145
|
+
date = path[/\d\d\d\d-\d\d-\d\d/]
|
146
|
+
Time.local(*date.split(/-/).map(&:to_i)) if date
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# Returns true if this page has a date (via config or within the path).
|
151
|
+
|
152
|
+
def dated?
|
153
|
+
config['date'] || date_from_path
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Is this a dated page? (ie, does it have YYYY-MM-DD in the path?)
|
158
|
+
|
159
|
+
def dated_path?
|
160
|
+
path[/\d\d\d\d-\d\d-\d\d/]
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Returns true if this is an html page.
|
165
|
+
|
166
|
+
def html?
|
167
|
+
path =~ /\.html/
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Wires up additional dependencies for this Page. +from_deps+ may
|
172
|
+
# be a Hash (eg site.pages), an Array (eg. site.categories.blog),
|
173
|
+
# or a single page.
|
174
|
+
|
175
|
+
def depends_on deps
|
176
|
+
if String === deps then
|
177
|
+
file self.path => deps
|
178
|
+
else
|
179
|
+
deps = deps.values if Hash === deps
|
180
|
+
deps = Array(deps)
|
181
|
+
|
182
|
+
file self.url_path => deps.map(&:url_path) - [self.url_path]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Returns the extension (without the '.') of +name+, defaulting to
|
188
|
+
# +self.path+.
|
189
|
+
|
190
|
+
def filetype name = self.path
|
191
|
+
File.extname(name)[1..-1]
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Returns an array of extensions (in reverse order) of this page
|
196
|
+
# that match known renderers. For example:
|
197
|
+
#
|
198
|
+
# Given renderer methods +render_erb+ and +render_md+, the file
|
199
|
+
# "index.html.md.erb" would return %w[erb md], but the file
|
200
|
+
# "index.html" would return [].
|
201
|
+
#
|
202
|
+
# Additional renderers can be added via Site.load_plugins.
|
203
|
+
|
204
|
+
def filetypes
|
205
|
+
@filetypes ||= path[self.class.renderers_re].split(/\./)[1..-1].reverse
|
206
|
+
rescue
|
207
|
+
[]
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Format a date string +s+ using the config value +date_fmt+ or YYYY/MM/DD.
|
212
|
+
|
213
|
+
def format_date s
|
214
|
+
fmt = self.config["date_fmt"] || "%Y/%m/%d"
|
215
|
+
Time.local(*s.split(/-/).map(&:to_i)).strftime(fmt)
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Render and write the result to #url_path.
|
220
|
+
|
221
|
+
def generate
|
222
|
+
warn "Rendering #{url_path}"
|
223
|
+
|
224
|
+
content = self.render
|
225
|
+
|
226
|
+
open url_path, "w" do |f|
|
227
|
+
f.puts content
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
##
|
232
|
+
# Render a named file from +_includes+.
|
233
|
+
#
|
234
|
+
# category: XXX
|
235
|
+
|
236
|
+
def include name, page
|
237
|
+
incl = Page.new(site, File.join("_includes", name))
|
238
|
+
incl.subrender page
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Returns true if this page is an index page.
|
243
|
+
|
244
|
+
def index?
|
245
|
+
url.end_with? "index.html"
|
246
|
+
end
|
247
|
+
|
248
|
+
def inspect # :nodoc:
|
249
|
+
"Page[#{path.inspect}]"
|
250
|
+
end
|
251
|
+
|
252
|
+
alias :to_s :inspect # :nodoc:
|
253
|
+
|
254
|
+
##
|
255
|
+
# Return a layout Page named in the config key +layout+.
|
256
|
+
#
|
257
|
+
# TODO: expand
|
258
|
+
|
259
|
+
def layout
|
260
|
+
unless defined? @layout then
|
261
|
+
@layout = site.layout self.config["layout"]
|
262
|
+
end
|
263
|
+
@layout
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# Convenience function to create an html link for this page.
|
268
|
+
|
269
|
+
def link_html title = self.title
|
270
|
+
%(<a href="#{clean_url}">#{title}</a>)
|
271
|
+
end
|
272
|
+
|
273
|
+
##
|
274
|
+
# Stupid helper method to make declaring header meta lines cleaner
|
275
|
+
|
276
|
+
def meta key, name=key, label="name"
|
277
|
+
val = self.config[key]
|
278
|
+
%(<meta #{label}="#{name}" content="#{val}">) if val
|
279
|
+
end
|
280
|
+
|
281
|
+
##
|
282
|
+
# Access a config variable and only warn if it isn't accessible.
|
283
|
+
# If +msg+ starts with render, go ahead and pass that up to the
|
284
|
+
# default method_missing.
|
285
|
+
|
286
|
+
def method_missing msg, *args # :nodoc:
|
287
|
+
case msg.to_s
|
288
|
+
when /=|^render_|^to_a(?:ry)?$/ then # to_a/ry for 1.9 only. :(
|
289
|
+
super
|
290
|
+
else
|
291
|
+
self[msg]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Render this page as a whole. This includes rendering the page's
|
297
|
+
# content into a layout if one has been specified via config.
|
298
|
+
|
299
|
+
def render page = self, content = nil
|
300
|
+
content = subrender page, content
|
301
|
+
|
302
|
+
layout = self.layout # TODO: make nullpage to avoid 'if layout' tests
|
303
|
+
content = layout.render page, content if layout
|
304
|
+
|
305
|
+
content
|
306
|
+
end
|
307
|
+
|
308
|
+
##
|
309
|
+
# Render a Page instance based on its filetypes. For example,
|
310
|
+
# index.html.md.erb will essentially call:
|
311
|
+
#
|
312
|
+
# render_md(render_erb(content))
|
313
|
+
|
314
|
+
def subrender page = self, content = nil
|
315
|
+
self.filetypes.inject(content) { |cont, type|
|
316
|
+
send "render_#{type}", page, cont
|
317
|
+
} || self.body
|
318
|
+
end
|
319
|
+
|
320
|
+
# TODO: move this and others to plugins/html_toys.rb (or something)
|
321
|
+
|
322
|
+
def run_js_script url
|
323
|
+
<<-"EOM".gsub(/^ {6}/, '')
|
324
|
+
<script type="text/javascript">
|
325
|
+
(function() {
|
326
|
+
var s = document.createElement('script');
|
327
|
+
s.type = 'text/javascript';
|
328
|
+
s.async = true;
|
329
|
+
s.src = '#{url}';
|
330
|
+
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(s);
|
331
|
+
})();
|
332
|
+
</script>
|
333
|
+
EOM
|
334
|
+
end
|
335
|
+
|
336
|
+
##
|
337
|
+
# Return the url for this page. The url is based entirely on its
|
338
|
+
# location in the file-system.
|
339
|
+
#
|
340
|
+
# TODO: expand
|
341
|
+
|
342
|
+
def url
|
343
|
+
@url ||= self.path.
|
344
|
+
sub(/^/, '/').
|
345
|
+
sub(/(\d\d\d\d)-(\d\d)-(\d\d)-/) { |s| "#{format_date s}/" }.
|
346
|
+
gsub(self.class.renderers_re, '')
|
347
|
+
end
|
348
|
+
|
349
|
+
##
|
350
|
+
# The directory portion of the url.
|
351
|
+
|
352
|
+
def url_dir
|
353
|
+
File.dirname url_path
|
354
|
+
end
|
355
|
+
|
356
|
+
##
|
357
|
+
# The real file path for the generated file.
|
358
|
+
|
359
|
+
def url_path
|
360
|
+
@url_path ||= File.join(".site", self.url)
|
361
|
+
end
|
362
|
+
|
363
|
+
##
|
364
|
+
# Wire up this page to the rest of the rake dependencies. If you
|
365
|
+
# have extra dependencies for this file (ie, an index page that
|
366
|
+
# links to many other pages) you can add them by creating a rake
|
367
|
+
# task named +:extra_wirings+ and using #depends_on. Eg:
|
368
|
+
#
|
369
|
+
# task :extra_wirings do |x|
|
370
|
+
# site = $website
|
371
|
+
# page = site.pages
|
372
|
+
#
|
373
|
+
# page["sitemap.xml.erb"]. depends_on site.html_pages
|
374
|
+
# page["atom.xml.erb"]. depends_on site.pages_by_date.first(30)
|
375
|
+
# page["blog/index.html.erb"].depends_on site.categories.blog
|
376
|
+
# end
|
377
|
+
|
378
|
+
def wire
|
379
|
+
@wired ||= false # HACK
|
380
|
+
return if @wired
|
381
|
+
@wired = true
|
382
|
+
|
383
|
+
file self.path
|
384
|
+
|
385
|
+
conf = self.config
|
386
|
+
conf = conf.parent if self.path == conf.path
|
387
|
+
|
388
|
+
file self.path => conf.path if conf.path
|
389
|
+
conf.wire
|
390
|
+
|
391
|
+
if self.layout then
|
392
|
+
file self.path => self.layout.path
|
393
|
+
self.layout.wire
|
394
|
+
end
|
395
|
+
|
396
|
+
file url_path => all_subpages.map(&:url_path) if url =~ /index.html/
|
397
|
+
|
398
|
+
unless url_dir =~ %r%/_% then
|
399
|
+
directory url_dir
|
400
|
+
file url_path => url_dir
|
401
|
+
file url_path => path do
|
402
|
+
self.generate
|
403
|
+
end
|
404
|
+
|
405
|
+
task :site => url_path
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end # class Page
|
409
|
+
end
|