zenweb 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +426 -0
  3. data/Manifest.txt +54 -0
  4. data/README.txt +63 -0
  5. data/Rakefile +22 -0
  6. data/bin/zenweb +27 -0
  7. data/bin/zenwebpage +66 -0
  8. data/bin/zenwebsite +39 -0
  9. data/design/REQUIREMENTS.txt +52 -0
  10. data/design/ZENWEB_2.txt +69 -0
  11. data/design/heirarchy.png +0 -0
  12. data/design/heirarchy.tgif +311 -0
  13. data/docs/Customizing +76 -0
  14. data/docs/FAQ +12 -0
  15. data/docs/Features +128 -0
  16. data/docs/Presentation +88 -0
  17. data/docs/QuickStart +32 -0
  18. data/docs/Renderers +85 -0
  19. data/docs/SiteMap +13 -0
  20. data/docs/YourOwnWebsite +32 -0
  21. data/docs/index +14 -0
  22. data/docs/metadata.txt +10 -0
  23. data/lib/ZenWeb.rb +850 -0
  24. data/lib/ZenWeb/CalendarRenderer.rb +162 -0
  25. data/lib/ZenWeb/CompactRenderer.rb +45 -0
  26. data/lib/ZenWeb/CompositeRenderer.rb +63 -0
  27. data/lib/ZenWeb/FileAttachmentRenderer.rb +57 -0
  28. data/lib/ZenWeb/FooterRenderer.rb +38 -0
  29. data/lib/ZenWeb/GenericRenderer.rb +143 -0
  30. data/lib/ZenWeb/HeaderRenderer.rb +52 -0
  31. data/lib/ZenWeb/HtmlRenderer.rb +81 -0
  32. data/lib/ZenWeb/HtmlTableRenderer.rb +94 -0
  33. data/lib/ZenWeb/HtmlTemplateRenderer.rb +173 -0
  34. data/lib/ZenWeb/MetadataRenderer.rb +83 -0
  35. data/lib/ZenWeb/RelativeRenderer.rb +98 -0
  36. data/lib/ZenWeb/RubyCodeRenderer.rb +56 -0
  37. data/lib/ZenWeb/SitemapRenderer.rb +56 -0
  38. data/lib/ZenWeb/StandardRenderer.rb +40 -0
  39. data/lib/ZenWeb/StupidRenderer.rb +88 -0
  40. data/lib/ZenWeb/SubpageRenderer.rb +45 -0
  41. data/lib/ZenWeb/TextToHtmlRenderer.rb +219 -0
  42. data/lib/ZenWeb/TocRenderer.rb +61 -0
  43. data/lib/ZenWeb/XXXRenderer.rb +32 -0
  44. data/test/SiteMap +14 -0
  45. data/test/Something +4 -0
  46. data/test/include.txt +3 -0
  47. data/test/index +8 -0
  48. data/test/metadata.txt +10 -0
  49. data/test/ryand/SiteMap +10 -0
  50. data/test/ryand/blah +4 -0
  51. data/test/ryand/blah-blah +4 -0
  52. data/test/ryand/index +52 -0
  53. data/test/ryand/metadata.txt +2 -0
  54. data/test/ryand/stuff/index +4 -0
  55. data/test/test_zenweb.rb +1624 -0
  56. metadata +161 -0
  57. metadata.gz.sig +0 -0
