webgen 0.5.17 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (408) hide show
  1. data/API.rdoc +143 -0
  2. data/AUTHORS +0 -1
  3. data/COPYING +17 -8
  4. data/ChangeLog +4456 -0
  5. data/GPL +623 -289
  6. data/README.md +71 -0
  7. data/Rakefile +87 -99
  8. data/VERSION +1 -1
  9. data/bin/webgen +1 -7
  10. data/data/webgen/basic_website_template/ext/init.rb +15 -0
  11. data/data/webgen/basic_website_template/webgen.config +18 -0
  12. data/data/webgen/bundle_template_files/README.md.erb +24 -0
  13. data/data/webgen/bundle_template_files/Rakefile.erb +36 -0
  14. data/data/webgen/bundle_template_files/info.yaml.erb +16 -0
  15. data/data/webgen/bundle_template_files/init.rb.erb +1 -0
  16. data/data/webgen/passive_sources/default.metainfo +32 -0
  17. data/data/webgen/passive_sources/stylesheets/coderay-default.css +109 -118
  18. data/data/webgen/passive_sources/templates/feed.template +62 -0
  19. data/data/webgen/passive_sources/templates/sitemap.template +3 -6
  20. data/data/webgen/passive_sources/templates/tag.template +42 -0
  21. data/data/webgen/website_bundles/default/README +2 -2
  22. data/lib/webgen/blackboard.rb +15 -51
  23. data/lib/webgen/bundle/built-in-show-changes/init.rb +54 -0
  24. data/lib/webgen/bundle/built-in/init.rb +366 -0
  25. data/lib/webgen/bundle_loader.rb +126 -0
  26. data/lib/webgen/cache.rb +9 -18
  27. data/lib/webgen/cli.rb +131 -58
  28. data/lib/webgen/cli/bundle_command.rb +30 -0
  29. data/lib/webgen/cli/create_bundle_command.rb +46 -0
  30. data/lib/webgen/cli/create_command.rb +48 -60
  31. data/lib/webgen/cli/generate_command.rb +25 -0
  32. data/lib/webgen/cli/install_bundle_command.rb +34 -0
  33. data/lib/webgen/cli/list_bundle_command.rb +108 -0
  34. data/lib/webgen/cli/logger.rb +45 -0
  35. data/lib/webgen/cli/show_command.rb +30 -0
  36. data/lib/webgen/cli/show_config_command.rb +63 -0
  37. data/lib/webgen/cli/show_dependencies_command.rb +103 -0
  38. data/lib/webgen/cli/show_extensions_command.rb +74 -0
  39. data/lib/webgen/cli/utils.rb +68 -95
  40. data/lib/webgen/configuration.rb +143 -105
  41. data/lib/webgen/content_processor.rb +160 -0
  42. data/lib/webgen/content_processor/blocks.rb +96 -0
  43. data/lib/webgen/content_processor/builder.rb +25 -0
  44. data/lib/webgen/content_processor/erb.rb +25 -0
  45. data/lib/webgen/content_processor/erubis.rb +31 -0
  46. data/lib/webgen/content_processor/fragments.rb +82 -0
  47. data/lib/webgen/content_processor/haml.rb +25 -0
  48. data/lib/webgen/content_processor/html_head.rb +157 -0
  49. data/lib/webgen/content_processor/kramdown.rb +49 -0
  50. data/lib/webgen/content_processor/maruku.rb +39 -0
  51. data/lib/webgen/content_processor/r_discount.rb +21 -0
  52. data/lib/webgen/content_processor/rdoc.rb +22 -0
  53. data/lib/webgen/content_processor/redcloth.rb +23 -0
  54. data/lib/webgen/content_processor/ruby.rb +20 -0
  55. data/lib/webgen/content_processor/sass.rb +145 -0
  56. data/lib/webgen/content_processor/scss.rb +23 -0
  57. data/lib/webgen/content_processor/tags.rb +30 -0
  58. data/lib/webgen/content_processor/tidy.rb +32 -0
  59. data/lib/webgen/content_processor/tikz.rb +116 -0
  60. data/lib/webgen/content_processor/xmllint.rb +31 -0
  61. data/lib/webgen/context.rb +57 -29
  62. data/lib/webgen/context/html_head.rb +60 -0
  63. data/lib/webgen/context/nodes.rb +32 -27
  64. data/lib/webgen/context/rendering.rb +39 -0
  65. data/lib/webgen/context/webgen_tags.rb +25 -0
  66. data/lib/webgen/core_ext.rb +25 -0
  67. data/lib/webgen/destination.rb +151 -0
  68. data/lib/webgen/destination/file_system.rb +62 -0
  69. data/lib/webgen/error.rb +59 -49
  70. data/lib/webgen/extension_manager.rb +121 -0
  71. data/lib/webgen/item_tracker.rb +237 -0
  72. data/lib/webgen/item_tracker/file.rb +39 -0
  73. data/lib/webgen/item_tracker/missing_node.rb +61 -0
  74. data/lib/webgen/item_tracker/node_content.rb +40 -0
  75. data/lib/webgen/item_tracker/node_meta_info.rb +53 -0
  76. data/lib/webgen/item_tracker/nodes.rb +92 -0
  77. data/lib/webgen/logger.rb +26 -82
  78. data/lib/webgen/node.rb +122 -367
  79. data/lib/webgen/node_finder.rb +336 -0
  80. data/lib/webgen/page.rb +48 -85
  81. data/lib/webgen/path.rb +218 -156
  82. data/lib/webgen/path_handler.rb +400 -0
  83. data/lib/webgen/path_handler/base.rb +220 -0
  84. data/lib/webgen/path_handler/copy.rb +78 -0
  85. data/lib/webgen/path_handler/directory.rb +21 -0
  86. data/lib/webgen/path_handler/feed.rb +82 -0
  87. data/lib/webgen/path_handler/meta_info.rb +84 -0
  88. data/lib/webgen/path_handler/page.rb +38 -0
  89. data/lib/webgen/path_handler/page_utils.rb +79 -0
  90. data/lib/webgen/path_handler/sitemap.rb +52 -0
  91. data/lib/webgen/path_handler/template.rb +96 -0
  92. data/lib/webgen/path_handler/virtual.rb +85 -0
  93. data/lib/webgen/{webgentask.rb → rake_task.rb} +31 -27
  94. data/lib/webgen/source.rb +106 -24
  95. data/lib/webgen/source/file_system.rb +41 -0
  96. data/lib/webgen/source/stacked.rb +49 -53
  97. data/lib/webgen/source/tar_archive.rb +59 -0
  98. data/lib/webgen/tag.rb +250 -19
  99. data/lib/webgen/tag/breadcrumb_trail.rb +65 -0
  100. data/lib/webgen/tag/coderay.rb +32 -35
  101. data/lib/webgen/tag/date.rb +9 -9
  102. data/lib/webgen/tag/execute_command.rb +31 -0
  103. data/lib/webgen/tag/include_file.rb +32 -0
  104. data/lib/webgen/tag/langbar.rb +31 -47
  105. data/lib/webgen/tag/link.rb +17 -18
  106. data/lib/webgen/tag/menu.rb +27 -189
  107. data/lib/webgen/tag/meta_info.rb +31 -0
  108. data/lib/webgen/tag/relocatable.rb +48 -39
  109. data/lib/webgen/tag/tikz.rb +24 -100
  110. data/lib/webgen/task.rb +99 -0
  111. data/lib/webgen/task/create_bundle.rb +73 -0
  112. data/lib/webgen/task/create_website.rb +94 -0
  113. data/lib/webgen/task/generate_website.rb +47 -0
  114. data/lib/webgen/test_helper.rb +183 -0
  115. data/lib/webgen/tree.rb +95 -46
  116. data/lib/webgen/utils.rb +39 -0
  117. data/lib/webgen/utils/external_command.rb +27 -0
  118. data/lib/webgen/utils/tag_parser.rb +124 -0
  119. data/lib/webgen/version.rb +1 -1
  120. data/lib/webgen/website.rb +134 -296
  121. data/setup.rb +1 -1
  122. data/test/test_documentation.rb +43 -0
  123. data/test/webgen/cli/test_logger.rb +41 -0
  124. data/test/{test_contentprocessor_blocks.rb → webgen/content_processor/test_blocks.rb} +30 -28
  125. data/test/webgen/content_processor/test_builder.rb +25 -0
  126. data/test/webgen/content_processor/test_erb.rb +21 -0
  127. data/test/webgen/content_processor/test_erubis.rb +33 -0
  128. data/test/webgen/content_processor/test_fragments.rb +96 -0
  129. data/test/webgen/content_processor/test_haml.rb +24 -0
  130. data/test/webgen/content_processor/test_html_head.rb +78 -0
  131. data/test/webgen/content_processor/test_kramdown.rb +49 -0
  132. data/test/webgen/content_processor/test_maruku.rb +30 -0
  133. data/test/webgen/content_processor/test_r_discount.rb +18 -0
  134. data/test/webgen/content_processor/test_rdoc.rb +18 -0
  135. data/test/webgen/content_processor/test_redcloth.rb +23 -0
  136. data/test/webgen/content_processor/test_ruby.rb +24 -0
  137. data/test/webgen/content_processor/test_sass.rb +44 -0
  138. data/test/webgen/content_processor/test_scss.rb +23 -0
  139. data/test/webgen/content_processor/test_tags.rb +44 -0
  140. data/test/webgen/content_processor/test_tidy.rb +31 -0
  141. data/test/webgen/content_processor/test_tikz.rb +33 -0
  142. data/test/webgen/content_processor/test_xmllint.rb +32 -0
  143. data/test/webgen/destination/test_file_system.rb +54 -0
  144. data/test/webgen/item_tracker/test_file.rb +31 -0
  145. data/test/webgen/item_tracker/test_missing_node.rb +70 -0
  146. data/test/webgen/item_tracker/test_node_content.rb +42 -0
  147. data/test/webgen/item_tracker/test_node_meta_info.rb +44 -0
  148. data/test/webgen/item_tracker/test_nodes.rb +61 -0
  149. data/test/webgen/path_handler/test_base.rb +153 -0
  150. data/test/webgen/path_handler/test_copy.rb +56 -0
  151. data/test/webgen/path_handler/test_feed.rb +85 -0
  152. data/test/webgen/path_handler/test_meta_info.rb +98 -0
  153. data/test/webgen/path_handler/test_page.rb +25 -0
  154. data/test/webgen/path_handler/test_page_utils.rb +59 -0
  155. data/test/webgen/path_handler/test_sitemap.rb +95 -0
  156. data/test/webgen/path_handler/test_template.rb +64 -0
  157. data/test/webgen/path_handler/test_virtual.rb +87 -0
  158. data/test/webgen/source/test_file_system.rb +51 -0
  159. data/test/webgen/source/test_stacked.rb +35 -0
  160. data/test/{test_source_tararchive.rb → webgen/source/test_tar_archive.rb} +10 -25
  161. data/test/webgen/tag/test_breadcrumb_trail.rb +66 -0
  162. data/test/webgen/tag/test_coderay.rb +34 -0
  163. data/test/webgen/tag/test_date.rb +18 -0
  164. data/test/webgen/tag/test_execute_command.rb +36 -0
  165. data/test/webgen/tag/test_include_file.rb +35 -0
  166. data/test/webgen/tag/test_langbar.rb +50 -0
  167. data/test/webgen/tag/test_link.rb +40 -0
  168. data/test/webgen/tag/test_menu.rb +61 -0
  169. data/test/webgen/tag/test_meta_info.rb +25 -0
  170. data/test/webgen/tag/test_relocatable.rb +50 -0
  171. data/test/webgen/tag/test_tikz.rb +41 -0
  172. data/test/webgen/task/test_create_website.rb +46 -0
  173. data/test/webgen/test_blackboard.rb +31 -0
  174. data/test/webgen/test_bundle_loader.rb +55 -0
  175. data/test/{test_cache.rb → webgen/test_cache.rb} +3 -15
  176. data/test/webgen/test_cli.rb +41 -0
  177. data/test/webgen/test_configuration.rb +131 -0
  178. data/test/webgen/test_content_processor.rb +86 -0
  179. data/test/webgen/test_context.rb +73 -0
  180. data/test/webgen/test_core_ext.rb +20 -0
  181. data/test/webgen/test_destination.rb +48 -0
  182. data/test/webgen/test_error.rb +121 -0
  183. data/test/webgen/test_extension_manager.rb +70 -0
  184. data/test/webgen/test_item_tracker.rb +106 -0
  185. data/test/{test_languages.rb → webgen/test_languages.rb} +4 -4
  186. data/test/webgen/test_logger.rb +46 -0
  187. data/test/webgen/test_node.rb +178 -0
  188. data/test/webgen/test_node_finder.rb +127 -0
  189. data/test/{test_page.rb → webgen/test_page.rb} +44 -48
  190. data/test/webgen/test_path.rb +271 -0
  191. data/test/{test_webgentask.rb → webgen/test_rake_task.rb} +4 -4
  192. data/test/webgen/test_source.rb +59 -0
  193. data/test/webgen/test_tag.rb +137 -0
  194. data/test/webgen/test_task.rb +40 -0
  195. data/test/webgen/test_tree.rb +147 -0
  196. data/test/webgen/test_utils.rb +16 -0
  197. data/test/webgen/test_website.rb +45 -0
  198. data/test/webgen/utils/test_tag_parser.rb +99 -0
  199. metadata +292 -344
  200. data/data/webgen/passive_sources/templates/atom_feed.template +0 -39
  201. data/data/webgen/passive_sources/templates/rss_feed.template +0 -28
  202. data/data/webgen/resources.yaml +0 -4
  203. data/data/webgen/webgui/app.rb +0 -11
  204. data/data/webgen/webgui/controller/main.rb +0 -135
  205. data/data/webgen/webgui/layout/default.xhtml +0 -40
  206. data/data/webgen/webgui/overrides/win32console.rb +0 -0
  207. data/data/webgen/webgui/public/css/jquery.autocomplete.css +0 -50
  208. data/data/webgen/webgui/public/css/ramaze_error.css +0 -90
  209. data/data/webgen/webgui/public/css/style.css +0 -55
  210. data/data/webgen/webgui/public/img/headerbg.jpg +0 -0
  211. data/data/webgen/webgui/public/img/webgen_logo.png +0 -0
  212. data/data/webgen/webgui/public/js/jquery.autocomplete.js +0 -15
  213. data/data/webgen/webgui/public/js/jquery.js +0 -32
  214. data/data/webgen/webgui/start.rb +0 -9
  215. data/data/webgen/webgui/view/create_website.xhtml +0 -14
  216. data/data/webgen/webgui/view/error.xhtml +0 -64
  217. data/data/webgen/webgui/view/index.xhtml +0 -22
  218. data/data/webgen/webgui/view/manage_website.xhtml +0 -18
  219. data/data/webgen/website_skeleton/README +0 -10
  220. data/data/webgen/website_skeleton/Rakefile +0 -69
  221. data/data/webgen/website_skeleton/config.yaml +0 -35
  222. data/data/webgen/website_skeleton/ext/init.rb +0 -10
  223. data/doc/contentprocessor.template +0 -11
  224. data/doc/contentprocessor/blocks.page +0 -129
  225. data/doc/contentprocessor/builder.page +0 -79
  226. data/doc/contentprocessor/erb.page +0 -60
  227. data/doc/contentprocessor/erubis.page +0 -46
  228. data/doc/contentprocessor/fragments.page +0 -26
  229. data/doc/contentprocessor/haml.page +0 -46
  230. data/doc/contentprocessor/head.page +0 -31
  231. data/doc/contentprocessor/kramdown.page +0 -49
  232. data/doc/contentprocessor/less.page +0 -34
  233. data/doc/contentprocessor/maruku.page +0 -44
  234. data/doc/contentprocessor/rdiscount.page +0 -37
  235. data/doc/contentprocessor/rdoc.page +0 -36
  236. data/doc/contentprocessor/redcloth.page +0 -41
  237. data/doc/contentprocessor/sass.page +0 -31
  238. data/doc/contentprocessor/scss.page +0 -39
  239. data/doc/contentprocessor/tags.page +0 -73
  240. data/doc/contentprocessor/tidy.page +0 -14
  241. data/doc/contentprocessor/xmllint.page +0 -14
  242. data/doc/extensions.metainfo +0 -29
  243. data/doc/extensions.page +0 -15
  244. data/doc/extensions.template +0 -17
  245. data/doc/faq.page +0 -222
  246. data/doc/getting_started.page +0 -135
  247. data/doc/index.page +0 -71
  248. data/doc/manual.page +0 -727
  249. data/doc/reference_configuration.page +0 -1254
  250. data/doc/reference_metainfo.page +0 -265
  251. data/doc/reference_website_styles.page +0 -32
  252. data/doc/source/filesystem.page +0 -41
  253. data/doc/source/tararchive.page +0 -40
  254. data/doc/sourcehandler.template +0 -23
  255. data/doc/sourcehandler/copy.page +0 -19
  256. data/doc/sourcehandler/directory.page +0 -27
  257. data/doc/sourcehandler/feed.page +0 -102
  258. data/doc/sourcehandler/metainfo.page +0 -48
  259. data/doc/sourcehandler/page.page +0 -14
  260. data/doc/sourcehandler/sitemap.page +0 -46
  261. data/doc/sourcehandler/template.page +0 -45
  262. data/doc/sourcehandler/virtual.page +0 -49
  263. data/doc/tag.template +0 -25
  264. data/doc/tag/breadcrumbtrail.page +0 -40
  265. data/doc/tag/coderay.page +0 -53
  266. data/doc/tag/date.page +0 -31
  267. data/doc/tag/executecommand.page +0 -26
  268. data/doc/tag/includefile.page +0 -32
  269. data/doc/tag/langbar.page +0 -47
  270. data/doc/tag/link.page +0 -44
  271. data/doc/tag/menu.page +0 -109
  272. data/doc/tag/metainfo.page +0 -29
  273. data/doc/tag/relocatable.page +0 -38
  274. data/doc/tag/sitemap.page +0 -31
  275. data/doc/tag/tikz.page +0 -159
  276. data/doc/upgrading.page +0 -138
  277. data/doc/webgen_page_format.page +0 -129
  278. data/doc/website_styles.metainfo +0 -8
  279. data/lib/webgen/cli/apply_command.rb +0 -66
  280. data/lib/webgen/cli/run_command.rb +0 -22
  281. data/lib/webgen/cli/webgui_command.rb +0 -68
  282. data/lib/webgen/common.rb +0 -27
  283. data/lib/webgen/common/sitemap.rb +0 -83
  284. data/lib/webgen/contentprocessor.rb +0 -117
  285. data/lib/webgen/contentprocessor/blocks.rb +0 -92
  286. data/lib/webgen/contentprocessor/builder.rb +0 -29
  287. data/lib/webgen/contentprocessor/erb.rb +0 -26
  288. data/lib/webgen/contentprocessor/erubis.rb +0 -39
  289. data/lib/webgen/contentprocessor/fragments.rb +0 -25
  290. data/lib/webgen/contentprocessor/haml.rb +0 -34
  291. data/lib/webgen/contentprocessor/head.rb +0 -128
  292. data/lib/webgen/contentprocessor/kramdown.rb +0 -27
  293. data/lib/webgen/contentprocessor/kramdown/html.rb +0 -36
  294. data/lib/webgen/contentprocessor/less.rb +0 -35
  295. data/lib/webgen/contentprocessor/maruku.rb +0 -36
  296. data/lib/webgen/contentprocessor/rdiscount.rb +0 -19
  297. data/lib/webgen/contentprocessor/rdoc.rb +0 -20
  298. data/lib/webgen/contentprocessor/redcloth.rb +0 -21
  299. data/lib/webgen/contentprocessor/sass.rb +0 -22
  300. data/lib/webgen/contentprocessor/scss.rb +0 -22
  301. data/lib/webgen/contentprocessor/tags.rb +0 -170
  302. data/lib/webgen/contentprocessor/tidy.rb +0 -38
  303. data/lib/webgen/contentprocessor/xmllint.rb +0 -37
  304. data/lib/webgen/context/render.rb +0 -32
  305. data/lib/webgen/context/tags.rb +0 -20
  306. data/lib/webgen/coreext.rb +0 -13
  307. data/lib/webgen/default_config.rb +0 -240
  308. data/lib/webgen/loggable.rb +0 -25
  309. data/lib/webgen/output.rb +0 -86
  310. data/lib/webgen/output/filesystem.rb +0 -69
  311. data/lib/webgen/source/filesystem.rb +0 -61
  312. data/lib/webgen/source/resource.rb +0 -45
  313. data/lib/webgen/source/tararchive.rb +0 -78
  314. data/lib/webgen/sourcehandler.rb +0 -275
  315. data/lib/webgen/sourcehandler/base.rb +0 -281
  316. data/lib/webgen/sourcehandler/copy.rb +0 -44
  317. data/lib/webgen/sourcehandler/directory.rb +0 -30
  318. data/lib/webgen/sourcehandler/feed.rb +0 -92
  319. data/lib/webgen/sourcehandler/fragment.rb +0 -70
  320. data/lib/webgen/sourcehandler/memory.rb +0 -42
  321. data/lib/webgen/sourcehandler/metainfo.rb +0 -128
  322. data/lib/webgen/sourcehandler/page.rb +0 -64
  323. data/lib/webgen/sourcehandler/sitemap.rb +0 -60
  324. data/lib/webgen/sourcehandler/template.rb +0 -66
  325. data/lib/webgen/sourcehandler/virtual.rb +0 -117
  326. data/lib/webgen/tag/base.rb +0 -170
  327. data/lib/webgen/tag/breadcrumbtrail.rb +0 -70
  328. data/lib/webgen/tag/executecommand.rb +0 -31
  329. data/lib/webgen/tag/includefile.rb +0 -42
  330. data/lib/webgen/tag/metainfo.rb +0 -27
  331. data/lib/webgen/tag/sitemap.rb +0 -41
  332. data/lib/webgen/websiteaccess.rb +0 -31
  333. data/lib/webgen/websitemanager.rb +0 -125
  334. data/misc/default.css +0 -403
  335. data/misc/default.template +0 -76
  336. data/misc/htmldoc.metainfo +0 -26
  337. data/misc/htmldoc.virtual +0 -17
  338. data/misc/images/arrow.gif +0 -0
  339. data/misc/images/error.png +0 -0
  340. data/misc/images/headerbg.jpg +0 -0
  341. data/misc/images/important.png +0 -0
  342. data/misc/images/information.png +0 -0
  343. data/misc/images/quote.gif +0 -0
  344. data/misc/images/warning.png +0 -0
  345. data/misc/logo.svg +0 -313
  346. data/misc/style.page +0 -33
  347. data/test/helper.rb +0 -61
  348. data/test/test_blackboard.rb +0 -60
  349. data/test/test_cli.rb +0 -119
  350. data/test/test_common_sitemap.rb +0 -58
  351. data/test/test_configuration.rb +0 -68
  352. data/test/test_contentprocessor.rb +0 -39
  353. data/test/test_contentprocessor_builder.rb +0 -41
  354. data/test/test_contentprocessor_erb.rb +0 -33
  355. data/test/test_contentprocessor_erubis.rb +0 -62
  356. data/test/test_contentprocessor_fragments.rb +0 -43
  357. data/test/test_contentprocessor_haml.rb +0 -39
  358. data/test/test_contentprocessor_head.rb +0 -96
  359. data/test/test_contentprocessor_kramdown.rb +0 -56
  360. data/test/test_contentprocessor_less.rb +0 -40
  361. data/test/test_contentprocessor_maruku.rb +0 -33
  362. data/test/test_contentprocessor_rdiscount.rb +0 -21
  363. data/test/test_contentprocessor_rdoc.rb +0 -22
  364. data/test/test_contentprocessor_redcloth.rb +0 -26
  365. data/test/test_contentprocessor_sass.rb +0 -28
  366. data/test/test_contentprocessor_scss.rb +0 -28
  367. data/test/test_contentprocessor_tags.rb +0 -122
  368. data/test/test_contentprocessor_tidy.rb +0 -34
  369. data/test/test_contentprocessor_xmllint.rb +0 -38
  370. data/test/test_context.rb +0 -81
  371. data/test/test_error.rb +0 -93
  372. data/test/test_loggable.rb +0 -32
  373. data/test/test_logger.rb +0 -94
  374. data/test/test_node.rb +0 -469
  375. data/test/test_output_filesystem.rb +0 -60
  376. data/test/test_path.rb +0 -241
  377. data/test/test_source_filesystem.rb +0 -76
  378. data/test/test_source_resource.rb +0 -28
  379. data/test/test_source_stacked.rb +0 -49
  380. data/test/test_sourcehandler_base.rb +0 -136
  381. data/test/test_sourcehandler_copy.rb +0 -47
  382. data/test/test_sourcehandler_directory.rb +0 -38
  383. data/test/test_sourcehandler_feed.rb +0 -88
  384. data/test/test_sourcehandler_fragment.rb +0 -70
  385. data/test/test_sourcehandler_main.rb +0 -39
  386. data/test/test_sourcehandler_memory.rb +0 -44
  387. data/test/test_sourcehandler_metainfo.rb +0 -127
  388. data/test/test_sourcehandler_page.rb +0 -73
  389. data/test/test_sourcehandler_sitemap.rb +0 -68
  390. data/test/test_sourcehandler_template.rb +0 -68
  391. data/test/test_sourcehandler_virtual.rb +0 -106
  392. data/test/test_tag_base.rb +0 -62
  393. data/test/test_tag_breadcrumbtrail.rb +0 -91
  394. data/test/test_tag_coderay.rb +0 -45
  395. data/test/test_tag_date.rb +0 -18
  396. data/test/test_tag_executecommand.rb +0 -41
  397. data/test/test_tag_includefile.rb +0 -50
  398. data/test/test_tag_langbar.rb +0 -71
  399. data/test/test_tag_link.rb +0 -70
  400. data/test/test_tag_menu.rb +0 -207
  401. data/test/test_tag_metainfo.rb +0 -26
  402. data/test/test_tag_relocatable.rb +0 -60
  403. data/test/test_tag_sitemap.rb +0 -47
  404. data/test/test_tag_tikz.rb +0 -69
  405. data/test/test_tree.rb +0 -70
  406. data/test/test_website.rb +0 -130
  407. data/test/test_websiteaccess.rb +0 -25
  408. data/test/test_websitemanager.rb +0 -65
