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.
Files changed (110) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.autotest +18 -0
  3. data/.gemtest +0 -0
  4. data/History.txt +6 -0
  5. data/Manifest.txt +52 -50
  6. data/README.txt +27 -22
  7. data/Rakefile +8 -9
  8. data/example-site/.isolate.rb +8 -0
  9. data/example-site/Rakefile +32 -0
  10. data/example-site/_config.yml +3 -0
  11. data/example-site/_includes/analytics.html.erb +11 -0
  12. data/example-site/_includes/header.html.erb +3 -0
  13. data/example-site/_includes/page_list_item.html +5 -0
  14. data/example-site/_includes/post_list_item.html +5 -0
  15. data/example-site/_layouts/post.erb +13 -0
  16. data/example-site/_layouts/project.erb +39 -0
  17. data/example-site/_layouts/site.erb +62 -0
  18. data/example-site/about/index.html.md +8 -0
  19. data/example-site/atom.xml.erb +43 -0
  20. data/example-site/blog/2012-01-02-page1.html.md +5 -0
  21. data/example-site/blog/2012-01-03-page2.html.md +5 -0
  22. data/example-site/blog/2012-01-04-page3.html.md +5 -0
  23. data/example-site/blog/_config.yml +1 -0
  24. data/example-site/blog/index.html.erb +19 -0
  25. data/example-site/config.ru +33 -0
  26. data/example-site/css/colors.css.less +71 -0
  27. data/example-site/css/styles.css +223 -0
  28. data/example-site/css/syntax.css +171 -0
  29. data/example-site/img/bg.png +0 -0
  30. data/example-site/index.html.erb +27 -0
  31. data/example-site/js/jquery.js +154 -0
  32. data/example-site/js/site.js +37 -0
  33. data/example-site/pages/index.html.erb +17 -0
  34. data/example-site/pages/nonblogpage.html.md +8 -0
  35. data/example-site/projects/index.html.erb +18 -0
  36. data/example-site/projects/zenweb.html.erb +9 -0
  37. data/example-site/sitemap.xml.erb +24 -0
  38. data/lib/zenweb.rb +12 -0
  39. data/lib/zenweb/config.rb +126 -0
  40. data/lib/zenweb/extensions.rb +51 -0
  41. data/lib/zenweb/page.rb +409 -0
  42. data/lib/zenweb/plugins/disqus.rb +18 -0
  43. data/lib/zenweb/plugins/erb.rb +43 -0
  44. data/lib/zenweb/plugins/google.rb +20 -0
  45. data/lib/zenweb/plugins/less.rb +10 -0
  46. data/lib/zenweb/plugins/markdown.rb +121 -0
  47. data/lib/zenweb/site.rb +237 -0
  48. data/lib/zenweb/tasks.rake +165 -0
  49. data/test/helper.rb +53 -0
  50. data/test/test_zenweb_config.rb +90 -0
  51. data/test/test_zenweb_extensions.rb +33 -0
  52. data/test/test_zenweb_page.rb +381 -0
  53. data/test/test_zenweb_plugins_disqus.rb +41 -0
  54. data/test/test_zenweb_plugins_erb.rb +60 -0
  55. data/test/test_zenweb_plugins_google.rb +38 -0
  56. data/test/test_zenweb_plugins_less.rb +33 -0
  57. data/test/test_zenweb_plugins_markdown.rb +227 -0
  58. data/test/test_zenweb_site.rb +294 -0
  59. metadata +230 -86
  60. metadata.gz.sig +0 -0
  61. data/bin/zenweb +0 -27
  62. data/bin/zenwebpage +0 -66
  63. data/bin/zenwebsite +0 -39
  64. data/design/REQUIREMENTS.txt +0 -52
  65. data/design/ZENWEB_2.txt +0 -69
  66. data/design/heirarchy.png +0 -0
  67. data/design/heirarchy.tgif +0 -311
  68. data/docs/Customizing +0 -76
  69. data/docs/FAQ +0 -12
  70. data/docs/Features +0 -128
  71. data/docs/Presentation +0 -88
  72. data/docs/QuickStart +0 -32
  73. data/docs/Renderers +0 -85
  74. data/docs/SiteMap +0 -13
  75. data/docs/YourOwnWebsite +0 -32
  76. data/docs/index +0 -14
  77. data/docs/metadata.txt +0 -10
  78. data/lib/ZenWeb.rb +0 -850
  79. data/lib/ZenWeb/CalendarRenderer.rb +0 -162
  80. data/lib/ZenWeb/CompactRenderer.rb +0 -45
  81. data/lib/ZenWeb/CompositeRenderer.rb +0 -63
  82. data/lib/ZenWeb/FileAttachmentRenderer.rb +0 -57
  83. data/lib/ZenWeb/FooterRenderer.rb +0 -38
  84. data/lib/ZenWeb/GenericRenderer.rb +0 -143
  85. data/lib/ZenWeb/HeaderRenderer.rb +0 -52
  86. data/lib/ZenWeb/HtmlRenderer.rb +0 -81
  87. data/lib/ZenWeb/HtmlTableRenderer.rb +0 -94
  88. data/lib/ZenWeb/HtmlTemplateRenderer.rb +0 -173
  89. data/lib/ZenWeb/MetadataRenderer.rb +0 -83
  90. data/lib/ZenWeb/RelativeRenderer.rb +0 -97
  91. data/lib/ZenWeb/RubyCodeRenderer.rb +0 -56
  92. data/lib/ZenWeb/SitemapRenderer.rb +0 -56
  93. data/lib/ZenWeb/StandardRenderer.rb +0 -40
  94. data/lib/ZenWeb/StupidRenderer.rb +0 -91
  95. data/lib/ZenWeb/SubpageRenderer.rb +0 -45
  96. data/lib/ZenWeb/TextToHtmlRenderer.rb +0 -219
  97. data/lib/ZenWeb/TocRenderer.rb +0 -60
  98. data/lib/ZenWeb/XXXRenderer.rb +0 -32
  99. data/test/SiteMap +0 -14
  100. data/test/Something +0 -4
  101. data/test/include.txt +0 -3
  102. data/test/index +0 -8
  103. data/test/metadata.txt +0 -10
  104. data/test/ryand/SiteMap +0 -10
  105. data/test/ryand/blah +0 -4
  106. data/test/ryand/blah-blah +0 -4
  107. data/test/ryand/index +0 -52
  108. data/test/ryand/metadata.txt +0 -2
  109. data/test/ryand/stuff/index +0 -4
  110. 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
@@ -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