nanoc 3.2.4 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. data/.gemtest +0 -0
  2. data/ChangeLog +3 -0
  3. data/Gemfile +32 -0
  4. data/LICENSE +19 -0
  5. data/NEWS.md +470 -0
  6. data/README.md +114 -0
  7. data/Rakefile +14 -0
  8. data/bin/nanoc +7 -27
  9. data/bin/nanoc3 +3 -0
  10. data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
  11. data/lib/nanoc.rb +41 -0
  12. data/lib/nanoc/base.rb +49 -0
  13. data/lib/nanoc/base/compilation/checksum_store.rb +57 -0
  14. data/lib/nanoc/base/compilation/compiled_content_cache.rb +62 -0
  15. data/lib/nanoc/base/compilation/compiler.rb +458 -0
  16. data/lib/nanoc/base/compilation/compiler_dsl.rb +214 -0
  17. data/lib/nanoc/base/compilation/dependency_tracker.rb +200 -0
  18. data/lib/nanoc/base/compilation/filter.rb +165 -0
  19. data/lib/nanoc/base/compilation/item_rep_proxy.rb +103 -0
  20. data/lib/nanoc/base/compilation/item_rep_recorder_proxy.rb +102 -0
  21. data/lib/nanoc/base/compilation/outdatedness_checker.rb +223 -0
  22. data/lib/nanoc/base/compilation/outdatedness_reasons.rb +46 -0
  23. data/lib/nanoc/base/compilation/rule.rb +73 -0
  24. data/lib/nanoc/base/compilation/rule_context.rb +84 -0
  25. data/lib/nanoc/base/compilation/rule_memory_calculator.rb +40 -0
  26. data/lib/nanoc/base/compilation/rule_memory_store.rb +53 -0
  27. data/lib/nanoc/base/compilation/rules_collection.rb +243 -0
  28. data/lib/nanoc/base/context.rb +47 -0
  29. data/lib/nanoc/base/core_ext.rb +6 -0
  30. data/lib/nanoc/base/core_ext/array.rb +62 -0
  31. data/lib/nanoc/base/core_ext/hash.rb +63 -0
  32. data/lib/nanoc/base/core_ext/pathname.rb +26 -0
  33. data/lib/nanoc/base/core_ext/string.rb +46 -0
  34. data/lib/nanoc/base/directed_graph.rb +275 -0
  35. data/lib/nanoc/base/errors.rb +211 -0
  36. data/lib/nanoc/base/memoization.rb +67 -0
  37. data/lib/nanoc/base/notification_center.rb +84 -0
  38. data/lib/nanoc/base/ordered_hash.rb +200 -0
  39. data/lib/nanoc/base/plugin_registry.rb +181 -0
  40. data/lib/nanoc/base/result_data/item_rep.rb +492 -0
  41. data/lib/nanoc/base/source_data/code_snippet.rb +58 -0
  42. data/lib/nanoc/base/source_data/configuration.rb +24 -0
  43. data/lib/nanoc/base/source_data/data_source.rb +234 -0
  44. data/lib/nanoc/base/source_data/item.rb +301 -0
  45. data/lib/nanoc/base/source_data/layout.rb +130 -0
  46. data/lib/nanoc/base/source_data/site.rb +361 -0
  47. data/lib/nanoc/base/store.rb +135 -0
  48. data/lib/nanoc/cli.rb +137 -0
  49. data/lib/nanoc/cli/command_runner.rb +104 -0
  50. data/lib/nanoc/cli/commands/autocompile.rb +58 -0
  51. data/lib/nanoc/cli/commands/compile.rb +297 -0
  52. data/lib/nanoc/cli/commands/create_item.rb +60 -0
  53. data/lib/nanoc/cli/commands/create_layout.rb +73 -0
  54. data/lib/nanoc/cli/commands/create_site.rb +411 -0
  55. data/lib/nanoc/cli/commands/debug.rb +117 -0
  56. data/lib/nanoc/cli/commands/deploy.rb +79 -0
  57. data/lib/nanoc/cli/commands/info.rb +98 -0
  58. data/lib/nanoc/cli/commands/nanoc.rb +38 -0
  59. data/lib/nanoc/cli/commands/prune.rb +50 -0
  60. data/lib/nanoc/cli/commands/update.rb +70 -0
  61. data/lib/nanoc/cli/commands/view.rb +82 -0
  62. data/lib/nanoc/cli/commands/watch.rb +124 -0
  63. data/lib/nanoc/cli/error_handler.rb +199 -0
  64. data/lib/nanoc/cli/logger.rb +92 -0
  65. data/lib/nanoc/data_sources.rb +29 -0
  66. data/lib/nanoc/data_sources/deprecated/delicious.rb +42 -0
  67. data/lib/nanoc/data_sources/deprecated/last_fm.rb +87 -0
  68. data/lib/nanoc/data_sources/deprecated/twitter.rb +38 -0
  69. data/lib/nanoc/data_sources/filesystem.rb +299 -0
  70. data/lib/nanoc/data_sources/filesystem_unified.rb +121 -0
  71. data/lib/nanoc/data_sources/filesystem_verbose.rb +91 -0
  72. data/lib/nanoc/extra.rb +24 -0
  73. data/lib/nanoc/extra/auto_compiler.rb +103 -0
  74. data/lib/nanoc/extra/chick.rb +125 -0
  75. data/lib/nanoc/extra/core_ext.rb +6 -0
  76. data/lib/nanoc/extra/core_ext/enumerable.rb +33 -0
  77. data/lib/nanoc/extra/core_ext/pathname.rb +30 -0
  78. data/lib/nanoc/extra/core_ext/time.rb +19 -0
  79. data/lib/nanoc/extra/deployer.rb +47 -0
  80. data/lib/nanoc/extra/deployers.rb +15 -0
  81. data/lib/nanoc/extra/deployers/fog.rb +98 -0
  82. data/lib/nanoc/extra/deployers/rsync.rb +70 -0
  83. data/lib/nanoc/extra/file_proxy.rb +40 -0
  84. data/lib/nanoc/extra/pruner.rb +86 -0
  85. data/lib/nanoc/extra/validators.rb +12 -0
  86. data/lib/nanoc/extra/validators/links.rb +268 -0
  87. data/lib/nanoc/extra/validators/w3c.rb +95 -0
  88. data/lib/nanoc/extra/vcs.rb +66 -0
  89. data/lib/nanoc/extra/vcses.rb +17 -0
  90. data/lib/nanoc/extra/vcses/bazaar.rb +25 -0
  91. data/lib/nanoc/extra/vcses/dummy.rb +24 -0
  92. data/lib/nanoc/extra/vcses/git.rb +25 -0
  93. data/lib/nanoc/extra/vcses/mercurial.rb +25 -0
  94. data/lib/nanoc/extra/vcses/subversion.rb +25 -0
  95. data/lib/nanoc/filters.rb +59 -0
  96. data/lib/nanoc/filters/asciidoc.rb +38 -0
  97. data/lib/nanoc/filters/bluecloth.rb +19 -0
  98. data/lib/nanoc/filters/coderay.rb +21 -0
  99. data/lib/nanoc/filters/coffeescript.rb +20 -0
  100. data/lib/nanoc/filters/colorize_syntax.rb +298 -0
  101. data/lib/nanoc/filters/erb.rb +38 -0
  102. data/lib/nanoc/filters/erubis.rb +34 -0
  103. data/lib/nanoc/filters/haml.rb +27 -0
  104. data/lib/nanoc/filters/kramdown.rb +20 -0
  105. data/lib/nanoc/filters/less.rb +53 -0
  106. data/lib/nanoc/filters/markaby.rb +20 -0
  107. data/lib/nanoc/filters/maruku.rb +20 -0
  108. data/lib/nanoc/filters/mustache.rb +24 -0
  109. data/lib/nanoc/filters/rainpress.rb +19 -0
  110. data/lib/nanoc/filters/rdiscount.rb +22 -0
  111. data/lib/nanoc/filters/rdoc.rb +33 -0
  112. data/lib/nanoc/filters/redcarpet.rb +62 -0
  113. data/lib/nanoc/filters/redcloth.rb +47 -0
  114. data/lib/nanoc/filters/relativize_paths.rb +94 -0
  115. data/lib/nanoc/filters/rubypants.rb +20 -0
  116. data/lib/nanoc/filters/sass.rb +74 -0
  117. data/lib/nanoc/filters/slim.rb +25 -0
  118. data/lib/nanoc/filters/typogruby.rb +23 -0
  119. data/lib/nanoc/filters/uglify_js.rb +42 -0
  120. data/lib/nanoc/filters/xsl.rb +46 -0
  121. data/lib/nanoc/filters/yui_compressor.rb +23 -0
  122. data/lib/nanoc/helpers.rb +16 -0
  123. data/lib/nanoc/helpers/blogging.rb +319 -0
  124. data/lib/nanoc/helpers/breadcrumbs.rb +40 -0
  125. data/lib/nanoc/helpers/capturing.rb +138 -0
  126. data/lib/nanoc/helpers/filtering.rb +50 -0
  127. data/lib/nanoc/helpers/html_escape.rb +55 -0
  128. data/lib/nanoc/helpers/link_to.rb +151 -0
  129. data/lib/nanoc/helpers/rendering.rb +140 -0
  130. data/lib/nanoc/helpers/tagging.rb +71 -0
  131. data/lib/nanoc/helpers/text.rb +44 -0
  132. data/lib/nanoc/helpers/xml_sitemap.rb +76 -0
  133. data/lib/nanoc/tasks.rb +10 -0
  134. data/lib/nanoc/tasks/clean.rake +16 -0
  135. data/lib/nanoc/tasks/clean.rb +29 -0
  136. data/lib/nanoc/tasks/deploy/rsync.rake +16 -0
  137. data/lib/nanoc/tasks/validate.rake +92 -0
  138. data/nanoc.gemspec +49 -0
  139. data/tasks/doc.rake +16 -0
  140. data/tasks/test.rake +46 -0
  141. data/test/base/core_ext/array_spec.rb +73 -0
  142. data/test/base/core_ext/hash_spec.rb +98 -0
  143. data/test/base/core_ext/pathname_spec.rb +27 -0
  144. data/test/base/core_ext/string_spec.rb +37 -0
  145. data/test/base/test_checksum_store.rb +35 -0
  146. data/test/base/test_code_snippet.rb +31 -0
  147. data/test/base/test_compiler.rb +403 -0
  148. data/test/base/test_compiler_dsl.rb +161 -0
  149. data/test/base/test_context.rb +31 -0
  150. data/test/base/test_data_source.rb +46 -0
  151. data/test/base/test_dependency_tracker.rb +262 -0
  152. data/test/base/test_directed_graph.rb +288 -0
  153. data/test/base/test_filter.rb +83 -0
  154. data/test/base/test_item.rb +179 -0
  155. data/test/base/test_item_rep.rb +579 -0
  156. data/test/base/test_layout.rb +59 -0
  157. data/test/base/test_memoization.rb +90 -0
  158. data/test/base/test_notification_center.rb +34 -0
  159. data/test/base/test_outdatedness_checker.rb +394 -0
  160. data/test/base/test_plugin.rb +30 -0
  161. data/test/base/test_rule.rb +19 -0
  162. data/test/base/test_rule_context.rb +65 -0
  163. data/test/base/test_site.rb +190 -0
  164. data/test/cli/commands/test_compile.rb +33 -0
  165. data/test/cli/commands/test_create_item.rb +14 -0
  166. data/test/cli/commands/test_create_layout.rb +28 -0
  167. data/test/cli/commands/test_create_site.rb +24 -0
  168. data/test/cli/commands/test_deploy.rb +74 -0
  169. data/test/cli/commands/test_help.rb +12 -0
  170. data/test/cli/commands/test_info.rb +11 -0
  171. data/test/cli/commands/test_prune.rb +98 -0
  172. data/test/cli/commands/test_update.rb +10 -0
  173. data/test/cli/test_cli.rb +102 -0
  174. data/test/cli/test_error_handler.rb +29 -0
  175. data/test/cli/test_logger.rb +10 -0
  176. data/test/data_sources/test_filesystem.rb +433 -0
  177. data/test/data_sources/test_filesystem_unified.rb +536 -0
  178. data/test/data_sources/test_filesystem_verbose.rb +357 -0
  179. data/test/extra/core_ext/test_enumerable.rb +30 -0
  180. data/test/extra/core_ext/test_pathname.rb +17 -0
  181. data/test/extra/core_ext/test_time.rb +15 -0
  182. data/test/extra/deployers/test_fog.rb +67 -0
  183. data/test/extra/deployers/test_rsync.rb +100 -0
  184. data/test/extra/test_auto_compiler.rb +417 -0
  185. data/test/extra/test_file_proxy.rb +19 -0
  186. data/test/extra/test_vcs.rb +22 -0
  187. data/test/extra/validators/test_links.rb +62 -0
  188. data/test/extra/validators/test_w3c.rb +47 -0
  189. data/test/filters/test_asciidoc.rb +22 -0
  190. data/test/filters/test_bluecloth.rb +18 -0
  191. data/test/filters/test_coderay.rb +44 -0
  192. data/test/filters/test_coffeescript.rb +18 -0
  193. data/test/filters/test_colorize_syntax.rb +379 -0
  194. data/test/filters/test_erb.rb +105 -0
  195. data/test/filters/test_erubis.rb +78 -0
  196. data/test/filters/test_haml.rb +96 -0
  197. data/test/filters/test_kramdown.rb +18 -0
  198. data/test/filters/test_less.rb +113 -0
  199. data/test/filters/test_markaby.rb +24 -0
  200. data/test/filters/test_maruku.rb +18 -0
  201. data/test/filters/test_mustache.rb +25 -0
  202. data/test/filters/test_rainpress.rb +29 -0
  203. data/test/filters/test_rdiscount.rb +31 -0
  204. data/test/filters/test_rdoc.rb +18 -0
  205. data/test/filters/test_redcarpet.rb +73 -0
  206. data/test/filters/test_redcloth.rb +33 -0
  207. data/test/filters/test_relativize_paths.rb +533 -0
  208. data/test/filters/test_rubypants.rb +18 -0
  209. data/test/filters/test_sass.rb +229 -0
  210. data/test/filters/test_slim.rb +35 -0
  211. data/test/filters/test_typogruby.rb +21 -0
  212. data/test/filters/test_uglify_js.rb +30 -0
  213. data/test/filters/test_xsl.rb +105 -0
  214. data/test/filters/test_yui_compressor.rb +44 -0
  215. data/test/gem_loader.rb +11 -0
  216. data/test/helper.rb +207 -0
  217. data/test/helpers/test_blogging.rb +754 -0
  218. data/test/helpers/test_breadcrumbs.rb +81 -0
  219. data/test/helpers/test_capturing.rb +41 -0
  220. data/test/helpers/test_filtering.rb +106 -0
  221. data/test/helpers/test_html_escape.rb +32 -0
  222. data/test/helpers/test_link_to.rb +249 -0
  223. data/test/helpers/test_rendering.rb +89 -0
  224. data/test/helpers/test_tagging.rb +87 -0
  225. data/test/helpers/test_text.rb +24 -0
  226. data/test/helpers/test_xml_sitemap.rb +103 -0
  227. data/test/tasks/test_clean.rb +67 -0
  228. metadata +327 -15
  229. data/bin/nanoc-select +0 -86
  230. data/lib/nanoc-select.rb +0 -11
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Provides support for breadcrumbs, which allow the user to go up in the
6
+ # page hierarchy.
7
+ module Breadcrumbs
8
+
9
+ # Creates a breadcrumb trail leading from the current item to its parent,
10
+ # to its parent’s parent, etc, until the root item is reached. This
11
+ # function does not require that each intermediate item exist; for
12
+ # example, if there is no `/foo/` item, breadcrumbs for a `/foo/bar/` item
13
+ # will contain a `nil` element.
14
+ #
15
+ # @return [Array] The breadcrumbs, starting with the root item and ending
16
+ # with the item itself
17
+ def breadcrumbs_trail
18
+ breadcrumbs_for_identifier(@item.identifier)
19
+ end
20
+
21
+ def item_with_identifier(identifier)
22
+ @identifier_cache ||= {}
23
+ @identifier_cache[identifier] ||= begin
24
+ @items.find { |i| i.identifier == identifier }
25
+ end
26
+ end
27
+
28
+ def breadcrumbs_for_identifier(identifier)
29
+ @breadcrumbs_cache ||= {}
30
+ @breadcrumbs_cache[identifier] ||= begin
31
+ head = (identifier == '/' ? [] : breadcrumbs_for_identifier(identifier.sub(/[^\/]+\/$/, '')) )
32
+ tail = [ item_with_identifier(identifier) ]
33
+
34
+ head + tail
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Provides functionality for “capturing” content in one place and reusing
6
+ # this content elsewhere.
7
+ #
8
+ # For example, suppose you want the sidebar of your site to contain a short
9
+ # summary of the item. You could put the summary in the meta file, but
10
+ # that’s not possible when the summary contains eRuby. You could also put
11
+ # the sidebar inside the actual item, but that’s not very pretty. Instead,
12
+ # you write the summary on the item itself, but capture it, and print it in
13
+ # the sidebar layout.
14
+ #
15
+ # This helper has been tested with ERB and Haml. Other filters may not work
16
+ # correctly.
17
+ #
18
+ # @example Capturing content for a summary
19
+ #
20
+ # <% content_for :summary do %>
21
+ # <p>On this item, nanoc is introduced, blah blah.</p>
22
+ # <% end %>
23
+ #
24
+ # @example Showing captured content in a sidebar
25
+ #
26
+ # <div id="sidebar">
27
+ # <h3>Summary</h3>
28
+ # <%= content_for(@item, :summary) || '(no summary)' %>
29
+ # </div>
30
+ #
31
+ # @example Showing captured content in a sidebar the old, deprecated way (do not use or I will become very angry)
32
+ #
33
+ # <div id="sidebar">
34
+ # <h3>Summary</h3>
35
+ # <%= @item[:content_for_summary] || '(no summary)' %>
36
+ # </div>
37
+ module Capturing
38
+
39
+ # @api private
40
+ class CapturesStore
41
+
42
+ require 'singleton'
43
+ include Singleton
44
+
45
+ def initialize
46
+ @store = {}
47
+ end
48
+
49
+ def []=(item, name, content)
50
+ @store[item.identifier] ||= {}
51
+ @store[item.identifier][name] = content
52
+ end
53
+
54
+ def [](item, name)
55
+ @store[item.identifier] ||= {}
56
+ @store[item.identifier][name]
57
+ end
58
+
59
+ end
60
+
61
+ # @overload content_for(name, &block)
62
+ #
63
+ # Captures the content inside the block and stores it so that it can be
64
+ # referenced later on. The same method, {#content_for}, is used for
65
+ # getting the captured content as well as setting it. When capturing,
66
+ # the content of the block itself will not be outputted.
67
+ #
68
+ # For backwards compatibility, it is also possible to fetch the captured
69
+ # content by getting the contents of the attribute named `content_for_`
70
+ # followed by the given name. This way of accessing captures is
71
+ # deprecated.
72
+ #
73
+ # @param [Symbol, String] name The base name of the attribute into which
74
+ # the content should be stored
75
+ #
76
+ # @return [void]
77
+ #
78
+ # @overload content_for(item, name)
79
+ #
80
+ # Fetches the capture with the given name from the given item and
81
+ # returns it.
82
+ #
83
+ # @param [Nanoc::Item] item The item for which to get the capture
84
+ #
85
+ # @param [Symbol, String] name The name of the capture to fetch
86
+ #
87
+ # @return [String] The stored captured content
88
+ def content_for(*args, &block)
89
+ if block_given? # Set content
90
+ # Get args
91
+ if args.size != 1
92
+ raise ArgumentError, "expected 1 argument (the name " +
93
+ "of the capture) but got #{args.size} instead"
94
+ end
95
+ name = args[0]
96
+
97
+ # Capture and store
98
+ content = capture(&block)
99
+ CapturesStore.instance[@item, name.to_sym] = content
100
+ else # Get content
101
+ # Get args
102
+ if args.size != 2
103
+ raise ArgumentError, "expected 2 arguments (the item " +
104
+ "and the name of the capture) but got #{args.size} instead"
105
+ end
106
+ item = args[0]
107
+ name = args[1]
108
+
109
+ # Get content
110
+ CapturesStore.instance[item, name.to_sym]
111
+ end
112
+ end
113
+
114
+ # Evaluates the given block and returns its contents. The contents of the
115
+ # block is not outputted.
116
+ #
117
+ # @return [String] The captured result
118
+ def capture(&block)
119
+ # Get erbout so far
120
+ erbout = eval('_erbout', block.binding)
121
+ erbout_length = erbout.length
122
+
123
+ # Execute block
124
+ block.call
125
+
126
+ # Get new piece of erbout
127
+ erbout_addition = erbout[erbout_length..-1]
128
+
129
+ # Remove addition
130
+ erbout[erbout_length..-1] = ''
131
+
132
+ # Done
133
+ erbout_addition
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Provides functionality for filtering parts of an item or a layout.
6
+ module Filtering
7
+
8
+ require 'nanoc/helpers/capturing'
9
+ include Nanoc::Helpers::Capturing
10
+
11
+ # Filters the content in the given block and outputs it. This function
12
+ # does not return anything; instead, the filtered contents is directly
13
+ # appended to the output buffer (`_erbout`).
14
+ #
15
+ # This function has been tested with ERB and Haml. Other filters may not
16
+ # work correctly.
17
+ #
18
+ # @example Running a filter on a part of an item or layout
19
+ #
20
+ # <p>Lorem ipsum dolor sit amet...</p>
21
+ # <% filter :rubypants do %>
22
+ # <p>Consectetur adipisicing elit...</p>
23
+ # <% end %>
24
+ #
25
+ # @param [Symbol] filter_name The name of the filter to run on the
26
+ # contents of the block
27
+ #
28
+ # @param [Hash] argument Arguments to pass to the filter
29
+ #
30
+ # @return [void]
31
+ def filter(filter_name, arguments={}, &block)
32
+ # Capture block
33
+ data = capture(&block)
34
+
35
+ # Find filter
36
+ klass = Nanoc::Filter.named(filter_name)
37
+ raise Nanoc::Errors::UnknownFilter.new(filter_name) if klass.nil?
38
+ filter = klass.new(@item_rep.assigns)
39
+
40
+ # Filter captured data
41
+ filtered_data = filter.run(data, arguments)
42
+
43
+ # Append filtered data to buffer
44
+ buffer = eval('_erbout', block.binding)
45
+ buffer << filtered_data
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Contains functionality for HTML-escaping strings.
6
+ module HTMLEscape
7
+
8
+ require 'nanoc/helpers/capturing'
9
+ include Nanoc::Helpers::Capturing
10
+
11
+ # Returns the HTML-escaped representation of the given string or the given
12
+ # block. Only `&`, `<`, `>` and `"` are escaped. When given a block, the
13
+ # contents of the block will be escaped and appended to the output buffer,
14
+ # `_erbout`.
15
+ #
16
+ # @example Escaping a string
17
+ #
18
+ # h('<br>')
19
+ # # => '&lt;br&gt;'
20
+ #
21
+ # @example Escaping with a block
22
+ #
23
+ # <% h do %>
24
+ # <h1>Hello <em>world</em>!</h1>
25
+ # <% end %>
26
+ # # The buffer will now contain “&lt;h1&gt;Hello &lt;em&gt;world&lt;/em&gt;!&lt;/h1&gt;”
27
+ #
28
+ # @param [String] string The string to escape
29
+ #
30
+ # @return [String] The escaped string
31
+ def html_escape(string=nil, &block)
32
+ if block_given?
33
+ # Capture and escape block
34
+ data = capture(&block)
35
+ escaped_data = html_escape(data)
36
+
37
+ # Append filtered data to buffer
38
+ buffer = eval('_erbout', block.binding)
39
+ buffer << escaped_data
40
+ elsif string
41
+ string.gsub('&', '&amp;').
42
+ gsub('<', '&lt;').
43
+ gsub('>', '&gt;').
44
+ gsub('"', '&quot;')
45
+ else
46
+ raise RuntimeError, "The #html_escape or #h function needs either a " \
47
+ "string or a block to HTML-escape, but neither a string nor a block was given"
48
+ end
49
+ end
50
+
51
+ alias h html_escape
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,151 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Contains functions for linking to items and item representations.
6
+ module LinkTo
7
+
8
+ require 'nanoc/helpers/html_escape'
9
+ include Nanoc::Helpers::HTMLEscape
10
+
11
+ # Creates a HTML link to the given path or item representation, and with
12
+ # the given text. All attributes of the `a` element, including the `href`
13
+ # attribute, will be HTML-escaped; the contents of the `a` element, which
14
+ # can contain markup, will not be HTML-escaped. The HTML-escaping is done
15
+ # using {Nanoc::Helpers::HTMLEscape#html_escape}.
16
+ #
17
+ # @param [String] text The visible link text
18
+ #
19
+ # @param [String, Nanoc::Item, Nanoc::ItemRep] target The path/URL,
20
+ # item or item representation that should be linked to
21
+ #
22
+ # @param [Hash] attributes A hash containing HTML attributes (e.g.
23
+ # `rel`, `title`, …) that will be added to the link.
24
+ #
25
+ # @return [String] The link text
26
+ #
27
+ # @example Linking to a path
28
+ #
29
+ # link_to('Blog', '/blog/')
30
+ # # => '<a href="/blog/">Blog</a>'
31
+ #
32
+ # @example Linking to an item
33
+ #
34
+ # about = @items.find { |i| i.identifier == '/about/' }
35
+ # link_to('About Me', about)
36
+ # # => '<a href="/about.html">About Me</a>'
37
+ #
38
+ # @example Linking to an item representation
39
+ #
40
+ # about = @items.find { |i| i.identifier == '/about/' }
41
+ # link_to('My vCard', about.rep(:vcard))
42
+ # # => '<a href="/about.vcf">My vCard</a>'
43
+ #
44
+ # @example Linking with custom attributes
45
+ #
46
+ # link_to('Blog', '/blog/', :title => 'My super cool blog')
47
+ # # => '<a title="My super cool blog" href="/blog/">Blog</a>'
48
+ def link_to(text, target, attributes={})
49
+ # Find path
50
+ if target.is_a?(String)
51
+ path = target
52
+ else
53
+ path = target.path
54
+ raise RuntimeError, "Cannot create a link to #{target.inspect} because this target is not outputted (its routing rule returns nil)" if path.nil?
55
+ end
56
+
57
+ # Join attributes
58
+ attributes = attributes.inject('') do |memo, (key, value)|
59
+ memo + key.to_s + '="' + h(value) + '" '
60
+ end
61
+
62
+ # Create link
63
+ "<a #{attributes}href=\"#{h path}\">#{text}</a>"
64
+ end
65
+
66
+ # Creates a HTML link using {#link_to}, except when the linked item is
67
+ # the current one. In this case, a span element with class “active” and
68
+ # with the given text will be returned. The HTML-escaping rules for
69
+ # {#link_to} apply here as well.
70
+ #
71
+ # @param [String] text The visible link text
72
+ #
73
+ # @param [String, Nanoc::Item, Nanoc::ItemRep] target The path/URL,
74
+ # item or item representation that should be linked to
75
+ #
76
+ # @param [Hash] attributes A hash containing HTML attributes (e.g.
77
+ # `rel`, `title`, …) that will be added to the link.
78
+ #
79
+ # @return [String] The link text
80
+ #
81
+ # @example Linking to a different page
82
+ #
83
+ # link_to_unless_current('Blog', '/blog/')
84
+ # # => '<a href="/blog/">Blog</a>'
85
+ #
86
+ # @example Linking to the same page
87
+ #
88
+ # link_to_unless_current('This Item', @item)
89
+ # # => '<span class="active" title="You\'re here.">This Item</span>'
90
+ def link_to_unless_current(text, target, attributes={})
91
+ # Find path
92
+ path = target.is_a?(String) ? target : target.path
93
+
94
+ if @item_rep && @item_rep.path == path
95
+ # Create message
96
+ "<span class=\"active\" title=\"You're here.\">#{text}</span>"
97
+ else
98
+ link_to(text, target, attributes)
99
+ end
100
+ end
101
+
102
+ # Returns the relative path from the current item to the given path or
103
+ # item representation. The returned path will not be HTML-escaped.
104
+ #
105
+ # @param [String, Nanoc::Item, Nanoc::ItemRep] target The path/URL,
106
+ # item or item representation to which the relative path should be
107
+ # generated
108
+ #
109
+ # @return [String] The relative path to the target
110
+ #
111
+ # @example
112
+ #
113
+ # # if the current item's path is /foo/bar/
114
+ # relative_path_to('/foo/qux/')
115
+ # # => '../qux/'
116
+ def relative_path_to(target)
117
+ require 'pathname'
118
+
119
+ # Find path
120
+ if target.is_a?(String)
121
+ path = target
122
+ else
123
+ path = target.path
124
+ raise RuntimeError, "Cannot get the relative path to #{target.inspect} because this target is not outputted (its routing rule returns nil)" if path.nil?
125
+ end
126
+
127
+ # Get source and destination paths
128
+ dst_path = Pathname.new(path)
129
+ raise RuntimeError, "Cannot get the relative path to #{path} because the current item representation, #{@item_rep.inspect}, is not outputted (its routing rule returns nil)" if @item_rep.path.nil?
130
+ src_path = Pathname.new(@item_rep.path)
131
+
132
+ # Calculate the relative path (method depends on whether destination is
133
+ # a directory or not).
134
+ if src_path.to_s[-1,1] != '/'
135
+ relative_path = dst_path.relative_path_from(src_path.dirname).to_s
136
+ else
137
+ relative_path = dst_path.relative_path_from(src_path).to_s
138
+ end
139
+
140
+ # Add trailing slash if necessary
141
+ if dst_path.to_s[-1,1] == '/'
142
+ relative_path << '/'
143
+ end
144
+
145
+ # Done
146
+ relative_path
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,140 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::Helpers
4
+
5
+ # Provides functionality for rendering layouts as partials.
6
+ module Rendering
7
+
8
+ include Nanoc::Helpers::Capturing
9
+
10
+ # Renders the given layout. The given layout will be run through the first
11
+ # matching layout rule.
12
+ #
13
+ # When this method is invoked _without_ a block, the return value will be
14
+ # the rendered layout (a string) and `_erbout` will not be modified.
15
+ #
16
+ # When this method is invoked _with_ a block, an empty string will be
17
+ # returned and the rendered content will be appended to `_erbout`. In this
18
+ # case, the content of the block will be captured (using the
19
+ # {Nanoc::Helpers::Capturing} helper) and this content will be made
20
+ # available with `yield`. In other words, a `yield` inside the partial
21
+ # will output the content of the block passed to the method.
22
+ #
23
+ # (For the curious: the reason why {#render} with a block has this
24
+ # behaviour of returning an empty string and modifying `_erbout` is
25
+ # because ERB does not support combining the `<%= ... %>` form with a
26
+ # method call that takes a block.)
27
+ #
28
+ # The assigns (`@item`, `@config`, …) will be available in the partial. It
29
+ # is also possible to pass custom assigns to the method; these assigns
30
+ # will be made available as instance variables inside the partial.
31
+ #
32
+ # @param [String] identifier The identifier of the layout that should be
33
+ # rendered
34
+ #
35
+ # @param [Hash] other_assigns A hash containing extra assigns that will be
36
+ # made available as instance variables in the partial
37
+ #
38
+ # @example Rendering a head and a foot partial around some text
39
+ #
40
+ # <%= render 'head' %> - MIDDLE - <%= render 'foot' %>
41
+ # # => "HEAD - MIDDLE - FOOT"
42
+ #
43
+ # @example Rendering a head partial with a custom title
44
+ #
45
+ # # The 'head' layout
46
+ # <h1><%= @title %></h1>
47
+ #
48
+ # # The item/layout where the partial is rendered
49
+ # <%= render 'head', :title => 'Foo' %>
50
+ # # => "<h1>Foo</h1>"
51
+ #
52
+ # @example Yielding inside a partial
53
+ #
54
+ # # The 'box' partial
55
+ # <div class="box">
56
+ # <%= yield %>
57
+ # </div>
58
+ #
59
+ # # The item/layout where the partial is rendered
60
+ # <% render 'box' do %>
61
+ # I'm boxy! Luvz!
62
+ # <% end %>
63
+ #
64
+ # # Result
65
+ # <div class="box">
66
+ # I'm boxy! Luvz!
67
+ # </div>
68
+ #
69
+ # @raise [Nanoc::Errors::UnknownLayout] if the given layout does not
70
+ # exist
71
+ #
72
+ # @raise [Nanoc::Errors::CannotDetermineFilter] if there is no layout
73
+ # rule for the given layout
74
+ #
75
+ # @raise [Nanoc::Errors::UnknownFilter] if the layout rule for the given
76
+ # layout specifies an unknown filter
77
+ #
78
+ # @return [String, nil] The rendered partial, or nil if this method was
79
+ # invoked with a block
80
+ def render(identifier, other_assigns={}, &block)
81
+ # Find layout
82
+ layout = @site.layouts.find { |l| l.identifier == identifier.cleaned_identifier }
83
+ raise Nanoc::Errors::UnknownLayout.new(identifier.cleaned_identifier) if layout.nil?
84
+
85
+ # Visit
86
+ Nanoc::NotificationCenter.post(:visit_started, layout)
87
+ Nanoc::NotificationCenter.post(:visit_ended, layout)
88
+
89
+ # Capture content, if any
90
+ captured_content = block_given? ? capture(&block) : nil
91
+
92
+ # Get assigns
93
+ assigns = {
94
+ :content => captured_content,
95
+ :item => @item,
96
+ :item_rep => @item_rep,
97
+ :items => @items,
98
+ :layout => layout,
99
+ :layouts => @layouts,
100
+ :config => @config,
101
+ :site => @site
102
+ }.merge(other_assigns)
103
+
104
+ # Get filter name
105
+ filter_name, filter_args = @site.compiler.rules_collection.filter_for_layout(layout)
106
+ raise Nanoc::Errors::CannotDetermineFilter.new(layout.identifier) if filter_name.nil?
107
+
108
+ # Get filter class
109
+ filter_class = Nanoc::Filter.named(filter_name)
110
+ raise Nanoc::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
111
+
112
+ # Create filter
113
+ filter = filter_class.new(assigns)
114
+
115
+ begin
116
+ # Notify start
117
+ Nanoc::NotificationCenter.post(:processing_started, layout)
118
+
119
+ # Layout
120
+ result = filter.run(layout.raw_content, filter_args)
121
+
122
+ # Append to erbout if we have a block
123
+ if block_given?
124
+ # Append result and return nothing
125
+ erbout = eval('_erbout', block.binding)
126
+ erbout << result
127
+ ''
128
+ else
129
+ # Return result
130
+ result
131
+ end
132
+ ensure
133
+ # Notify end
134
+ Nanoc::NotificationCenter.post(:processing_ended, layout)
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end