@@ -0,0 +1,336 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'webgen/path'
4
+
5
+ module Webgen
6
+
7
+ # Used for finding nodes that match certain criterias.
8
+ #
9
+ # == About
10
+ #
11
+ # This extension class is used for finding nodes that match certain criterias (all nodes are used
12
+ # if no filter options are specified) when calling the #find method. There are some built-in
13
+ # filters but one can also provide custom filters via #add_filter_module.
14
+ #
15
+ # The found nodes are either returned in a flat list or hierarchical in nested lists (if a node
16
+ # has no child nodes, only the node itself is used; otherwise a two element array containing the
17
+ # node and child nodes is used). Sorting, limiting the number of returned nodes and using an
18
+ # offset are also possible.
19
+ #
20
+ # *Note* that results are cached in the volatile cache of the Cache instance!
21
+ #
22
+ # == Finder options
23
+ #
24
+ # Following is the list of all finder options. Note that there may also be other 3rd party node
25
+ # filters available if you are using extension bundles!
26
+ #
27
+ # === Non-filter options
28
+ #
29
+ # These options are not used for filtering out nodes but provide additional functionality.
30
+ #
31
+ # [:limit]
32
+ # Value: an integer. Specifies the maximum number of nodes that should be returned. Implies
33
+ # 'flatten = true'.
34
+ #
35
+ # Note that fewer nodes may be returned if fewer nodes match the filter criterias.
36
+ #
37
+ # [:offset]
38
+ # Value: an integer. Specifies how many nodes from the front of the list should *not* be
39
+ # returned. Implies 'flatten = true'.
40
+ #
41
+ # [:flatten]
42
+ # Value: anything except +nil+ or +false+. A flat list of nodes is returned if this option is
43
+ # set, otherwise the nodes are returned in their correct hierarchical order using nested lists.
44
+ #
45
+ # Note that any missing nodes in the hierarchy are automatically added so that traversing the
46
+ # hierarchy is always possible. For example, if we have the tree '/a/b/c' and only nodes +a+ and
47
+ # +c+ are found, node +b+ is automatically added.
48
+ #
49
+ # [:sort]
50
+ # Value: +nil+/+false+, +true+ or a meta information key. If +nil+ or +false+ is specified, no
51
+ # sorting is performed. If +true+ is specified, the meta information +sort_info+ (or if absent,
52
+ # the meta information +title+) is used for sorting. If the compared values are both integers, a
53
+ # numeric comparison is done, else a string comparison. If a meta information key is specified,
54
+ # the value of this meta information is used for comparison of nodes (again, if both compared
55
+ # values are integers, a numeric comparison is done, else a string comparison).
56
+ #
57
+ # === Filter options
58
+ #
59
+ # These options are used for filtering the nodes. All nodes are used by default if no filter
60
+ # options are specified.
61
+ #
62
+ # [:alcn]
63
+ # Value: an alcn pattern or an array of alcn patterns. Nodes that match any of the patterns are
64
+ # used.
65
+ #
66
+ # [:lang]
67
+ # Value: a language code/+nil+/the special value :+node+ or an array of these values. Nodes that
68
+ # have one of the specified language codes, are language independent (in case of the value
69
+ # +nil+) or have the same language as the reference node (in case of the value :+node+) are
70
+ # used.
71
+ #
72
+ # [:mi]
73
+ # Value: a hash with meta information key to value mappings. Only nodes that have the same
74
+ # values for all meta information keys are used.
75
+ #
76
+ # [:or]
77
+ # Value: a finder option set or an array of finder options sets (specifying option set names is
78
+ # also possible). Nodes that appear in any specified option set are additionally used.
79
+ #
80
+ # [:and]
81
+ # Value: a finder option set or an array of finder options sets (specifying option set names is
82
+ # also possible). Only nodes that appear in all specified option sets are used.
83
+ #
84
+ # [:not]
85
+ # Value: a finder option set or an array of finder options sets (specifying option set names is
86
+ # also possible). Only nodes that do not appear in any specified option set are used.
87
+ #
88
+ # [:levels]
89
+ # Value: one integer (is used as start and end level) or an array with two integers (the start
90
+ # and end levels). All nodes whose hierarchy levels are greater than or equal to the start level
91
+ # and lower than or equal to the end level are used.
92
+ #
93
+ # [:ancestors]
94
+ # Value: +true+ or +false+/+nil+. If this filter option is set to +true+, only nodes that are
95
+ # ancestors of the reference node are used. The reference node itself is used as well.
96
+ #
97
+ # [:descendants]
98
+ # Value: +true+ or +false+/+nil+. If this filter option is set to +true+, only nodes that are
99
+ # descendants of the reference node are used. The reference node itself is used as well.
100
+ #
101
+ # [:siblings]
102
+ # Value: +true+ or +false+/+nil+. If this filter option is set to +true+, only nodes that are
103
+ # siblings of the reference are node used. The reference node itself is used as well.
104
+ #
105
+ # == Implementing a filter module
106
+ #
107
+ # Implementing a filter module is very easy. Just create a module that contains your filter
108
+ # methods and tell the NodeFinder object about it using the #add_filter_module method. A filter
109
+ # method needs to take three arguments: an array of nodes, the reference node and the filter
110
+ # value.
111
+ #
112
+ # Here is a sample filter module which provides the ability to filter nodes based on the meta
113
+ # information key +category+. The +category+ key contains an array with one or more categories.
114
+ # The value for this category filter is one or more strings and the filter returns those nodes
115
+ # that contain at least one specified category.
116
+ #
117
+ # module CategoryFilter
118
+ #
119
+ # def filter_on_category(nodes, ref_node, categories)
120
+ # categories = [categories].flatten # needed in case categories is a string
121
+ # nodes.select {|n| categories.any? {|c| n['category'].include?(c)}}
122
+ # end
123
+ #
124
+ # end
125
+ #
126
+ # website.ext.node_finder.add_filter_module(CategoryFilter, category: 'filter_on_category')
127
+ #
128
+ class NodeFinder
129
+
130
+ # Create a new NodeFinder object for the given website.
131
+ def initialize(website)
132
+ @website = website
133
+ @mapping = {
134
+ :alcn => :filter_alcn, :levels => :filter_levels, :lang => :filter_lang,
135
+ :and => :filter_and, :or => :filter_or, :not => :filter_not,
136
+ :ancestors => :filter_ancestors, :descendants => :filter_descendants,
137
+ :siblings => :filter_siblings,
138
+ :mi => :filter_meta_info
139
+ }
140
+ end
141
+
142
+ # Add a module with filter methods.
143
+ #
144
+ # The parameter +mapping+ needs to be a hash associating unique names with the methods of the
145
+ # given module that can be used as finder methods.
146
+ #
147
+ # === Examples:
148
+ #
149
+ # node_finder.add_filter_module(MyModule, blog: 'filter_on_blog')
150
+ #
151
+ def add_filter_module(mod, mapping)
152
+ public_methods = mod.public_instance_methods.map {|c| c.to_s}
153
+ mapping.each do |name, method|
154
+ if !public_methods.include?(method.to_s)
155
+ raise ArgumentError, "Finder method '#{method}' not found in module #{mod}"
156
+ end
157
+ @mapping[name.intern] = method
158
+ end
159
+ extend(mod)
160
+ end
161
+
162
+ # Return all nodes that match certain criterias.
163
+ #
164
+ # The parameter +opts_or_name+ can either be a hash with finder options or the name of a finder
165
+ # option set defined using the configuration option 'node_finder.options_sets'. The node
166
+ # +ref_node+ is used as reference node.
167
+ def find(opts_or_name, ref_node)
168
+ if result = cached_result(opts_or_name, ref_node)
169
+ return result
170
+ end
171
+ opts = prepare_options_hash(opts_or_name)
172
+
173
+ limit, offset, flatten, sort = remove_non_filter_options(opts)
174
+ flatten = true if limit || offset
175
+
176
+ nodes = filter_nodes(opts, ref_node)
177
+
178
+ if flatten
179
+ sort_nodes(nodes, sort)
180
+ nodes = nodes[(offset.to_s.to_i)..(limit ? offset.to_s.to_i + limit.to_s.to_i - 1 : -1)] if limit || offset
181
+ else
182
+ result = {}
183
+ min_level = 1_000_000
184
+ nodes.each {|n| min_level = n.level if n.level < min_level}
185
+
186
+ nodes.each do |n|
187
+ hierarchy_nodes = []
188
+ (hierarchy_nodes.unshift(n); n = n.parent) while n.level >= min_level
189
+ hierarchy_nodes.inject(result) {|memo, hn| memo[hn] ||= {}}
190
+ end
191
+
192
+ reducer = lambda do |h|
193
+ h.map {|k,v| v.empty? ? k : [k, reducer.call(v)]}
194
+ end
195
+ nodes = reducer.call(result)
196
+ sort_nodes(nodes, sort, false)
197
+ end
198
+
199
+ cache_result(opts_or_name, ref_node, nodes)
200
+ end
201
+
202
+ #######
203
+ private
204
+ #######
205
+
206
+ def cached_result(opts, ref_node)
207
+ (@website.cache.volatile[:node_finder] ||= {})[[opts, ref_node.alcn]]
208
+ end
209
+
210
+ def cache_result(opts, ref_node, result)
211
+ (@website.cache.volatile[:node_finder] ||= {})[[opts, ref_node.alcn]] = result
212
+ end
213
+
214
+ def prepare_options_hash(opts_or_name)
215
+ if Hash === opts_or_name
216
+ opts_or_name.symbolize_keys
217
+ elsif @website.config['node_finder.option_sets'].has_key?(opts_or_name)
218
+ @website.config['node_finder.option_sets'][opts_or_name].symbolize_keys
219
+ else
220
+ raise ArgumentError, "Invalid argument supplied, expected Hash or name of search definition, not #{opts_or_name}"
221
+ end
222
+ end
223
+
224
+ def remove_non_filter_options(opts)
225
+ [opts.delete(:limit), opts.delete(:offset), opts.delete(:flatten), opts.delete(:sort)]
226
+ end
227
+
228
+ def filter_nodes(opts, ref_node)
229
+ nodes = @website.tree.node_access[:alcn].values
230
+ nodes.delete(@website.tree.dummy_root)
231
+
232
+ opts.each do |filter, value|
233
+ if @mapping.has_key?(filter)
234
+ nodes = send(@mapping[filter], nodes, ref_node, value)
235
+ else
236
+ @website.logger.warn { "Ignoring unknown node finder filter '#{filter}'" }
237
+ end
238
+ end
239
+
240
+ nodes
241
+ end
242
+
243
+ def sort_nodes(nodes, sort, flat_mode = true)
244
+ return unless sort
245
+ if sort == true
246
+ nodes.sort! do |(a,_),(b,_)|
247
+ a = (a['sort_info'] && a['sort_info'].to_s) || a['title'].to_s || ''
248
+ b = (b['sort_info'] && b['sort_info'].to_s) || b['title'].to_s || ''
249
+ (a = a.to_i; b = b.to_i) if a !~ /\D/ && b !~ /\D/
250
+ a <=> b
251
+ end
252
+ else
253
+ nodes.sort! do |(a,_),(b,_)|
254
+ a, b = a[sort].to_s, b[sort].to_s
255
+ a, b = a.to_i, b.to_i if a !~ /\D/ && b !~ /\D/
256
+ a <=> b
257
+ end
258
+ end
259
+ nodes.each {|n, children| sort_nodes(children, sort, flat_mode) if children } unless flat_mode
260
+ end
261
+
262
+ # :section: Filter methods
263
+
264
+ def filter_and(nodes, ref_node, opts)
265
+ [opts].flatten.each do |cur_opts|
266
+ cur_opts = prepare_options_hash(cur_opts)
267
+ remove_non_filter_options(cur_opts)
268
+ nodes &= filter_nodes(cur_opts, ref_node)
269
+ end
270
+ nodes
271
+ end
272
+
273
+ def filter_or(nodes, ref_node, opts)
274
+ [opts].flatten.each do |cur_opts|
275
+ cur_opts = prepare_options_hash(cur_opts)
276
+ remove_non_filter_options(cur_opts)
277
+ nodes |= filter_nodes(cur_opts, ref_node)
278
+ end
279
+ nodes
280
+ end
281
+
282
+ def filter_not(nodes, ref_node, opts)
283
+ [opts].flatten.each do |cur_opts|
284
+ cur_opts = prepare_options_hash(cur_opts)
285
+ remove_non_filter_options(cur_opts)
286
+ nodes -= filter_nodes(cur_opts, ref_node)
287
+ end
288
+ nodes
289
+ end
290
+
291
+ def filter_meta_info(nodes, ref_node, mi)
292
+ nodes.keep_if {|n| mi.all? {|key, val| n[key] == val}}
293
+ end
294
+
295
+ def filter_alcn(nodes, ref_node, alcn)
296
+ alcn = [alcn].flatten.map {|a| Webgen::Path.append(ref_node.alcn, a.to_s)}
297
+ nodes.keep_if {|n| alcn.any? {|a| n =~ a}}
298
+ end
299
+
300
+ def filter_levels(nodes, ref_node, range)
301
+ range = [range].flatten.map {|i| i.to_i}
302
+ nodes.keep_if {|n| n.level >= range.first && n.level <= range.last}
303
+ end
304
+
305
+ def filter_lang(nodes, ref_node, langs)
306
+ langs = [langs].flatten.map {|l| l == :node ? ref_node.lang : l}.uniq
307
+ nodes.keep_if {|n| langs.any? {|l| n.lang == l}}
308
+ end
309
+
310
+ def filter_ancestors(nodes, ref_node, enabled)
311
+ return nodes unless enabled
312
+ nodes = []
313
+ node = ref_node
314
+ until node == node.tree.dummy_root
315
+ nodes.unshift(node)
316
+ node = node.parent
317
+ end
318
+ nodes
319
+ end
320
+
321
+ def filter_descendants(nodes, ref_node, enabled)
322
+ return nodes unless enabled
323
+ nodes.keep_if do |n|
324
+ n = n.parent while n != n.tree.dummy_root && n != ref_node
325
+ n == ref_node
326
+ end
327
+ end
328
+
329
+ def filter_siblings(nodes, ref_node, enabled)
330
+ return nodes unless enabled
331
+ nodes.keep_if { |n| n.parent == ref_node.parent}
332
+ end
333
+
334
+ end
335
+
336
+ end
data/lib/webgen/page.rb CHANGED
@@ -1,95 +1,49 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'yaml'
4
+ require 'webgen/error'
4
5
 
