nanoc 3.2.4 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,214 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc
4
+
5
+ # Contains methods that will be executed by the site’s `Rules` file.
6
+ class CompilerDSL
7
+
8
+ # Creates a new compiler DSL for the given collection of rules.
9
+ #
10
+ # @api private
11
+ #
12
+ # @param [Nanoc::RulesCollection] rules_collection The collection of
13
+ # rules to modify when loading this DSL
14
+ #
15
+ # @param [Hash] config The site configuration
16
+ def initialize(rules_collection, config)
17
+ @rules_collection = rules_collection
18
+ @config = config
19
+ end
20
+
21
+ # Creates a preprocessor block that will be executed after all data is
22
+ # loaded, but before the site is compiled.
23
+ #
24
+ # @yield The block that will be executed before site compilation starts
25
+ #
26
+ # @return [void]
27
+ def preprocess(&block)
28
+ @rules_collection.preprocessor = block
29
+ end
30
+
31
+ # Creates a compilation rule for all items whose identifier match the
32
+ # given identifier, which may either be a string containing the *
33
+ # wildcard, or a regular expression.
34
+ #
35
+ # This rule will be applicable to reps with a name equal to `:default`;
36
+ # this can be changed by giving an explicit `:rep` parameter.
37
+ #
38
+ # An item rep will be compiled by calling the given block and passing the
39
+ # rep as a block argument.
40
+ #
41
+ # @param [String] identifier A pattern matching identifiers of items that
42
+ # should be compiled using this rule
43
+ #
44
+ # @option params [Symbol] :rep (:default) The name of the representation
45
+ # that should be compiled using this rule
46
+ #
47
+ # @yield The block that will be executed when an item matching this
48
+ # compilation rule needs to be compiled
49
+ #
50
+ # @return [void]
51
+ #
52
+ # @example Compiling the default rep of the `/foo/` item
53
+ #
54
+ # compile '/foo/' do
55
+ # rep.filter :erb
56
+ # end
57
+ #
58
+ # @example Compiling the `:raw` rep of the `/bar/` item
59
+ #
60
+ # compile '/bar/', :rep => :raw do
61
+ # # do nothing
62
+ # end
63
+ def compile(identifier, params={}, &block)
64
+ # Require block
65
+ raise ArgumentError.new("#compile requires a block") unless block_given?
66
+
67
+ # Get rep name
68
+ rep_name = params[:rep] || :default
69
+
70
+ # Create rule
71
+ rule = Rule.new(identifier_to_regex(identifier), rep_name, block)
72
+ @rules_collection.add_item_compilation_rule(rule)
73
+ end
74
+
75
+ # Creates a routing rule for all items whose identifier match the
76
+ # given identifier, which may either be a string containing the `*`
77
+ # wildcard, or a regular expression.
78
+ #
79
+ # This rule will be applicable to reps with a name equal to `:default`;
80
+ # this can be changed by giving an explicit `:rep` parameter.
81
+ #
82
+ # The path of an item rep will be determined by calling the given block
83
+ # and passing the rep as a block argument.
84
+ #
85
+ # @param [String] identifier A pattern matching identifiers of items that
86
+ # should be routed using this rule
87
+ #
88
+ # @option params [Symbol] :rep (:default) The name of the representation
89
+ # that should be routed using this rule
90
+ #
91
+ # @yield The block that will be executed when an item matching this
92
+ # compilation rule needs to be routed
93
+ #
94
+ # @return [void]
95
+ #
96
+ # @example Routing the default rep of the `/foo/` item
97
+ #
98
+ # route '/foo/' do
99
+ # item.identifier + 'index.html'
100
+ # end
101
+ #
102
+ # @example Routing the `:raw` rep of the `/bar/` item
103
+ #
104
+ # route '/bar/', :rep => :raw do
105
+ # '/raw' + item.identifier + 'index.txt'
106
+ # end
107
+ def route(identifier, params={}, &block)
108
+ # Require block
109
+ raise ArgumentError.new("#route requires a block") unless block_given?
110
+
111
+ # Get rep name
112
+ rep_name = params[:rep] || :default
113
+ snapshot_name = params[:snapshot] || :last
114
+
115
+ # Create rule
116
+ rule = Rule.new(identifier_to_regex(identifier), rep_name, block, :snapshot_name => snapshot_name)
117
+ @rules_collection.add_item_routing_rule(rule)
118
+ end
119
+
120
+ # Creates a layout rule for all layouts whose identifier match the given
121
+ # identifier, which may either be a string containing the * wildcard, or a
122
+ # regular expression. The layouts matching the identifier will be filtered
123
+ # using the filter specified in the second argument. The params hash
124
+ # contains filter arguments that will be passed to the filter.
125
+ #
126
+ # @param [String] identifier A pattern matching identifiers of layouts
127
+ # that should be filtered using this rule
128
+ #
129
+ # @param [Symbol] filter_name The name of the filter that should be run
130
+ # when processing the layout
131
+ #
132
+ # @param [Hash] params Extra filter arguments that should be passed to the
133
+ # filter when processing the layout (see {Nanoc::Filter#run})
134
+ #
135
+ # @return [void]
136
+ #
137
+ # @example Specifying the filter to use for a layout
138
+ #
139
+ # layout '/default/', :erb
140
+ #
141
+ # @example Using custom filter arguments for a layout
142
+ #
143
+ # layout '/custom/', :haml, :format => :html5
144
+ def layout(identifier, filter_name, params={})
145
+ @rules_collection.layout_filter_mapping[identifier_to_regex(identifier)] = [ filter_name, params ]
146
+ end
147
+
148
+ # Creates a pair of compilation and routing rules that indicate that the
149
+ # specified item(s) should be copied to the output folder as-is. The items
150
+ # are selected using an identifier, which may either be a string
151
+ # containing the `*` wildcard, or a regular expression.
152
+ #
153
+ # This meta-rule will be applicable to reps with a name equal to
154
+ # `:default`; this can be changed by giving an explicit `:rep` parameter.
155
+ #
156
+ # @param [String] identifier A pattern matching identifiers of items that
157
+ # should be processed using this meta-rule
158
+ #
159
+ # @option params [Symbol] :rep (:default) The name of the representation
160
+ # that should be routed using this rule
161
+ #
162
+ # @return [void]
163
+ #
164
+ # @since 3.2.0
165
+ #
166
+ # @example Copying the `/foo/` item as-is
167
+ #
168
+ # passthrough '/foo/'
169
+ #
170
+ # @example Copying the `:raw` rep of the `/bar/` item as-is
171
+ #
172
+ # passthrough '/bar/', :rep => :raw
173
+ def passthrough(identifier, params={})
174
+ # Require no block
175
+ raise ArgumentError.new("#passthrough does not require a block") if block_given?
176
+
177
+ # Get rep name
178
+ rep_name = params[:rep] || :default
179
+
180
+ # Create compilation rule
181
+ compilation_block = proc { }
182
+ compilation_rule = Rule.new(identifier_to_regex(identifier), rep_name, compilation_block)
183
+ @rules_collection.add_item_compilation_rule(compilation_rule, :before)
184
+
185
+ # Create routing rule
186
+ routing_block = proc do
187
+ item.identifier.chop + '.' + item[:extension]
188
+ end
189
+ routing_rule = Rule.new(identifier_to_regex(identifier), rep_name, routing_block)
190
+ @rules_collection.add_item_routing_rule(routing_rule, :before)
191
+ end
192
+
193
+ private
194
+
195
+ # Converts the given identifier, which can contain the '*' or '+'
196
+ # wildcard characters, matching zero or more resp. one or more
197
+ # characters, to a regex. For example, 'foo/*/bar' is transformed
198
+ # into /^foo\/(.*?)\/bar$/ and 'foo+' is transformed into /^foo(.+?)/.
199
+ def identifier_to_regex(identifier)
200
+ if identifier.is_a? String
201
+ # Add leading/trailing slashes if necessary
202
+ new_identifier = identifier.dup
203
+ new_identifier[/^/] = '/' if identifier[0,1] != '/'
204
+ new_identifier[/$/] = '/' unless [ '*', '/' ].include?(identifier[-1,1])
205
+
206
+ /^#{new_identifier.gsub('*', '(.*?)').gsub('+', '(.+?)')}$/
207
+ else
208
+ identifier
209
+ end
210
+ end
211
+
212
+ end
213
+
214
+ end
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc
4
+
5
+ # Responsible for remembering dependencies between items and layouts. It is
6
+ # used to speed up compilation by only letting an item be recompiled when it
7
+ # is outdated or any of its dependencies (or dependencies’ dependencies,
8
+ # etc) is outdated.
9
+ #
10
+ # The dependencies tracked by the dependency tracker are not dependencies
11
+ # based on an item’s or a layout’s content. When one object uses an
12
+ # attribute of another object, then this is also treated as a dependency.
13
+ # While dependencies based on an item’s or layout’s content (handled in
14
+ # {Nanoc::Compiler}) cannot be mutually recursive, the more general
15
+ # dependencies in Nanoc::DependencyTracker can (e.g. item A can use an
16
+ # attribute of item B and vice versa without problems).
17
+ #
18
+ # The dependency tracker remembers the dependency information between runs.
19
+ # Dependency information is stored in the `tmp/dependencies` file.
20
+ #
21
+ # @api private
22
+ class DependencyTracker < ::Nanoc::Store
23
+
24
+ # @return [Array<Nanoc::Item, Nanoc::Layout>] The list of items and
25
+ # layouts that are being tracked by the dependency tracker
26
+ attr_reader :objects
27
+
28
+ # @return [Nanoc::Compiler] The compiler that corresponds to this
29
+ # dependency tracker
30
+ attr_accessor :compiler
31
+
32
+ # Creates a new dependency tracker for the given items and layouts.
33
+ #
34
+ # @param [Array<Nanoc::Item, Nanoc::Layout>] objects The list of items
35
+ # and layouts whose dependencies should be managed
36
+ def initialize(objects)
37
+ super('tmp/dependencies', 4)
38
+
39
+ @objects = objects
40
+
41
+ @graph = Nanoc::DirectedGraph.new([ nil ] + @objects)
42
+ end
43
+
44
+ # Starts listening for dependency messages (`:visit_started` and
45
+ # `:visit_ended`) and start recording dependencies.
46
+ #
47
+ # @return [void]
48
+ def start
49
+ # Initialize dependency stack. An object will be pushed onto this stack
50
+ # when it is visited. Therefore, an object on the stack always depends
51
+ # on all objects pushed above it.
52
+ @stack = []
53
+
54
+ # Register start of visits
55
+ Nanoc::NotificationCenter.on(:visit_started, self) do |obj|
56
+ if !@stack.empty?
57
+ Nanoc::NotificationCenter.post(:dependency_created, @stack.last, obj)
58
+ self.record_dependency(@stack.last, obj)
59
+ end
60
+ @stack.push(obj)
61
+ end
62
+
63
+ # Register end of visits
64
+ Nanoc::NotificationCenter.on(:visit_ended, self) do |obj|
65
+ @stack.pop
66
+ end
67
+ end
68
+
69
+ # Stop listening for dependency messages and stop recording dependencies.
70
+ #
71
+ # @return [void]
72
+ def stop
73
+ # Unregister
74
+ Nanoc::NotificationCenter.remove(:visit_started, self)
75
+ Nanoc::NotificationCenter.remove(:visit_ended, self)
76
+ end
77
+
78
+ # Returns the direct dependencies for the given object.
79
+ #
80
+ # The direct dependencies of the given object include the items and
81
+ # layouts that, when outdated will cause the given object to be marked as
82
+ # outdated. Indirect dependencies will not be returned (e.g. if A depends
83
+ # on B which depends on C, then the direct dependencies of A do not
84
+ # include C).
85
+ #
86
+ # The direct predecessors can include nil, which indicates an item that is
87
+ # no longer present in the site.
88
+ #
89
+ # @param [Nanoc::Item, Nanoc::Layout] object The object for
90
+ # which to fetch the direct predecessors
91
+ #
92
+ # @return [Array<Nanoc::Item, Nanoc::Layout, nil>] The direct
93
+ # predecessors of
94
+ # the given object
95
+ def objects_causing_outdatedness_of(object)
96
+ @graph.direct_predecessors_of(object)
97
+ end
98
+
99
+ # Returns the direct inverse dependencies for the given object.
100
+ #
101
+ # The direct inverse dependencies of the given object include the objects
102
+ # that will be marked as outdated when the given object is outdated.
103
+ # Indirect dependencies will not be returned (e.g. if A depends on B which
104
+ # depends on C, then the direct inverse dependencies of C do not include
105
+ # A).
106
+ #
107
+ # @param [Nanoc::Item, Nanoc::Layout] object The object for which to
108
+ # fetch the direct successors
109
+ #
110
+ # @return [Array<Nanoc::Item, Nanoc::Layout>] The direct successors of
111
+ # the given object
112
+ def objects_outdated_due_to(object)
113
+ @graph.direct_successors_of(object).compact
114
+ end
115
+
116
+ # Records a dependency from `src` to `dst` in the dependency graph. When
117
+ # `dst` is oudated, `src` will also become outdated.
118
+ #
119
+ # @param [Nanoc::Item, Nanoc::Layout] src The source of the dependency,
120
+ # i.e. the object that will become outdated if dst is outdated
121
+ #
122
+ # @param [Nanoc::Item, Nanoc::Layout] dst The destination of the
123
+ # dependency, i.e. the object that will cause the source to become
124
+ # outdated if the destination is outdated
125
+ #
126
+ # @return [void]
127
+ def record_dependency(src, dst)
128
+ # Warning! dst and src are *reversed* here!
129
+ @graph.add_edge(dst, src) unless src == dst
130
+ end
131
+
132
+ # Empties the list of dependencies for the given object. This is necessary
133
+ # before recompiling the given object, because otherwise old dependencies
134
+ # will stick around and new dependencies will appear twice. This function
135
+ # removes all incoming edges for the given vertex.
136
+ #
137
+ # @api private
138
+ #
139
+ # @param [Nanoc::Item, Nanoc::Layout] object The object for which to
140
+ # forget all dependencies
141
+ #
142
+ # @return [void]
143
+ def forget_dependencies_for(object)
144
+ @graph.delete_edges_to(object)
145
+ end
146
+
147
+ # @deprecated Use {#store} instead
148
+ def store_graph
149
+ self.store
150
+ end
151
+
152
+ # @deprecated Use {#load} instead
153
+ def load_graph
154
+ self.load
155
+ end
156
+
157
+ # @see Nanoc::Store#unload
158
+ def unload
159
+ @graph = Nanoc::DirectedGraph.new([ nil ] + @objects)
160
+ end
161
+
162
+ protected
163
+
164
+ def data
165
+ {
166
+ :edges => @graph.edges,
167
+ :vertices => @graph.vertices.map { |obj| obj && obj.reference }
168
+ }
169
+ end
170
+
171
+ def data=(new_data)
172
+ # Create new graph
173
+ @graph = Nanoc::DirectedGraph.new([ nil ] + @objects)
174
+
175
+ # Load vertices
176
+ previous_objects = new_data[:vertices].map do |reference|
177
+ @objects.find { |obj| reference == obj.reference }
178
+ end
179
+
180
+ # Load edges
181
+ new_data[:edges].each do |edge|
182
+ from_index, to_index = *edge
183
+ from = from_index && previous_objects[from_index]
184
+ to = to_index && previous_objects[to_index]
185
+ @graph.add_edge(from, to)
186
+ end
187
+
188
+ # Record dependency from all items on new items
189
+ new_objects = (@objects - previous_objects)
190
+ new_objects.each do |new_obj|
191
+ @objects.each do |obj|
192
+ next unless obj.is_a?(Nanoc::Item)
193
+ @graph.add_edge(new_obj, obj)
194
+ end
195
+ end
196
+ end
197
+
198
+ end
199
+
200
+ end
@@ -0,0 +1,165 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc
4
+
5
+ # Nanoc::Filter is responsible for filtering items. It is the superclass
6
+ # for all textual filters.
7
+ #
8
+ # A filter instance should only be used once. Filters should not be reused
9
+ # since they store state.
10
+ #
11
+ # When creating a filter with a hash containing assigned variables, those
12
+ # variables will be made available in the `@assigns` instance variable and
13
+ # the {#assigns} method. The assigns itself will also be available as
14
+ # instance variables and instance methods.
15
+ #
16
+ # @example Accessing assigns in different ways
17
+ #
18
+ # filter = SomeFilter.new({ :foo => 'bar' })
19
+ # filter.instance_eval { @assigns[:foo] }
20
+ # # => 'bar'
21
+ # filter.instance_eval { assigns[:foo] }
22
+ # # => 'bar'
23
+ # filter.instance_eval { @foo }
24
+ # # => 'bar'
25
+ # filter.instance_eval { foo }
26
+ # # => 'bar'
27
+ #
28
+ # @abstract Subclass and override {#run} to implement a custom filter.
29
+ class Filter < Context
30
+
31
+ # The path to the directory where temporary binary items are stored
32
+ TMP_BINARY_ITEMS_DIR = 'tmp/binary_items'
33
+
34
+ # A hash containing variables that will be made available during
35
+ # filtering.
36
+ #
37
+ # @return [Hash]
38
+ attr_reader :assigns
39
+
40
+ extend Nanoc::PluginRegistry::PluginMethods
41
+
42
+ class << self
43
+
44
+ # Sets the new type for the filter. The type can be `:binary` (default)
45
+ # or `:text`. The given argument can either be a symbol indicating both
46
+ # “from” and “to” types, or a hash where the only key is the “from” type
47
+ # and the only value is the “to” type.
48
+ #
49
+ # @example Specifying a text-to-text filter
50
+ #
51
+ # type :text
52
+ #
53
+ # @example Specifying a text-to-binary filter
54
+ #
55
+ # type :text => :binary
56
+ #
57
+ # @param [Symbol, Hash] arg The new type of this filter
58
+ #
59
+ # @return [void]
60
+ def type(arg)
61
+ if arg.is_a?(Hash)
62
+ @from, @to = arg.keys[0], arg.values[0]
63
+ else
64
+ @from, @to = arg, arg
65
+ end
66
+ end
67
+
68
+ # @return [Boolean] True if this filter can be applied to binary item
69
+ # representations, false otherwise
70
+ def from_binary?
71
+ (@from || :text) == :binary
72
+ end
73
+
74
+ # @return [Boolean] True if this filter results in a binary item
75
+ # representation, false otherwise
76
+ def to_binary?
77
+ (@to || :text) == :binary
78
+ end
79
+
80
+ end
81
+
82
+ # Creates a new filter that has access to the given assigns.
83
+ #
84
+ # @param [Hash] hash A hash containing variables that should be made
85
+ # available during filtering.
86
+ def initialize(hash={})
87
+ @assigns = hash
88
+ super
89
+ end
90
+
91
+ # Runs the filter on the given content or filename.
92
+ #
93
+ # @abstract
94
+ #
95
+ # @param [String] content_or_filename The unprocessed content that should
96
+ # be filtered (if the item is a textual item) or the path to the file
97
+ # that should be filtered (if the item is a binary item)
98
+ #
99
+ # @param [Hash] params A hash containing parameters. Filter subclasses can
100
+ # use these parameters to allow modifying the filter's behaviour.
101
+ #
102
+ # @return [String, void] If the filter output binary content, the return
103
+ # value is undefined; if the filter outputs textual content, the return
104
+ # value will be the filtered content.
105
+ def run(content_or_filename, params={})
106
+ raise NotImplementedError.new("Nanoc::Filter subclasses must implement #run")
107
+ end
108
+
109
+ # Returns a filename that is used to write data to. This method is only
110
+ # used on binary items. When running a binary filter on a file, the
111
+ # resulting file must end up in the location returned by this method.
112
+ #
113
+ # The returned filename will be absolute, so it is safe to change to
114
+ # another directory inside the filter.
115
+ #
116
+ # @return [String] The output filename
117
+ def output_filename
118
+ @output_filename ||= begin
119
+ FileUtils.mkdir_p(TMP_BINARY_ITEMS_DIR)
120
+ tempfile = Tempfile.new(filename.gsub(/[^a-z]/, '-'), TMP_BINARY_ITEMS_DIR)
121
+ new_filename = tempfile.path
122
+ tempfile.close!
123
+
124
+ File.expand_path(new_filename)
125
+ end
126
+ end
127
+
128
+ # Returns the filename associated with the item that is being filtered.
129
+ # It is in the format `item <identifier> (rep <name>)`.
130
+ #
131
+ # @return [String] The filename
132
+ def filename
133
+ if assigns[:layout]
134
+ "layout #{assigns[:layout].identifier}"
135
+ elsif assigns[:item]
136
+ "item #{assigns[:item].identifier} (rep #{assigns[:item_rep].name})"
137
+ else
138
+ '?'
139
+ end
140
+ end
141
+
142
+ # Creates a dependency from the item that is currently being filtered onto
143
+ # the given collection of items. In other words, require the given items
144
+ # to be compiled first before this items is processed.
145
+ #
146
+ # @param [Array<Nanoc::Item>] items The items that are depended on.
147
+ #
148
+ # @return [void]
149
+ def depend_on(items)
150
+ # Notify
151
+ items.each do |item|
152
+ Nanoc::NotificationCenter.post(:visit_started, item)
153
+ Nanoc::NotificationCenter.post(:visit_ended, item)
154
+ end
155
+
156
+ # Raise unmet dependency error if necessary
157
+ items.each do |item|
158
+ rep = item.reps.find { |r| !r.compiled? }
159
+ raise Nanoc::Errors::UnmetDependency.new(rep) if rep
160
+ end
161
+ end
162
+
163
+ end
164
+
165
+ end