jamesgolick-webby 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. data/History.txt +219 -0
  2. data/README.rdoc +92 -0
  3. data/Rakefile +62 -0
  4. data/bin/webby +41 -0
  5. data/bin/webby-gen +41 -0
  6. data/examples/blog/Sitefile +7 -0
  7. data/examples/blog/tasks/blog.rake +72 -0
  8. data/examples/blog/templates/atom_feed.erb +40 -0
  9. data/examples/blog/templates/blog/month.erb +22 -0
  10. data/examples/blog/templates/blog/post.erb +16 -0
  11. data/examples/blog/templates/blog/year.erb +22 -0
  12. data/examples/presentation/Sitefile +10 -0
  13. data/examples/presentation/content/css/uv/twilight.css +137 -0
  14. data/examples/presentation/content/presentation/_sample_code.txt +10 -0
  15. data/examples/presentation/content/presentation/index.txt +63 -0
  16. data/examples/presentation/content/presentation/s5/blank.gif +0 -0
  17. data/examples/presentation/content/presentation/s5/bodybg.gif +0 -0
  18. data/examples/presentation/content/presentation/s5/framing.css +23 -0
  19. data/examples/presentation/content/presentation/s5/iepngfix.htc +42 -0
  20. data/examples/presentation/content/presentation/s5/opera.css +7 -0
  21. data/examples/presentation/content/presentation/s5/outline.css +15 -0
  22. data/examples/presentation/content/presentation/s5/pretty.css +86 -0
  23. data/examples/presentation/content/presentation/s5/print.css +25 -0
  24. data/examples/presentation/content/presentation/s5/s5-core.css +9 -0
  25. data/examples/presentation/content/presentation/s5/slides.css +3 -0
  26. data/examples/presentation/content/presentation/s5/slides.js +553 -0
  27. data/examples/presentation/layouts/presentation.txt +43 -0
  28. data/examples/presentation/templates/_code_partial.erb +13 -0
  29. data/examples/presentation/templates/presentation.erb +40 -0
  30. data/examples/tumblog/Sitefile +9 -0
  31. data/examples/tumblog/content/css/tumblog.css +308 -0
  32. data/examples/tumblog/content/images/tumblog/permalink.gif +0 -0
  33. data/examples/tumblog/content/images/tumblog/rss.gif +0 -0
  34. data/examples/tumblog/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  35. data/examples/tumblog/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  36. data/examples/tumblog/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  37. data/examples/tumblog/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  38. data/examples/tumblog/content/tumblog/200807/up-a-tree/index.txt +13 -0
  39. data/examples/tumblog/content/tumblog/index.txt +37 -0
  40. data/examples/tumblog/content/tumblog/rss.txt +37 -0
  41. data/examples/tumblog/layouts/tumblog/default.txt +44 -0
  42. data/examples/tumblog/layouts/tumblog/post.txt +15 -0
  43. data/examples/tumblog/lib/tumblog_helper.rb +32 -0
  44. data/examples/tumblog/tasks/tumblog.rake +30 -0
  45. data/examples/tumblog/templates/atom_feed.erb +40 -0
  46. data/examples/tumblog/templates/tumblog/conversation.erb +12 -0
  47. data/examples/tumblog/templates/tumblog/link.erb +10 -0
  48. data/examples/tumblog/templates/tumblog/photo.erb +13 -0
  49. data/examples/tumblog/templates/tumblog/post.erb +12 -0
  50. data/examples/tumblog/templates/tumblog/quote.erb +11 -0
  51. data/examples/webby/Sitefile +19 -0
  52. data/examples/webby/content/communicate/index.txt +28 -0
  53. data/examples/webby/content/css/background.gif +0 -0
  54. data/examples/webby/content/css/blueprint/print.css +76 -0
  55. data/examples/webby/content/css/blueprint/screen.css +696 -0
  56. data/examples/webby/content/css/coderay.css +96 -0
  57. data/examples/webby/content/css/site.css +196 -0
  58. data/examples/webby/content/css/uv/twilight.css +137 -0
  59. data/examples/webby/content/index.txt +37 -0
  60. data/examples/webby/content/learn/index.txt +28 -0
  61. data/examples/webby/content/reference/index.txt +204 -0
  62. data/examples/webby/content/release-notes/index.txt +21 -0
  63. data/examples/webby/content/release-notes/rel-0-9-0/index.txt +74 -0
  64. data/examples/webby/content/release-notes/rel-0-9-1/index.txt +93 -0
  65. data/examples/webby/content/release-notes/rel-0-9-2/index.txt +14 -0
  66. data/examples/webby/content/release-notes/rel-0-9-3/index.txt +47 -0
  67. data/examples/webby/content/robots.txt +6 -0
  68. data/examples/webby/content/script/jquery.corner.js +152 -0
  69. data/examples/webby/content/script/jquery.js +31 -0
  70. data/examples/webby/content/sitemap.txt +31 -0
  71. data/examples/webby/content/tips_and_tricks/index.txt +97 -0
  72. data/examples/webby/content/tutorial/index.txt +135 -0
  73. data/examples/webby/content/user-manual/index.txt +419 -0
  74. data/examples/webby/layouts/default.txt +49 -0
  75. data/examples/webby/templates/page.erb +10 -0
  76. data/examples/website/Sitefile +7 -0
  77. data/examples/website/content/css/blueprint/ie.css +26 -0
  78. data/examples/website/content/css/blueprint/plugins/buttons/icons/cross.png +0 -0
  79. data/examples/website/content/css/blueprint/plugins/buttons/icons/key.png +0 -0
  80. data/examples/website/content/css/blueprint/plugins/buttons/icons/tick.png +0 -0
  81. data/examples/website/content/css/blueprint/plugins/buttons/readme.txt +32 -0
  82. data/examples/website/content/css/blueprint/plugins/buttons/screen.css +97 -0
  83. data/examples/website/content/css/blueprint/plugins/fancy-type/readme.txt +14 -0
  84. data/examples/website/content/css/blueprint/plugins/fancy-type/screen.css +71 -0
  85. data/examples/website/content/css/blueprint/plugins/link-icons/icons/doc.png +0 -0
  86. data/examples/website/content/css/blueprint/plugins/link-icons/icons/email.png +0 -0
  87. data/examples/website/content/css/blueprint/plugins/link-icons/icons/external.png +0 -0
  88. data/examples/website/content/css/blueprint/plugins/link-icons/icons/feed.png +0 -0
  89. data/examples/website/content/css/blueprint/plugins/link-icons/icons/im.png +0 -0
  90. data/examples/website/content/css/blueprint/plugins/link-icons/icons/pdf.png +0 -0
  91. data/examples/website/content/css/blueprint/plugins/link-icons/icons/visited.png +0 -0
  92. data/examples/website/content/css/blueprint/plugins/link-icons/icons/xls.png +0 -0
  93. data/examples/website/content/css/blueprint/plugins/link-icons/readme.txt +18 -0
  94. data/examples/website/content/css/blueprint/plugins/link-icons/screen.css +40 -0
  95. data/examples/website/content/css/blueprint/plugins/rtl/readme.txt +10 -0
  96. data/examples/website/content/css/blueprint/plugins/rtl/screen.css +109 -0
  97. data/examples/website/content/css/blueprint/print.css +30 -0
  98. data/examples/website/content/css/blueprint/screen.css +251 -0
  99. data/examples/website/content/css/blueprint/src/forms.css +49 -0
  100. data/examples/website/content/css/blueprint/src/grid.css +212 -0
  101. data/examples/website/content/css/blueprint/src/grid.png +0 -0
  102. data/examples/website/content/css/blueprint/src/ie.css +59 -0
  103. data/examples/website/content/css/blueprint/src/print.css +85 -0
  104. data/examples/website/content/css/blueprint/src/reset.css +38 -0
  105. data/examples/website/content/css/blueprint/src/typography.css +105 -0
  106. data/examples/website/content/css/coderay.css +111 -0
  107. data/examples/website/content/css/site.css +67 -0
  108. data/examples/website/content/index.txt +19 -0
  109. data/examples/website/layouts/default.txt +61 -0
  110. data/examples/website/lib/breadcrumbs.rb +28 -0
  111. data/examples/website/templates/_partial.erb +10 -0
  112. data/examples/website/templates/page.erb +18 -0
  113. data/lib/webby.rb +233 -0
  114. data/lib/webby/apps.rb +12 -0
  115. data/lib/webby/apps/generator.rb +276 -0
  116. data/lib/webby/apps/main.rb +258 -0
  117. data/lib/webby/auto_builder.rb +157 -0
  118. data/lib/webby/builder.rb +172 -0
  119. data/lib/webby/core_ext/enumerable.rb +11 -0
  120. data/lib/webby/core_ext/hash.rb +28 -0
  121. data/lib/webby/core_ext/kernel.rb +26 -0
  122. data/lib/webby/core_ext/string.rb +163 -0
  123. data/lib/webby/core_ext/time.rb +9 -0
  124. data/lib/webby/filters.rb +85 -0
  125. data/lib/webby/filters/basepath.rb +97 -0
  126. data/lib/webby/filters/erb.rb +9 -0
  127. data/lib/webby/filters/haml.rb +18 -0
  128. data/lib/webby/filters/markdown.rb +16 -0
  129. data/lib/webby/filters/maruku.rb +16 -0
  130. data/lib/webby/filters/outline.rb +309 -0
  131. data/lib/webby/filters/sass.rb +17 -0
  132. data/lib/webby/filters/slides.rb +56 -0
  133. data/lib/webby/filters/textile.rb +16 -0
  134. data/lib/webby/filters/tidy.rb +76 -0
  135. data/lib/webby/filters/wiki_words.rb +14 -0
  136. data/lib/webby/helpers.rb +30 -0
  137. data/lib/webby/helpers/capture_helper.rb +141 -0
  138. data/lib/webby/helpers/coderay_helper.rb +69 -0
  139. data/lib/webby/helpers/graphviz_helper.rb +136 -0
  140. data/lib/webby/helpers/tag_helper.rb +65 -0
  141. data/lib/webby/helpers/tex_img_helper.rb +133 -0
  142. data/lib/webby/helpers/ultraviolet_helper.rb +63 -0
  143. data/lib/webby/helpers/url_helper.rb +241 -0
  144. data/lib/webby/journal.rb +126 -0
  145. data/lib/webby/link_validator.rb +160 -0
  146. data/lib/webby/renderer.rb +390 -0
  147. data/lib/webby/resources.rb +137 -0
  148. data/lib/webby/resources/db.rb +251 -0
  149. data/lib/webby/resources/layout.rb +54 -0
  150. data/lib/webby/resources/meta_file.rb +211 -0
  151. data/lib/webby/resources/page.rb +81 -0
  152. data/lib/webby/resources/partial.rb +85 -0
  153. data/lib/webby/resources/resource.rb +201 -0
  154. data/lib/webby/resources/static.rb +36 -0
  155. data/lib/webby/stelan/mktemp.rb +135 -0
  156. data/lib/webby/stelan/paginator.rb +165 -0
  157. data/lib/webby/tasks/build.rake +27 -0
  158. data/lib/webby/tasks/create.rake +25 -0
  159. data/lib/webby/tasks/deploy.rake +22 -0
  160. data/lib/webby/tasks/growl.rake +16 -0
  161. data/lib/webby/tasks/validate.rake +19 -0
  162. data/spec/core_ext/hash_spec.rb +47 -0
  163. data/spec/core_ext/string_spec.rb +110 -0
  164. data/spec/core_ext/time_spec.rb +19 -0
  165. data/spec/data/hooligans/bad_meta_data_1.txt +34 -0
  166. data/spec/data/hooligans/bad_meta_data_2.txt +34 -0
  167. data/spec/data/html/anchor.html +11 -0
  168. data/spec/data/html/external.html +10 -0
  169. data/spec/data/html/invalid-relative.html +10 -0
  170. data/spec/data/html/relative-anchor.html +10 -0
  171. data/spec/data/html/relative-invalid-anchor.html +10 -0
  172. data/spec/data/html/relative.html +10 -0
  173. data/spec/data/outline/basic.out +81 -0
  174. data/spec/data/outline/basic.txt +25 -0
  175. data/spec/data/outline/no_clobber.out +86 -0
  176. data/spec/data/outline/numbering.out +81 -0
  177. data/spec/data/outline/numbering_only.out +21 -0
  178. data/spec/data/outline/toc_range_1.out +66 -0
  179. data/spec/data/outline/toc_range_2.out +55 -0
  180. data/spec/data/outline/toc_style.out +81 -0
  181. data/spec/data/site/Sitefile +9 -0
  182. data/spec/data/site/content/_partial.txt +10 -0
  183. data/spec/data/site/content/css/coderay.css +111 -0
  184. data/spec/data/site/content/css/site.css +67 -0
  185. data/spec/data/site/content/css/tumblog.css +308 -0
  186. data/spec/data/site/content/images/tumblog/permalink.gif +0 -0
  187. data/spec/data/site/content/images/tumblog/rss.gif +0 -0
  188. data/spec/data/site/content/index.txt +19 -0
  189. data/spec/data/site/content/photos.txt +21 -0
  190. data/spec/data/site/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
  191. data/spec/data/site/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
  192. data/spec/data/site/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
  193. data/spec/data/site/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
  194. data/spec/data/site/content/tumblog/200807/up-a-tree/index.txt +13 -0
  195. data/spec/data/site/content/tumblog/index.txt +37 -0
  196. data/spec/data/site/content/tumblog/rss.txt +37 -0
  197. data/spec/data/site/layouts/default.txt +58 -0
  198. data/spec/data/site/layouts/tumblog/default.txt +44 -0
  199. data/spec/data/site/layouts/tumblog/post.txt +15 -0
  200. data/spec/data/site/lib/breadcrumbs.rb +28 -0
  201. data/spec/data/site/lib/tumblog_helper.rb +32 -0
  202. data/spec/data/site/tasks/tumblog.rake +30 -0
  203. data/spec/data/site/templates/_partial.erb +10 -0
  204. data/spec/data/site/templates/atom_feed.erb +40 -0
  205. data/spec/data/site/templates/page.erb +18 -0
  206. data/spec/data/site/templates/presentation.erb +40 -0
  207. data/spec/data/site/templates/tumblog/conversation.erb +12 -0
  208. data/spec/data/site/templates/tumblog/link.erb +10 -0
  209. data/spec/data/site/templates/tumblog/photo.erb +13 -0
  210. data/spec/data/site/templates/tumblog/post.erb +12 -0
  211. data/spec/data/site/templates/tumblog/quote.erb +11 -0
  212. data/spec/spec.opts +1 -0
  213. data/spec/spec_helper.rb +58 -0
  214. data/spec/webby/apps/generator_spec.rb +117 -0
  215. data/spec/webby/apps/main_spec.rb +88 -0
  216. data/spec/webby/filters/basepath_spec.rb +167 -0
  217. data/spec/webby/filters/maruku_spec.rb +31 -0
  218. data/spec/webby/filters/outline_spec.rb +92 -0
  219. data/spec/webby/filters/textile_spec.rb +31 -0
  220. data/spec/webby/helpers/capture_helper_spec.rb +56 -0
  221. data/spec/webby/link_validator_spec.rb +154 -0
  222. data/spec/webby/renderer_spec.rb +139 -0
  223. data/spec/webby/resources/db_spec.rb +250 -0
  224. data/spec/webby/resources/layout_spec.rb +83 -0
  225. data/spec/webby/resources/meta_file_spec.rb +171 -0
  226. data/spec/webby/resources/page_spec.rb +111 -0
  227. data/spec/webby/resources/partial_spec.rb +58 -0
  228. data/spec/webby/resources/resource_spec.rb +219 -0
  229. data/spec/webby/resources/static_spec.rb +49 -0
  230. data/spec/webby/resources_spec.rb +69 -0
  231. data/tasks/mswin32.rake +38 -0
  232. data/tasks/website.rake +37 -0
  233. metadata +435 -0