5
6
  module Webgen
6
7
 
7
- # A Page object wraps a meta information hash and an array of Block objects. It is normally
8
- # generated from a file or string in Webgen Page Format using the provided class methods.
8
+ # A Page object wraps a meta information hash and a hash of {block name => block content}
9
+ # associations.
10
+ #
11
+ # It is normally generated from a file or string in Webgen Page Format using the provided class
12
+ # methods.
9
13
  class Page
10
14
 
11
- # A single block within a Page object. The content of the block can be rendered using the #render method.
12
- class Block
13
-
14
- # The name of the block.
15
- attr_reader :name
16
-
17
- # The content of the block.
18
- attr_reader :content
19
-
20
- # The options set specifically for this block.
21
- attr_reader :options
22
-
23
- # Create a new block with the name +name+ and the given +content+ and +options+.
24
- def initialize(name, content, options)
25
- @name, @content, @options = name, content, options
26
- end
27
-
28
- # Render the block using the provided context object.
29
- #
30
- # The context object needs to respond to <tt>#[]</tt> and <tt>#[]=</tt> (e.g. a Hash is a valid
31
- # context object) and the key <tt>:processors</tt> needs to contain a Hash which maps processor
32
- # names to processor objects that respond to <tt>#call</tt>.
33
- #
34
- # Uses the content processors specified in the +pipeline+ key of the +options+ attribute to do
35
- # the actual rendering.
36
- #
37
- # Returns the given context with the rendered content.
38
- def render(context)
39
- context[:content] = @content.dup
40
- context[:block] = self
41
- @options['pipeline'].to_s.split(/,/).each do |processor|
42
- raise "No such content processor available: #{processor}" unless context[:processors].has_key?(processor)
43
- context[:processors][processor].call(context)
44
- end
45
- context
46
- end
47
-
48
- end
49
-
50
-
51
15
  # Raised during parsing of data in Webgen Page Format if the data is invalid.