@@ -0,0 +1,162 @@
1
+ # this is a simple template. Globally replace Calendar with the name of
2
+ # your renderer and then go fill in YYY with the appropriate content.
3
+
4
+ require 'ZenWeb/GenericRenderer'
5
+ require 'date' # which requires rational
6
+ require 'time'
7
+
8
+ # class Integer
9
+ # alias :slowgcd :gcd
10
+ # def gcd(n)
11
+ # m = abs
12
+ # while n != 0
13
+ # m %= n
14
+ # tmp = m; m = n; n = tmp
15
+ # end
16
+ # m.abs
17
+ # end
18
+ # end
19
+
20
+ # require 'tally'
21
+ # class Integer
22
+ # tally :gcd, true
23
+ # end
24
+
25
+ =begin
26
+
27
+ = Class CalendarRenderer
28
+
29
+ DOC
30
+
31
+ === Methods
32
+
33
+ =end
34
+
35
+ class CalendarRenderer < GenericRenderer
36
+
37
+ DAYS_IN_MONTH = [
38
+ [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
39
+ [nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
40
+ ]
41
+
42
+ =begin
43
+
44
+ --- CalendarRenderer#render(content)
45
+
46
+ DOC
47
+
48
+ =end
49
+
50
+ def render(content)
51
+ events = Hash.new { |h,k| h[k] = [] }
52
+ reverse = false
53
+ self.scan_region(content, /<cal/i, /<\/cal>/i) do |line, context|
54
+ case context
55
+ when :START then
56
+ reverse = true if line =~ /<cal\s+reverse\s*>/i
57
+ when :END then
58
+ self.generate_calendars(events, reverse)
59
+ else
60
+ if line =~ /(\d\d\d\d-\d\d-\d\d):\s*(.*)/ then
61
+ description = $2
62
+ time = Time.parse($1) # NOT Date! hellishly slow!
63
+ events[$1] << description
64
+ end
65
+ end
66
+ end
67
+
68
+ return self.result
69
+ end
70
+
71
+ def generate_calendar(year, month, events)
72
+
73
+ current_events = []
74
+
75
+ which_year = Date.leap?(year) ? 1 : 0
76
+ date_start = Date.civil(year, month, 1)
77
+ last_day = DAYS_IN_MONTH[which_year][month]
78
+ date_end = Date.civil(year, month, -1)
79
+
80
+ push "<table class=\"calendar\">"
81
+ push "<tr>"
82
+ push "<td valign=\"top\">" # calendar
83
+
84
+ m2 = "%02d" % month
85
+ push "<table class=\"view y#{year} m#{m2}\">\n"
86
+
87
+ long_month_name = Date::MONTHNAMES[month]
88
+ month_name = Date::ABBR_MONTHNAMES[month].downcase
89
+
90
+ push "<tr class=\"title\">\n"
91
+ push "<th colspan=7>#{long_month_name} #{year}</th>\n"
92
+ push "</tr>\n"
93
+
94
+ push "<tr class=\"weektitle\"\n"
95
+ push Date::ABBR_DAYNAMES.map { |d| "<th class=\"#{d.downcase}\">#{d}</th>" }.join("\n")
96
+ push "</tr>\n"
97
+
98
+ dow_start = date_start.wday
99
+ push "<tr class=\"days firstweek\">\n"
100
+ push "<td colspan=#{dow_start}>&nbsp;</td>\n" unless dow_start == 0
101
+
102
+ last_sunday = date_end.day - date_end.wday + 1
103
+
104
+ week = 1
105
+ wday = dow_start
106
+
107
+ day_last = date_end.day
108
+
109
+ 1.upto(day_last) do |day|
110
+
111
+ event=""
112
+ cal = Time.local(year, month, day).strftime("%Y-%m-%d")
113
+ if events.has_key? cal then
114
+ current_events << "<li>#{cal}:\n<ul>\n"
115
+ events[cal].each do |description|
116
+ current_events << "<li>#{description}\n"
117
+ end
118
+ current_events << "</ul>\n"
119
+ event=" event"
120
+ end
121
+
122
+ dow = Date::ABBR_DAYNAMES[(day + dow_start - 1) % 7].downcase
123
+ d2 = "%02d" % day
124
+ push "<td class=\"d#{d2} #{dow}#{event}\">#{day}</td>\n"
125
+
126
+ if day != day_last and wday == 6 then
127
+ push "</tr>\n"
128
+ unless day == last_sunday then
129
+ push "<tr class=\"days\">\n"
130
+ else
131
+ push "<tr class=\"days lastweek\">\n"
132
+ end
133
+ week += 1
134
+ end
135
+
136
+ wday += 1
137
+ wday = 0 if wday >= 7 # remember 0..6
138
+ end
139
+
140
+ day_count = 7-date_end.wday-1
141
+ push "<td colspan=#{day_count}>&nbsp;</td>\n" unless day_count == 0
142
+ push "</tr>\n"
143
+ push "</table>\n"
144
+ push "</td>\n" # /calendar
145
+ push "<td class=\"eventlist\">\n" # events
146
+ push "<ul>\n"
147
+ push current_events.join('')
148
+ push "</ul>\n"
149
+ push "</td>\n" # /events
150
+ push "</tr>\n"
151
+ push "</table>\n"
152
+ end
153
+
154
+ def generate_calendars(events, should_reverse=false)
155
+ active_months = events.keys.map { |d| d[0..6] }.sort.uniq
156
+ active_months = active_months.reverse if should_reverse
157
+ active_months.each do |ym|
158
+ year, month = ym.split(/-/).map { |n| n.to_i }
159
+ self.generate_calendar(year, month, events)
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,45 @@
1
+ #
2
+ #
3
+
4
+ require 'ZenWeb/GenericRenderer'
5
+
6
+ =begin
7
+
8
+ = Class CompactRenderer
9
+
10
+ CompactRenderer compacts HTML whitespace and comments to minimize how much text gets served.
11
+
12
+ === Methods
13
+
14
+ =end
15
+
16
+ class CompactRenderer < GenericRenderer
17
+
18
+ =begin
19
+
20
+ --- CompactRenderer#render(content)
21
+
22
+ Compacts HTML blah blah DOC
23
+
24
+ =end
25
+
26
+ def render(content)
27
+
28
+ # protect pre blocks
29
+ content = content.gsub(/(<pre>)([\s\S]*?)(<\/pre>)/i) do
30
+ p1 = $1
31
+ body = $2
32
+ p2 = $3
33
+ body = body.gsub(/\n/, 1.chr).gsub(/ /, 2.chr)
34
+ p1 + body + p2
35
+ end
36
+
37
+ content = content.gsub(/\n\r/, '').gsub(/\s+/, ' ').gsub(/> </, '><').strip
38
+ content = content.gsub(1.chr, "\n").gsub(2.chr, " ")
39
+
40
+ push(content)
41
+
42
+ return self.result
43
+ end
44
+
45
+ end
@@ -0,0 +1,63 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ =begin
4
+
5
+ = Class CompositeRenderer
6
+
7
+ Allows multiple renderers to be plugged into a single renderer.
8
+
9
+ === Methods
10
+
11
+ =end
12
+
13
+ class CompositeRenderer < GenericRenderer
14
+
15
+ attr_reader :renderers
16
+
17
+ =begin
18
+
19
+ --- CompositeRenderer#new(document)
20
+
21
+ Creates a new CompositeRenderer.
22
+
23
+ =end
24
+
25
+ def initialize(document)
26
+ super(document)
27
+ @renderers = []
28
+ end
29
+
30
+ =begin
31
+
32
+ --- CompositeRenderer#render(content)
33
+
34
+ Renders by running all of the renderers in sequence.
35
+
36
+ =end
37
+
38
+ def render(content)
39
+ @renderers.each { | renderer |
40
+ content = renderer.render(content)
41
+ }
42
+ return content
43
+ end
44
+
45
+ =begin
46
+
47
+ --- CompositeRenderer#addRenderer(renderer)
48
+
49
+ Adds renderer to the list of renderers used by this composite.
50
+
51
+ =end
52
+
53
+ def addRenderer(renderer)
54
+
55
+ if (renderer.nil? or ! renderer.kind_of? GenericRenderer) then
56
+ raise ArgumentError, "You may only add an instance of GenericRenderer"
57
+ end
58
+
59
+ @renderers.push(renderer)
60
+ end
61
+
62
+ end
63
+
@@ -0,0 +1,57 @@
1
+ # this is a simple template. Globally replace FileAttachment with the name of
2
+ # your renderer and then go fill in YYY with the appropriate content.
3
+
4
+ require 'ZenWeb/GenericRenderer'
5
+
6
+ =begin
7
+
8
+ = Class FileAttachmentRenderer
9
+
10
+ Finds content between <file name="name">...</file> tags. Writes the
11
+ content to a file of the given name and includes a link to the file.
12
+
13
+ === Methods
14
+
15
+ =end
16
+
17
+ class FileAttachmentRenderer < GenericRenderer
18
+
19
+ =begin
20
+
21
+ --- FileAttachmentRenderer#render(content)
22
+
23
+ YYY
24
+
25
+ =end
26
+
27
+ def render(content)
28
+
29
+ start_re = /<file\s+name\s*=\s*\"([\w\.-]+)\"\s*>/i
30
+ end_re = /<\/file>/i
31
+
32
+ file_content = []
33
+ name = nil
34
+ self.scan_region(content, start_re, end_re) do |line, context|
35
+ case context
36
+ when :START then
37
+ name = $1 if line =~ /name\s*=\s*\"([\w\.-]+)\"/i
38
+ when :END then
39
+ raise "name is undefined, add name= attribute" if name.nil?
40
+ dir = File.dirname @document.htmlpath
41
+ path = File.join(dir, name)
42
+ push "\n"
43
+ push "<A HREF=\"#{name}\">Download #{name}</A>\n"
44
+ File.open(path, "w") do |file|
45
+ file.print file_content.join('')
46
+ end
47
+ file_content = []
48
+ else
49
+ file_content.push line
50
+ push " " + line
51
+ end
52
+ end
53
+
54
+ return self.result.strip
55
+ end
56
+
57
+ end
@@ -0,0 +1,38 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ =begin
4
+
5
+ = Class FooterRenderer
6
+
7
+ Inserts a footer based on metadata.
8
+
9
+ === Methods
10
+
11
+ =end
12
+
13
+ class FooterRenderer < GenericRenderer
14
+
15
+ =begin
16
+
17
+ --- FooterRenderer#render(content)
18
+
19
+ Adds a footer if the ((|footer|)) metadata item exists. If the
20
+ document contains a BODY close HTML tag, then the footer
21
+ immediately precedes it, otherwise it is simply at the bottom.
22
+
23
+ =end
24
+
25
+ def render(content)
26
+
27
+ footer = @document['footer'] || nil
28
+
29
+ if footer then
30
+ content.sub!(/(<\/BODY>|\z)/i) {
31
+ footer + $1
32
+ }
33
+ end
34
+
35
+ return content
36
+ end
37
+ end
38
+
@@ -0,0 +1,143 @@
1
+ $TESTING = FALSE unless defined? $TESTING
2
+
3
+ =begin
4
+
5
+ = Class GenericRenderer
6
+
7
+ A GenericRenderer provides an interface for all renderers. It renders
8
+ nothing itself.
9
+
10
+ === Methods
11
+
12
+ =end
13
+
14
+ class GenericRenderer
15
+
16
+ =begin
17
+
18
+ --- GenericRenderer.new(document)
19
+
20
+ Instantiates a generic renderer with a reference to
21
+ ((|document|)), it\'s website and sitemap.
22
+
23
+ =end
24
+
25
+ # REFACTOR: do not take a document at all
26
+ def initialize(document)
27
+ @document = document
28
+ @website = @document.website
29
+ @sitemap = @website.sitemap
30
+ @result = []
31
+ end
32
+
33
+ =begin
34
+
35
+ --- GenericRenderer#push(obj)
36
+
37
+ Pushes a string representation of ((|obj|)) onto the result
38
+ array. If ((|obj|)) is an array, it iterates each item and pushes
39
+ them (recursively). If it is not an array, it pushes (({obj.to_s})).
40
+
41
+ =end
42
+
43
+ def push(obj)
44
+ if obj.is_a?(Array) then
45
+ stuff = obj.flatten
46
+ @result.push(*stuff) unless stuff.empty?
47
+ else
48
+ if false then
49
+ @result.push(obj.to_s)
50
+ else
51
+ @result.push(obj)
52
+ end
53
+ end
54
+ end
55
+
56
+ =begin
57
+
58
+ --- GenericRenderer#unshift(obj)
59
+
60
+ Same as ((<GenericRenderer#push>)) but prepends instead of appends.
61
+
62
+ =end
63
+
64
+ def unshift(obj)
65
+ if obj.is_a?(Array) then
66
+ obj.reverse.each { | item |
67
+ self.unshift(item)
68
+ }
69
+ else
70
+ @result.unshift(obj.to_s)
71
+ end
72
+
73
+ end
74
+
75
+ # DOC GenericRenderer#result
76
+ def result(clear=true)
77
+ result = @result.join('')
78
+ @result = [] if clear
79
+ return result
80
+ end
81
+
82
+ =begin
83
+
84
+ --- GenericRenderer#render(content)
85
+
86
+ Renders the content. Does nothing in GenericRenderer, but is
87
+ expected to be overridden by subclasses. ((|content|)) is an array
88
+ of strings and render must return an array of strings.
89
+
90
+ NEW: the argument and result are now a single string!
91
+
92
+ =end
93
+
94
+ # REFACTOR: pass in content and document to render
95
+ def render(content)
96
+ return content
97
+ end
98
+
99
+ def each_paragraph(content)
100
+ content.scan(/.+?(?:#{$/}#{$/}+|\Z)/m) do |block|
101
+ $stderr.puts "BLOCK = #{block.inspect}" if $DEBUG
102
+ yield(block)
103
+ end
104
+ end
105
+
106
+ def each_paragraph_matching(content, pattern)
107
+ $stderr.puts "CONTENT = #{content.inspect}" if $DEBUG
108
+ self.each_paragraph(content) do |block|
109
+ $stderr.puts "PARAGRAPH = #{block.inspect}" if $DEBUG
110
+ if block =~ pattern then
111
+ yield(block)
112
+ else
113
+ push block
114
+ end
115
+ end
116
+ end
117
+
118
+ def scan_region(content, region_start, region_end)
119
+ matching = false
120
+ content.scan(/.*#{$/}?/) do |l|
121
+ # TODO: detect region_end w/o start and freak
122
+ # TODO: detect nesting and freak
123
+ if l =~ region_start then
124
+ matching = true
125
+ $stderr.puts :START, l.inspect if $DEBUG
126
+ yield(l, :START)
127
+ matching = false if l =~ region_end
128
+ elsif l =~ region_end then
129
+ matching = false
130
+ $stderr.puts :END, l.inspect if $DEBUG
131
+ yield(l, :END)
132
+ elsif matching then
133
+ $stderr.puts :MIDDLE, l.inspect if $DEBUG
134
+ yield(l, :MIDDLE)
135
+ else
136
+ $stderr.puts :IGNORED, l.inspect if $DEBUG
137
+ push l
138
+ end
139
+ end
140
+ end
141
+
142
+ end
143
+