@@ -0,0 +1,160 @@
1
+ require 'hpricot'
2
+ require 'open-uri'
3
+
4
+ module Webby
5
+
6
+ # The Webby LinkValidator class is used to validate the hyperlinks of all
7
+ # the HTML files in the output directory. By default, only links to other
8
+ # pages in the output directory are checked. However, setting the
9
+ # :external flag to +true+ will cause hyperlinks to external web sites to
10
+ # be validated as well.
11
+ #
12
+ class LinkValidator
13
+
14
+ # A lazy man's method that will instantiate a new link validator and run
15
+ # the validations.
16
+ #
17
+ def self.validate( opts = {} )
18
+ new(opts).validate
19
+ end
20
+
21
+ attr_accessor :validate_externals
22
+
23
+ # call-seq:
24
+ # LinkValidator.new( opts = {} )
25
+ #
26
+ # Creates a new LinkValidator object. The only supported option is the
27
+ # :external flag. When set to +true+, the link validator will also check
28
+ # out links to external websites. This is done by opening a connection to
29
+ # the remote site and pulling down the page specified in the hyperlink.
30
+ # Use with caution.
31
+ #
32
+ def initialize( opts = {} )
33
+ @log = Logging::Logger[self]
34
+
35
+ glob = ::File.join(::Webby.site.output_dir, '**', '*.html')
36
+ @files = Dir.glob(glob).sort
37
+ @attr_rgxp = %r/\[@(\w+)\]$/o
38
+
39
+ @validate_externals = opts.getopt(:external, false)
40
+ @valid_uris = ::Webby.site.valid_uris.flatten
41
+ @invalid_uris = []
42
+ end
43
+
44
+ # Iterate over all the HTML files in the output directory and validate the
45
+ # hyperlinks.
46
+ #
47
+ def validate
48
+ @files.each {|fn| check_file fn}
49
+ end
50
+
51
+ # Check the given file (identified by its filename {fn for short here}) by
52
+ # iterating through all the configured xpaths and validating that those
53
+ # hyperlinks ae valid.
54
+ #
55
+ def check_file( fn )
56
+ @log.info "validating #{fn}"
57
+
58
+ dir = ::File.dirname(fn)
59
+ doc = Hpricot(::File.read(fn))
60
+
61
+ ::Webby.site.xpaths.each do |xpath|
62
+ @attr_name = nil
63
+
64
+ doc.search(xpath).each do |element|
65
+ attr_name ||= @attr_rgxp.match(xpath)[1]
66
+ uri = URI.parse(element.get_attribute(attr_name))
67
+ validate_uri(uri, dir, doc)
68
+ end
69
+ end
70
+ return nil # side effect of original implementation
71
+ end
72
+
73
+ # Validate the the page the _uri_ refers to actually exists. The directory
74
+ # of the current page being processed is needed in order to resolve
75
+ # relative paths.
76
+ #
77
+ # If the _uri_ is a relative path, then the output directory is searched
78
+ # for the appropriate page. If the _uri_ is an absolute path, then the
79
+ # remote server is contacted and the page requested from the server. This
80
+ # will only take place if the LinkValidator was created with the :external
81
+ # flag set to true.
82
+ #
83
+ def validate_uri( uri, dir, doc )
84
+ # do not retry external uris that have already been validated
85
+ return if @valid_uris.include? uri.to_s
86
+
87
+ return validate_relative_uri(uri, dir, doc) if uri.relative?
88
+ return validate_external_uri(uri, dir, doc) if uri.respond_to? :open
89
+
90
+ # otherwise, post a warning that the URI could not be validated
91
+ @log.warn "could not validate URI '#{uri.to_s}'"
92
+ end
93
+
94
+ # Validate that the anchor fragment of the URI exists in the given
95
+ # document. The document is an Hpricot document object.
96
+ #
97
+ # Returns +true+ if the anchor exists in the document and +false+ if it
98
+ # does not.
99
+ #
100
+ def validate_anchor( uri, doc )
101
+ return false if uri.fragment.nil?
102
+
103
+ anchor = '#' + uri.fragment
104
+ if doc.at(anchor).nil?
105
+ @log.error "invalid URI '#{uri.to_s}'"
106
+ false
107
+ else true end
108
+ end
109
+
110
+ # Validate that the file pointed to by the relative URI exists in the output
111
+ # directory. If the URI has an anchor, validate that the anchor exists as
112
+ # well.
113
+ #
114
+ def validate_relative_uri( uri, dir, doc )
115
+ return validate_anchor(uri, doc) if uri.path.empty?
116
+
117
+ path = if uri.path =~ %r/^\//
118
+ ::File.join(::Webby.site.output_dir, uri.path)
119
+ else
120
+ ::File.join(dir, uri.path)
121
+ end
122
+ path = ::File.join(path, 'index.html') if ::File.extname(path).empty?
123
+
124
+ uri_str = path.dup
125
+ (uri_str << '#' << uri.fragment) if uri.fragment
126
+ return if @valid_uris.include? uri_str
127
+
128
+ if test ?f, path
129
+ valid = if uri.fragment
130
+ validate_anchor(uri, Hpricot(::File.read(path)))
131
+ else true end
132
+ @valid_uris << uri_str if valid
133
+ else
134
+ @log.error "invalid URI '#{uri.to_s}'"
135
+ end
136
+ end
137
+
138
+ # Validate that an external URI can be opened
139
+ #
140
+ def validate_external_uri( uri, dir, doc )
141
+ return unless @validate_externals
142
+
143
+ if @invalid_uris.include? uri.to_s
144
+ @log.error "could not open URI '#{uri.to_s}'"
145
+ return
146
+ end
147
+
148
+ begin
149
+ uri.open {|_| nil}
150
+ @valid_uris << uri.to_s
151
+ rescue Exception
152
+ @log.error "could not open URI '#{uri.to_s}'"
153
+ @invalid_uris << uri.to_s
154
+ end
155
+ end
156
+
157
+ end # class LinkValidator
158
+ end # module Webby
159
+
160
+ # EOF
@@ -0,0 +1,390 @@
1
+ # Equivalent to a header guard in C/C++
2
+ # Used to prevent the spec helper from being loaded more than once
3
+ unless defined? ::Webby::Renderer
4
+
5
+ require 'erb'
6
+
7
+ module Webby
8
+
9
+ # The Webby::Renderer is used to _filter_ and _layout_ the text found in the
10
+ # resource page files in the content directory.
11
+ #
12
+ # A page is filtered based on the settings of the 'filter' option in the
13
+ # page's meta-data information. For example, if 'textile' is specified as
14
+ # a filter, then the page will be run through the RedCloth markup filter.
15
+ # More than one filter can be used on a page; they will be run in the
16
+ # order specified in the meta-data.
17
+ #
18
+ # A page is rendered into a layout specified by the 'layout' option in the
19
+ # page's meta-data information.
20
+ #
21
+ class Renderer
22
+ include ERB::Util
23
+
24
+ # :stopdoc:
25
+ @@stack = []
26
+ # :startdoc:
27
+
28
+ # call-seq:
29
+ # Renderer.write( page )
30
+ #
31
+ # Render the given _page_ and write the resulting output to the page's
32
+ # destination. If the _page_ uses pagination, then multiple destination
33
+ # files will be created -- one for each paginated data set in the page.
34
+ #
35
+ def self.write( page )
36
+ renderer = self.new(page)
37
+
38
+ loop {
39
+ dest = page.destination
40
+ FileUtils.mkdir_p ::File.dirname(dest)
41
+ journal.create_or_update(page)
42
+
43
+ text = renderer._layout_page
44
+ unless text.nil?
45
+ ::File.open(dest, 'w') {|fd| fd.write(text)}
46
+ end
47
+
48
+ break unless renderer._next_page
49
+ }
50
+ end
51
+
52
+ attr_reader :logger
53
+
54
+ # call-seq:
55
+ # Renderer.new( page )
56
+ #
57
+ # Create a new renderer for the given _page_. The renderer will apply the
58
+ # desired filters to the _page_ (from the page's meta-data) and then
59
+ # render the filtered page into the desired layout.
60
+ #
61
+ def initialize( page )
62
+ unless page.instance_of? Resources::Page
63
+ raise ArgumentError,
64
+ "only page resources can be rendered '#{page.path}'"
65
+ end
66
+
67
+ @page = page
68
+ @pages = Resources.pages
69
+ @partials = Resources.partials
70
+ @content = nil
71
+ @config = ::Webby.site
72
+
73
+ @_bindings = []
74
+ @_content_for = {}
75
+ @logger = Logging::Logger[self]
76
+ end
77
+
78
+ # call-seq:
79
+ # render( resource = nil, opts = {} ) => string
80
+ #
81
+ # Render the given resource (a page or a partial) and return the results
82
+ # as a string. If a resource is not given, then the options hash should
83
+ # contain the name of a partial to render (:partial => 'name').
84
+ #
85
+ # When a partial name is given, the partial is found by looking in the
86
+ # directory of the current page being rendered. Otherwise, the full path
87
+ # to the partial can be given.
88
+ #
89
+ # If a :guard option is given as true, then the resulting string will be
90
+ # protected from processing by subsequent filters. Currently this only
91
+ # protects against the textile filter.
92
+ #
93
+ # When rendering partials, local variables can be passed to the partial by
94
+ # setting them in hash passed as the :locals option.
95
+ #
96
+ # ==== Options
97
+ # :partial<String>::
98
+ # The partial to render
99
+ # :locals<Hash>::
100
+ # Locals values to define when rendering a partial
101
+ # :guard<Boolean>::
102
+ # Prevents the resulting string from being processed by subsequent
103
+ # filters (only textile for now)
104
+ #
105
+ # ==== Returns
106
+ # A string that is the rendered page or partial.
107
+ #
108
+ # ==== Examples
109
+ # # render the partial "foo" using the given local variables
110
+ # render( :partial => "foo", :locals => {:bar => "value for bar"} )
111
+ #
112
+ # # find another page and render it into this page and protect the
113
+ # # resulting contents from further filters
114
+ # page = @pages.find( :title => "Chicken Coop" )
115
+ # render( page, :guard => true )
116
+ #
117
+ # # find a partial and render it using the given local variables
118
+ # partial = @partials.find( :filename => "foo", :in_directory => "/path" )
119
+ # render( partial, :locals => {:baz => "baztastic"} )
120
+ #
121
+ def render( *args )
122
+ opts = Hash === args.last ? args.pop : {}
123
+ resource = args.first
124
+ resource = _find_partial(opts[:partial]) if resource.nil?
125
+
126
+ str = case resource
127
+ when Resources::Page
128
+ ::Webby::Renderer.new(resource)._render_page
129
+ when Resources::Partial
130
+ _render_partial(resource, opts)
131
+ when Resources::Static
132
+ resource._read
133
+ else
134
+ raise ::Webby::Error, "expecting a page or a partial but got '#{resource.class.name}'"
135
+ end
136
+
137
+ str = _guard(str) if opts[:guard]
138
+ str
139
+ end
140
+
141
+ # call-seq:
142
+ # render_page => string
143
+ #
144
+ # This method is being deprecated. It is being made internal to the
145
+ # framework and really shouldn't be used anymore.
146
+ #
147
+ def render_page
148
+ Webby.deprecated "render_page", "this method is being made internal to the framework"
149
+ _render_page
150
+ end
151
+
152
+ # call-seq:
153
+ # render_partial( partial, :locals => {} ) => string
154
+ #
155
+ # This method is being deprecated. Please use the +render+ method instead.
156
+ #
157
+ def render_partial( part, opts = {} )
158
+ Webby.deprecated "render_partial", "it is being replaced by the Renderer#render() method"
159
+ opts[:partial] = part
160
+ render opts
161
+ end
162
+
163
+ # call-seq:
164
+ # paginate( items, per_page ) {|item| block}
165
+ #
166
+ # Iterate the given _block_ for each item selected from the _items_ array
167
+ # using the given number of items _per_page_. The first time the page is
168
+ # rendered, the items passed to the block are selected using the range
169
+ # (0...per_page). The next rendering selects (per_page...2*per_page). This
170
+ # continues until all _items_ have been paginated.
171
+ #
172
+ # Calling this method creates a <code>@pager</code> object that can be
173
+ # accessed from the page. The <code>@pager</code> contains information
174
+ # about the next page, the current page number, the previous page, and the
175
+ # number of items in the current page.
176
+ #
177
+ def paginate( items, count, &block )
178
+ @pager ||= Paginator.new(items.length, count, @page) do |offset, per_page|
179
+ items[offset,per_page]
180
+ end.first
181
+
182
+ @pager.each(&block)
183
+ end
184
+
185
+ # call-seq:
186
+ # get_binding => binding
187
+ #
188
+ # Returns the current binding for the renderer.
189
+ #
190
+ def get_binding
191
+ @_bindings.last
192
+ end
193
+
194
+ # call-seq:
195
+ # _render_page => string
196
+ #
197
+ # Apply the desired filters to the page. The filters to apply are
198
+ # determined from the page's meta-data.
199
+ #
200
+ def _render_page
201
+ _track_rendering(@page.path) {
202
+ Filters.process(self, @page, @page._read)
203
+ }
204
+ end
205
+
206
+ # call-seq:
207
+ # _render_partial( partial, :locals => {} ) => string
208
+ #
209
+ # Render the given _partial_ into the current page. The :locals are a hash
210
+ # of key / value pairs that will be set as local variables in the scope of
211
+ # the partial when it is rendered.
212
+ #
213
+ def _render_partial( part, opts = {} )
214
+ _track_rendering(part.path) {
215
+ _configure_locals(opts[:locals])
216
+ Filters.process(self, part, part._read)
217
+ }
218
+ end
219
+
220
+ # call-seq:
221
+ # _layout_page => string
222
+ #
223
+ # Apply the desired filters to the page and then render the filtered page
224
+ # into the desired layout. The filters to apply to the page are determined
225
+ # from the page's meta-data. The layout to use is also determined from the
226
+ # page's meta-data.
227
+ #
228
+ def _layout_page
229
+ @content = _render_page
230
+
231
+ _track_rendering(@page.path) {
232
+ _render_layout_for(@page)
233
+ }
234
+ raise ::Webby::Error, "rendering stack corrupted" unless @@stack.empty?
235
+
236
+ @content
237
+ rescue ::Webby::Error => err
238
+ logger.error "while rendering page '#{@page.path}'"
239
+ logger.error err.message
240
+ return nil
241
+ rescue => err
242
+ logger.error "while rendering page '#{@page.path}'"
243
+ logger.fatal err
244
+ exit 1
245
+ ensure
246
+ @content = nil
247
+ @@stack.clear
248
+ end
249
+
250
+ # call-seq:
251
+ # _render_layout_for( resource )
252
+ #
253
+ # Render the layout for the given resource. If the resource does not have
254
+ # a layout, then this method returns immediately.
255
+ #
256
+ def _render_layout_for( res )
257
+ return unless res.layout
258
+ lyt = Resources.find_layout(res.layout)
259
+ return if lyt.nil?
260
+
261
+ _track_rendering(lyt.path) {
262
+ @content = Filters.process(self, lyt, lyt._read)
263
+ _render_layout_for(lyt)
264
+ }
265
+ end
266
+
267
+ # call-seq:
268
+ # _next_page => true or false
269
+ #
270
+ # Returns +true+ if there is a next page to render. Returns +false+ if
271
+ # there is no next page or if pagination has not been configured for the
272
+ # current page.
273
+ #
274
+ def _next_page
275
+ return false unless defined? @pager and @pager
276
+
277
+ # go to the next page; break out if there is no next page
278
+ if @pager.next?
279
+ @pager = @pager.next
280
+ @_content_for.clear
281
+ @_bindings.clear
282
+ else
283
+ @pager.pager.reset
284
+ @pager = nil
285
+ return false
286
+ end
287
+
288
+ true
289
+ end
290
+
291
+ # call-seq:
292
+ # _track_rendering( path ) {block}
293
+ #
294
+ # Keep track of the page rendering for the given _path_. The _block_ is
295
+ # where the the page will be rendered.
296
+ #
297
+ # This method keeps a stack of the current pages being rendeered. It looks
298
+ # for duplicates in the stack -- an indication of a rendering loop. When a
299
+ # rendering loop is detected, an error is raised.
300
+ #
301
+ # This method returns whatever is returned from the _block_.
302
+ #
303
+ def _track_rendering( path )
304
+ loop_error = @@stack.include? path
305
+ @@stack << path
306
+ @_bindings << _binding
307
+
308
+ if loop_error
309
+ msg = "rendering loop detected for '#{path}'\n"
310
+ msg << " current rendering stack\n\t"
311
+ msg << @@stack.join("\n\t")
312
+ raise ::Webby::Error, msg
313
+ end
314
+
315
+ yield
316
+ ensure
317
+ @@stack.pop if path == @@stack.last
318
+ @_bindings.pop
319
+ end
320
+
321
+ # call-seq:
322
+ # _configure_locals( locals )
323
+ #
324
+ # Configure local variables in the scope of the current binding returned
325
+ # by the +get_binding+ method. The _locals_ should be given as a hash of
326
+ # name / value pairs.
327
+ #
328
+ def _configure_locals( locals )
329
+ return if locals.nil?
330
+
331
+ locals.each do |k,v|
332
+ Thread.current[:value] = v
333
+ definition = "#{k} = Thread.current[:value]"
334
+ eval(definition, get_binding)
335
+ end
336
+ end
337
+
338
+ # Attempts to locate a partial by name. If only the partial name is given,
339
+ # then the current directory of the page being rendered is searched for
340
+ # the partial. If a full path is given, then the partial is searched for
341
+ # in that directory.
342
+ #
343
+ # Raies a Webby::Error if the partial could not be found.
344
+ #
345
+ def _find_partial( part )
346
+ case part
347
+ when String
348
+ part_dir = ::File.dirname(part)
349
+ part_dir = @page.dir if part_dir == '.'
350
+
351
+ part_fn = ::File.basename(part)
352
+ part_fn = '_' + part_fn unless part_fn =~ %r/^_/
353
+
354
+ p = Resources.partials.find(
355
+ :filename => part_fn, :in_directory => part_dir ) rescue nil
356
+ raise ::Webby::Error, "could not find partial '#{part}'" if p.nil?
357
+ p
358
+ when ::Webby::Resources::Partial
359
+ part
360
+ else raise ::Webby::Error, "expecting a partial or a partial name" end
361
+ end
362
+
363
+ # This method will put filter guards around the given input string. This
364
+ # will protect the string from being processed by any remaining filters
365
+ # (specifically the textile filter).
366
+ #
367
+ # The string is returned unchanged if there are no remaining filters to
368
+ # guard against.
369
+ #
370
+ def _guard( str )
371
+ return str unless @_cursor
372
+
373
+ if @_cursor.remaining_filters.include? 'textile'
374
+ str = "<notextile>\n%s\n</notextile>" % str
375
+ end
376
+ str
377
+ end
378
+
379
+ # Returns the binding in the scope of this Renderer object.
380
+ #
381
+ def _binding() binding end
382
+
383
+ end # class Renderer
384
+ end # module Webby
385
+
386
+ Webby.require_all_libs_relative_to(__FILE__, 'stelan')
387
+
388
+ end # unless defined?
389
+
390
+ # EOF