52
- class FormatError < StandardError; end
16
+ class FormatError < Error; end
53
17
 
54
18
 
55
19
  # :stopdoc:
56
- RE_META_INFO_START = /\A---\s*(?:\n|\r|\r\n)/m
57
- RE_META_INFO = /\A---\s*(?:\n|\r|\r\n).*?(?:\n|\r|\r\n)(?=---.*?(?:\n|\r|\r\n)|\Z)/m
58
- RE_BLOCKS_OPTIONS = /^--- *?(?: *((?:\w+:[^\s]* *)*))?$|^$/
59
- RE_BLOCKS_START = /^--- .*?$|^--- *$/
60
- RE_BLOCKS = /(?:(#{RE_BLOCKS_START})|\A)\n?(.*?)(?:(?=#{RE_BLOCKS_START})|\z)/m
20
+ RE_NEWLINE = /\r?\n/
21
+ RE_META_INFO_START = /\A---\s*#{RE_NEWLINE}/
22
+ RE_META_INFO = /#{RE_META_INFO_START}.*?#{RE_NEWLINE}(?=---.*?#{RE_NEWLINE}|\Z)/m
23
+ RE_BLOCKS_START_SIMPLE = /^--- (\w+)(?:\s*| -+\s*)$|^$/
24
+ RE_BLOCKS_START_COMPLEX = /^--- *?(?: *((?:\w+:[^\s]* *)*))?-*\s*$|^$/
25
+ RE_BLOCKS_START = /^---(?: .*?|)(?=#{RE_NEWLINE})/
26
+ RE_BLOCKS = /(?:(#{RE_BLOCKS_START})|\A)#{RE_NEWLINE}?(.*?)(?:(?=#{RE_BLOCKS_START})|\z)/m
27
+ RE_PAGE = /(#{RE_META_INFO})?(.*)/m
61
28
  # :startdoc:
62
29
 
63
30
  class << self
64
31
 
65
- # Parse the given string +data+ in Webgen Page Format and initialize a new Page object with
66
- # the information. The +meta_info+ parameter can be used to provide default meta information.
67
- def from_data(data, meta_info = {})
68
- md = /(#{RE_META_INFO})?(.*)/m.match(normalize_eol(data))
69
- meta_info = meta_info.merge(parse_meta_info(md[1], data))
32
+ # Parse the given string +data+ in Webgen Page Format.
33
+ #
34
+ # This method returns a Page object containing the hash with the meta information and the
35
+ # parsed blocks.
36
+ def from_data(data)
37
+ md = RE_PAGE.match(data)
38
+ meta_info = parse_meta_info(md[1], data =~ RE_META_INFO_START)
70
39
  blocks = parse_blocks(md[2] || '', meta_info)
71
40
  new(meta_info, blocks)
72
41
  end
73
42
 
74
- # Parse the given string +data+ in Webgen Page Format and return the found meta information.
75
- def meta_info_from_data(data)
76
- md = /(#{RE_META_INFO})?/m.match(normalize_eol(data))
77
- parse_meta_info(md[1], data)
78
- end
79
-
80
- #######
81
- private
82
- #######
83
-
84
- # Normalize the end-of-line encodings to Unix style.
85
- def normalize_eol(data)
86
- data.gsub(/\r\n?/, "\n")
87
- end
88
-
89
43
  # Parse the meta info string in +mi_data+ and return the hash with the meta information. The
90
44
  # original +data+ is used for checking the validness of the meta information block.
91
- def parse_meta_info(mi_data, data)
92
- if mi_data.nil? && data =~ RE_META_INFO_START
45
+ def parse_meta_info(mi_data, has_mi_start)
46
+ if mi_data.nil? && has_mi_start
93
47
  raise FormatError, 'Found start line for meta information block but no valid meta information block'
94
48
  elsif mi_data.nil?
95
49
  {}
@@ -99,39 +53,48 @@ module Webgen
99
53
  unless meta_info.kind_of?(Hash)
100
54
  raise FormatError, "Invalid structure of meta information block: expected YAML hash but found #{meta_info.class}"
101
55
  end
102
- rescue ArgumentError => e
56
+ rescue ArgumentError, SyntaxError => e
103
57
  raise FormatError, "Invalid YAML syntax in meta information block: #{e.message}"
104
58
  end
105
59
  meta_info
106
60
  end
107
61
  end
62
+ private :parse_meta_info
108
63
 
109
- # Parse all blocks in +data+ and return them. Meta information can be provided in +meta_info+
110
- # which is used for setting the block names and options.
64
+ # Parse all blocks in +data+ and return them.
65
+ #
66
+ # The key 'blocks' of the meta information hash is updated with information found on block
67
+ # starting lines.
111
68
  def parse_blocks(data, meta_info)
112
69
  scanned = data.scan(RE_BLOCKS)
113
70
  raise(FormatError, 'No content blocks specified') if scanned.length == 0
114
71
 
115
72
  blocks = {}
116
73
  scanned.each_with_index do |block_data, index|
74
+ index += 1
117
75
  options, content = *block_data
118
- md = RE_BLOCKS_OPTIONS.match(options.to_s)
119
- raise(FormatError, "Found invalid blocks starting line for block #{index+1}: #{options}") if content =~ /\A---/ || md.nil?
120
- options = Hash[*md[1].to_s.scan(/(\w+):([^\s]*)/).map {|k,v| [k, (v == '' ? nil : YAML::load(v))]}.flatten]
121
- options = (meta_info['blocks']['default'] || {} rescue {}).
122
- merge((meta_info['blocks'][index+1] || {} rescue {})).
123
- merge(options)
124
-
125
- name = options.delete('name') || (index == 0 ? 'content' : 'block' + (index + 1).to_s)
126
- raise(FormatError, "Previously used name '#{name}' also used for block #{index+1}") if blocks.has_key?(name)
76
+ if md = RE_BLOCKS_START_SIMPLE.match(options.to_s)
77
+ options = {'name' => md[1]}
78
+ else
79
+ md = RE_BLOCKS_START_COMPLEX.match(options.to_s)
80
+ raise(FormatError, "Found invalid blocks starting line for block #{index}: #{options}") if content =~ /\A---/ || md.nil?
81
+ options = Hash[*md[1].to_s.scan(/(\w+):([^\s]*)/).map {|k,v| [k, (v == '' ? nil : YAML::load(v))]}.flatten]
82
+ end
83
+
84
+ name = options.delete('name') || (index == 1 ? 'content' : 'block' + (index).to_s)
85
+ raise(FormatError, "Previously used name '#{name}' also used for block #{index}") if blocks.has_key?(name)
127
86
  content ||= ''
128
87
  content.gsub!(/^(\\+)(---.*?)$/) {|m| "\\" * ($1.length / 2) + $2}
129
- content.chomp!("\n") unless index + 1 == scanned.length
130
- blocks[name] = blocks[index+1] = Block.new(name, content, options)
88
+ content.chomp! unless index == scanned.length
89
+
90
+ blocks[name] = content
91
+ ((meta_info['blocks'] ||= {})[name] ||= {}).merge!(options) unless options.empty?
131
92
  end
132
- meta_info.delete('blocks')
93
+ meta_info['blocks'].delete_if {|k,v| v.empty?} if meta_info.has_key?('blocks')
94
+ meta_info.delete('blocks') if meta_info.has_key?('blocks') && meta_info['blocks'].empty?
133
95
  blocks
134
96
  end
97
+ private :parse_blocks
135
98
 
136
99
  end
137
100