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,181 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc
4
+
5
+ # The class responsible for keeping track of all loaded plugins, such as
6
+ # filters ({Nanoc::Filter}), data sources ({Nanoc::DataSource}) and VCSes
7
+ # ({Nanoc::Extra::VCS}).
8
+ class PluginRegistry
9
+
10
+ # A module that contains class methods for plugins. It provides functions
11
+ # for setting identifiers, registering plugins and finding plugins. Plugin
12
+ # classes should extend this module.
13
+ module PluginMethods
14
+
15
+ # Sets the identifiers for this plugin.
16
+ #
17
+ # @param [Array<Symbol>] identifier A list of identifiers to assign to
18
+ # this plugin.
19
+ #
20
+ # @return [void]
21
+ def identifiers(*identifiers)
22
+ register(self, *identifiers)
23
+ end
24
+
25
+ # Sets the identifier for this plugin.
26
+ #
27
+ # @param [Symbol] identifier An identifier to assign to this plugin.
28
+ #
29
+ # @return [void]
30
+ def identifier(identifier)
31
+ register(self, identifier)
32
+ end
33
+
34
+ # Registers the given class as a plugin with the given identifier.
35
+ #
36
+ # @param [Class, String] class_or_name The class to register, or a
37
+ # string containing the class name to register.
38
+ #
39
+ # @param [Array<Symbol>] identifiers A list of identifiers to assign to
40
+ # this plugin.
41
+ #
42
+ # @return [void]
43
+ def register(class_or_name, *identifiers)
44
+ # Find plugin class
45
+ klass = self
46
+ klass = klass.superclass while klass.superclass.respond_to?(:register)
47
+
48
+ # Register
49
+ registry = Nanoc::PluginRegistry.instance
50
+ registry.register(klass, class_or_name, *identifiers)
51
+ end
52
+
53
+ # Returns the plugin with the given name (identifier)
54
+ #
55
+ # @param [String] name The name of the plugin class to find
56
+ #
57
+ # @return [Class] The plugin class with the given name
58
+ def named(name)
59
+ Nanoc::Plugin.find(self, name)
60
+ end
61
+
62
+ end
63
+
64
+ # Returns the shared {PluginRegistry} instance, creating it if none exists
65
+ # yet.
66
+ #
67
+ # @return [Nanoc::PluginRegistry] The shared plugin registry
68
+ def self.instance
69
+ @instance ||= self.new
70
+ end
71
+
72
+ # Creates a new plugin registry. This should usually not be necessary; it
73
+ # is recommended to use the shared instance (obtained from
74
+ # {Nanoc::PluginRegistry.instance}).
75
+ def initialize
76
+ @map = {}
77
+ end
78
+
79
+ # Registers the given class as a plugin.
80
+ #
81
+ # @param [Class] superclass The superclass of the plugin. For example:
82
+ # {Nanoc::Filter}, {Nanoc::Extra::VCS}.
83
+ #
84
+ # @param [Class, String] class_or_name The class to register. This can be
85
+ # a string, in which case it will be automatically converted to a proper
86
+ # class at lookup. For example: `Nanoc::Filters::ERB`,
87
+ # `"Nanoc::Filters::Haml"`.
88
+ #
89
+ # @param [Symbol] identifiers One or more symbols identifying the class.
90
+ # For example: `:haml`, :`erb`.
91
+ #
92
+ # @return [void]
93
+ def register(superclass, class_or_name, *identifiers)
94
+ @map[superclass] ||= {}
95
+
96
+ identifiers.each do |identifier|
97
+ @map[superclass][identifier.to_sym] = class_or_name
98
+ end
99
+ end
100
+
101
+ # Finds the plugin that is a subclass of the given class and has the given
102
+ # name.
103
+ #
104
+ # @param [Class] klass The class of the plugin to return
105
+ #
106
+ # @param [Symbol] name The name of the plugin to return
107
+ #
108
+ # @return [Class, nil] The plugin with the given name
109
+ def find(klass, name)
110
+ @map[klass] ||= {}
111
+ resolve(@map[klass][name.to_sym], klass)
112
+ end
113
+
114
+ # Returns all plugins of the given class.
115
+ #
116
+ # @param [Class] klass The class of the plugin to return
117
+ #
118
+ # @return [Enumerable<Class>] A collection of class plugins
119
+ def find_all(klass)
120
+ @map[klass] ||= {}
121
+ res = {}
122
+ @map[klass].each_pair { |k,v| res[k] = resolve(v, k) }
123
+ res
124
+ end
125
+
126
+ # Returns a list of all plugins. The returned list of plugins is an array
127
+ # with array elements in the following format:
128
+ #
129
+ # { :class => ..., :superclass => ..., :identifiers => ... }
130
+ #
131
+ # @return [Array<Hash>] A list of all plugins in the format described
132
+ def all
133
+ plugins = []
134
+ @map.each_pair do |superclass, submap|
135
+ submap.each_pair do |identifier, klass|
136
+ # Find existing plugin
137
+ existing_plugin = plugins.find do |p|
138
+ p[:class] == klass && p[:superclass] == superclass
139
+ end
140
+
141
+ if existing_plugin
142
+ # Add identifier to existing plugin
143
+ existing_plugin[:identifiers] << identifier
144
+ existing_plugin[:identifiers] = existing_plugin[:identifiers].sort_by { |s| s.to_s }
145
+ else
146
+ # Create new plugin
147
+ plugins << {
148
+ :class => klass,
149
+ :superclass => superclass,
150
+ :identifiers => [ identifier ]
151
+ }
152
+ end
153
+ end
154
+ end
155
+
156
+ plugins
157
+ end
158
+
159
+ # @deprecated Use {Nanoc::PluginRegistry#find} instead
160
+ def named(name)
161
+ find(self, name)
162
+ end
163
+
164
+ protected
165
+
166
+ def resolve(class_or_name, klass)
167
+ if class_or_name.is_a?(String)
168
+ class_or_name.scan(/\w+/).inject(Kernel) do |memo, part|
169
+ memo.const_get(part)
170
+ end
171
+ else
172
+ class_or_name
173
+ end
174
+ end
175
+
176
+ end
177
+
178
+ # @deprecated Use {Nanoc::PluginRegistry.instance} instead
179
+ Plugin = PluginRegistry.instance
180
+
181
+ end
@@ -0,0 +1,492 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc
4
+
5
+ # A single representation (rep) of an item ({Nanoc::Item}). An item can
6
+ # have multiple representations. A representation has its own output file.
7
+ # A single item can therefore have multiple output files, each run through
8
+ # a different set of filters with a different layout.
9
+ class ItemRep
10
+
11
+ # Contains all deprecated methods. Mixed into {Nanoc::ItemRep}.
12
+ module Deprecated
13
+
14
+ # @deprecated Modify the {#raw_paths} attribute instead
15
+ def raw_path=(raw_path)
16
+ raw_paths[:last] = raw_path
17
+ end
18
+
19
+ # @deprecated Modify the {#paths} attribute instead
20
+ def path=(path)
21
+ paths[:last] = path
22
+ end
23
+
24
+ # @deprecated Use {Nanoc::ItemRep#compiled_content} instead.
25
+ def content_at_snapshot(snapshot=:pre)
26
+ compiled_content(:snapshot => snapshot)
27
+ end
28
+
29
+ # @deprecated
30
+ def created
31
+ raise NotImplementedError, "Nanoc::ItemRep#created is no longer implemented"
32
+ end
33
+
34
+ # @deprecated
35
+ def created?
36
+ raise NotImplementedError, "Nanoc::ItemRep#created? is no longer implemented"
37
+ end
38
+
39
+ # @deprecated
40
+ def modified
41
+ raise NotImplementedError, "Nanoc::ItemRep#modified is no longer implemented"
42
+ end
43
+
44
+ # @deprecated
45
+ def modified?
46
+ raise NotImplementedError, "Nanoc::ItemRep#modified? is no longer implemented"
47
+ end
48
+
49
+ # @deprecated
50
+ def written
51
+ raise NotImplementedError, "Nanoc::ItemRep#written is no longer implemented"
52
+ end
53
+
54
+ # @deprecated
55
+ def written?
56
+ raise NotImplementedError, "Nanoc::ItemRep#written? is no longer implemented"
57
+ end
58
+
59
+ end
60
+
61
+ # Contains all private methods. Mixed into {Nanoc::ItemRep}.
62
+ module Private
63
+
64
+ # @return [Hash] A hash containing the assigns that will be used in the
65
+ # next filter or layout operation. The keys (symbols) will be made
66
+ # available during the next operation.
67
+ attr_accessor :assigns
68
+
69
+ # @return [Boolean] true if this representation has already been
70
+ # compiled during the current or last compilation session; false
71
+ # otherwise
72
+ #
73
+ # @api private
74
+ attr_accessor :compiled
75
+ alias_method :compiled?, :compiled
76
+
77
+ # @return [Hash<Symbol,String>] A hash containing the raw paths (paths
78
+ # including the path to the output directory and the filename) for all
79
+ # snapshots. The keys correspond with the snapshot names, and the
80
+ # values with the path.
81
+ #
82
+ # @api private
83
+ attr_accessor :raw_paths
84
+
85
+ # @return [Hash<Symbol,String>] A hash containing the paths for all
86
+ # snapshots. The keys correspond with the snapshot names, and the
87
+ # values with the path.
88
+ #
89
+ # @api private
90
+ attr_accessor :paths
91
+
92
+ # @return [Hash<Symbol,String>] A hash containing the paths to the
93
+ # temporary _files_ that filters write binary content to. This is only
94
+ # used when the item representation is binary. The keys correspond
95
+ # with the snapshot names, and the values with the filename. When
96
+ # writing the item representation, the file corresponding with the
97
+ # requested snapshot (usually `:last`) will be copied from
98
+ # `filenames[snapshot]` to `raw_paths[snapshot]`.
99
+ #
100
+ # @api private
101
+ attr_reader :temporary_filenames
102
+
103
+ # @return [Hash<Symbol,String>] A hash containing the content at all
104
+ # snapshots. The keys correspond with the snapshot names, and the
105
+ # values with the content.
106
+ #
107
+ # @api private
108
+ attr_accessor :content
109
+
110
+ # Writes the item rep's compiled content to the rep's output file.
111
+ #
112
+ # This method will send two notifications: one before writing the item
113
+ # representation, and one after. These notifications can be used for
114
+ # generating diffs, for example.
115
+ #
116
+ # @api private
117
+ #
118
+ # @param [String, nil] raw_path The raw path to write the compiled rep
119
+ # to. If nil, the default raw path will be used.
120
+ #
121
+ # @return [void]
122
+ def write(snapshot=:last)
123
+ # Get raw path
124
+ raw_path = self.raw_path(:snapshot => snapshot)
125
+ return if raw_path.nil?
126
+
127
+ # Create parent directory
128
+ FileUtils.mkdir_p(File.dirname(raw_path))
129
+
130
+ # Check if file will be created
131
+ is_created = !File.file?(raw_path)
132
+
133
+ # Calculate characteristics of old content
134
+ if File.file?(raw_path)
135
+ hash_old = Pathname.new(raw_path).checksum
136
+ size_old = File.size(raw_path)
137
+ end
138
+
139
+ # Notify
140
+ Nanoc::NotificationCenter.post(:will_write_rep, self, snapshot)
141
+
142
+ if self.binary?
143
+ # Calculate characteristics of new content
144
+ size_new = File.size(temporary_filenames[:last])
145
+ hash_new = Pathname.new(temporary_filenames[:last]).checksum if size_old == size_new
146
+
147
+ # Check whether content was modified
148
+ is_modified = (size_old != size_new || hash_old != hash_new)
149
+
150
+ # Copy
151
+ if is_modified
152
+ FileUtils.cp(temporary_filenames[:last], raw_path)
153
+ end
154
+ else
155
+ # Check whether content was modified
156
+ is_modified = (!File.file?(raw_path) || File.read(raw_path) != @content[:last])
157
+
158
+ # Write
159
+ if is_modified
160
+ File.open(raw_path, 'w') { |io| io.write(@content[:last]) }
161
+ end
162
+ end
163
+
164
+ # Notify
165
+ Nanoc::NotificationCenter.post(:rep_written, self, raw_path, is_created, is_modified)
166
+ end
167
+
168
+ # Resets the compilation progress for this item representation. This is
169
+ # necessary when an unmet dependency is detected during compilation.
170
+ #
171
+ # @api private
172
+ #
173
+ # @return [void]
174
+ def forget_progress
175
+ initialize_content
176
+ end
177
+
178
+ # Returns the type of this object. Will always return `:item_rep`,
179
+ # because this is an item rep. For layouts, this method returns
180
+ # `:layout`.
181
+ #
182
+ # @api private
183
+ #
184
+ # @return [Symbol] :item_rep
185
+ def type
186
+ :item_rep
187
+ end
188
+
189
+ end
190
+
191
+ include Deprecated
192
+ include Private
193
+
194
+ # @return [Nanoc::Item] The item to which this rep belongs
195
+ attr_reader :item
196
+
197
+ # @return [Symbol] The representation's unique name
198
+ attr_reader :name
199
+
200
+ # @return [Boolean] true if this rep is currently binary; false otherwise
201
+ attr_reader :binary
202
+ alias_method :binary?, :binary
203
+
204
+ # @return [Array] A list of snapshots, represented as arrays where the
205
+ # first element is the snapshot name (a Symbol) and the last element is
206
+ # a Boolean indicating whether the snapshot is final or not
207
+ attr_accessor :snapshots
208
+
209
+ # Creates a new item representation for the given item.
210
+ #
211
+ # @param [Nanoc::Item] item The item to which the new representation will
212
+ # belong.
213
+ #
214
+ # @param [Symbol] name The unique name for the new item representation.
215
+ def initialize(item, name)
216
+ # Set primary attributes
217
+ @item = item
218
+ @name = name
219
+
220
+ # Set binary
221
+ @binary = @item.binary?
222
+
223
+ # Set default attributes
224
+ @raw_paths = {}
225
+ @paths = {}
226
+ @assigns = {}
227
+ @snapshots = []
228
+ initialize_content
229
+
230
+ # Reset flags
231
+ @compiled = false
232
+ end
233
+
234
+ # Returns the compiled content from a given snapshot.
235
+ #
236
+ # @option params [String] :snapshot The name of the snapshot from which to
237
+ # fetch the compiled content. By default, the returned compiled content
238
+ # will be the content compiled right before the first layout call (if
239
+ # any).
240
+ #
241
+ # @return [String] The compiled content at the given snapshot (or the
242
+ # default snapshot if no snapshot is specified)
243
+ def compiled_content(params={})
244
+ # Notify
245
+ Nanoc::NotificationCenter.post(:visit_started, self.item)
246
+ Nanoc::NotificationCenter.post(:visit_ended, self.item)
247
+
248
+ # Get name of last pre-layout snapshot
249
+ snapshot = params.fetch(:snapshot) { @content[:pre] ? :pre : :last }
250
+ is_moving = [ :pre, :post, :last ].include?(snapshot)
251
+
252
+ # Check existance of snapshot
253
+ if !is_moving && snapshots.find { |s| s.first == snapshot && s.last == true }.nil?
254
+ raise Nanoc::Errors::NoSuchSnapshot.new(self, snapshot)
255
+ end
256
+
257
+ # Require compilation
258
+ if @content[snapshot].nil? || (!self.compiled? && is_moving)
259
+ raise Nanoc::Errors::UnmetDependency.new(self)
260
+ else
261
+ @content[snapshot]
262
+ end
263
+ end
264
+
265
+ # Checks whether content exists at a given snapshot.
266
+ #
267
+ # @return [Boolean] True if content exists for the snapshot with the
268
+ # given name, false otherwise
269
+ #
270
+ # @since 3.2.0
271
+ def has_snapshot?(snapshot_name)
272
+ !@content[snapshot_name].nil?
273
+ end
274
+
275
+ # Returns the item rep’s raw path. It includes the path to the output
276
+ # directory and the full filename.
277
+ #
278
+ # @option params [Symbol] :snapshot (:last) The snapshot for which the
279
+ # path should be returned
280
+ #
281
+ # @return [String] The item rep’s path
282
+ def raw_path(params={})
283
+ Nanoc3::NotificationCenter.post(:visit_started, self.item)
284
+ Nanoc3::NotificationCenter.post(:visit_ended, self.item)
285
+
286
+ snapshot_name = params[:snapshot] || :last
287
+ @raw_paths[snapshot_name]
288
+ end
289
+
290
+ # Returns the item rep’s path, as used when being linked to. It starts
291
+ # with a slash and it is relative to the output directory. It does not
292
+ # include the path to the output directory. It will not include the
293
+ # filename if the filename is an index filename.
294
+ #
295
+ # @option params [Symbol] :snapshot (:last) The snapshot for which the
296
+ # path should be returned
297
+ #
298
+ # @return [String] The item rep’s path
299
+ def path(params={})
300
+ Nanoc3::NotificationCenter.post(:visit_started, self.item)
301
+ Nanoc3::NotificationCenter.post(:visit_ended, self.item)
302
+
303
+ snapshot_name = params[:snapshot] || :last
304
+ @paths[snapshot_name]
305
+ end
306
+
307
+ # Runs the item content through the given filter with the given arguments.
308
+ # This method will replace the content of the `:last` snapshot with the
309
+ # filtered content of the last snapshot.
310
+ #
311
+ # This method is supposed to be called only in a compilation rule block
312
+ # (see {Nanoc::CompilerDSL#compile}).
313
+ #
314
+ # @see Nanoc::ItemRepProxy#filter
315
+ #
316
+ # @param [Symbol] filter_name The name of the filter to run the item
317
+ # representations' content through
318
+ #
319
+ # @param [Hash] filter_args The filter arguments that should be passed to
320
+ # the filter's #run method
321
+ #
322
+ # @return [void]
323
+ def filter(filter_name, filter_args={})
324
+ # Get filter class
325
+ klass = filter_named(filter_name)
326
+ raise Nanoc::Errors::UnknownFilter.new(filter_name) if klass.nil?
327
+
328
+ # Check whether filter can be applied
329
+ if klass.from_binary? && !self.binary?
330
+ raise Nanoc::Errors::CannotUseBinaryFilter.new(self, klass)
331
+ elsif !klass.from_binary? && self.binary?
332
+ raise Nanoc::Errors::CannotUseTextualFilter.new(self, klass)
333
+ end
334
+
335
+ begin
336
+ # Notify start
337
+ Nanoc::NotificationCenter.post(:filtering_started, self, filter_name)
338
+
339
+ # Create filter
340
+ filter = klass.new(assigns)
341
+
342
+ # Run filter
343
+ source = self.binary? ? temporary_filenames[:last] : @content[:last]
344
+ result = filter.run(source, filter_args)
345
+ if klass.to_binary?
346
+ temporary_filenames[:last] = filter.output_filename
347
+ else
348
+ @content[:last] = result
349
+ @content[:last].freeze
350
+ end
351
+ @binary = klass.to_binary?
352
+
353
+ # Check whether file was written
354
+ if self.binary? && !File.file?(filter.output_filename)
355
+ raise RuntimeError,
356
+ "The #{filter_name.inspect} filter did not write anything to the required output file, #{filter.output_filename}."
357
+ end
358
+
359
+ # Create snapshot
360
+ snapshot(@content[:post] ? :post : :pre, :final => false) unless self.binary?
361
+ ensure
362
+ # Notify end
363
+ Nanoc::NotificationCenter.post(:filtering_ended, self, filter_name)
364
+ end
365
+ end
366
+
367
+ # Lays out the item using the given layout. This method will replace the
368
+ # content of the `:last` snapshot with the laid out content of the last
369
+ # snapshot.
370
+ #
371
+ # This method is supposed to be called only in a compilation rule block
372
+ # (see {Nanoc::CompilerDSL#compile}).
373
+ #
374
+ # @see Nanoc::ItemRepProxy#layout
375
+ #
376
+ # @param [Nanoc::Layout] layout The layout to use
377
+ #
378
+ # @param [Symbol] filter_name The name of the filter to layout the item
379
+ # representations' content with
380
+ #
381
+ # @param [Hash] filter_args The filter arguments that should be passed to
382
+ # the filter's #run method
383
+ #
384
+ # @return [void]
385
+ def layout(layout, filter_name, filter_args)
386
+ # Check whether item can be laid out
387
+ raise Nanoc::Errors::CannotLayoutBinaryItem.new(self) if self.binary?
388
+
389
+ # Create "pre" snapshot
390
+ if @content[:post].nil?
391
+ snapshot(:pre, :final => true)
392
+ end
393
+
394
+ # Create filter
395
+ klass = filter_named(filter_name)
396
+ raise Nanoc::Errors::UnknownFilter.new(filter_name) if klass.nil?
397
+ filter = klass.new(assigns.merge({ :layout => layout }))
398
+
399
+ # Visit
400
+ Nanoc::NotificationCenter.post(:visit_started, layout)
401
+ Nanoc::NotificationCenter.post(:visit_ended, layout)
402
+
403
+ begin
404
+ # Notify start
405
+ Nanoc::NotificationCenter.post(:processing_started, layout)
406
+ Nanoc::NotificationCenter.post(:filtering_started, self, filter_name)
407
+
408
+ # Layout
409
+ @content[:last] = filter.run(layout.raw_content, filter_args)
410
+
411
+ # Create "post" snapshot
412
+ snapshot(:post, :final => false)
413
+ ensure
414
+ # Notify end
415
+ Nanoc::NotificationCenter.post(:filtering_ended, self, filter_name)
416
+ Nanoc::NotificationCenter.post(:processing_ended, layout)
417
+ end
418
+ end
419
+
420
+ # Creates a snapshot of the current compiled item content.
421
+ #
422
+ # @param [Symbol] snapshot_name The name of the snapshot to create
423
+ #
424
+ # @option params [Boolean] :final (true) True if this is the final time
425
+ # the snapshot will be updated; false if it is a non-final moving
426
+ # snapshot (such as `:pre`, `:post` or `:last`)
427
+ #
428
+ # @return [void]
429
+ def snapshot(snapshot_name, params={})
430
+ is_final = params.fetch(:final) { true }
431
+ @content[snapshot_name] = @content[:last] unless self.binary?
432
+ self.write(snapshot_name) if is_final
433
+ end
434
+
435
+ # Returns a recording proxy that is used for determining whether the
436
+ # compilation has changed, and thus whether the item rep needs to be
437
+ # recompiled.
438
+ #
439
+ # @api private
440
+ #
441
+ # @return [Nanoc::ItemRepRecorderProxy] The recording proxy
442
+ def to_recording_proxy
443
+ Nanoc::ItemRepRecorderProxy.new(self)
444
+ end
445
+
446
+ # Returns false because this item is not yet a proxy, and therefore does
447
+ # need to be wrapped in a proxy during compilation.
448
+ #
449
+ # @api private
450
+ #
451
+ # @return [false]
452
+ #
453
+ # @see Nanoc::ItemRepRecorderProxy#is_proxy?
454
+ # @see Nanoc::ItemRepProxy#is_proxy?
455
+ def is_proxy?
456
+ false
457
+ end
458
+
459
+ # Returns an object that can be used for uniquely identifying objects.
460
+ #
461
+ # @api private
462
+ #
463
+ # @return [Object] An unique reference to this object
464
+ def reference
465
+ [ type, self.item.identifier, self.name ]
466
+ end
467
+
468
+ def inspect
469
+ "<#{self.class} name=#{self.name} binary=#{self.binary?} raw_path=#{self.raw_path} item.identifier=#{self.item.identifier}>"
470
+ end
471
+
472
+ private
473
+
474
+ def initialize_content
475
+ # Initialize content and filenames
476
+ if self.binary?
477
+ @temporary_filenames = { :last => @item.raw_filename }
478
+ @content = {}
479
+ else
480
+ @content = { :last => @item.raw_content }
481
+ @content[:last].freeze
482
+ @temporary_filenames = {}
483
+ end
484
+ end
485
+
486
+ def filter_named(name)
487
+ Nanoc::Filter.named(name)
488
+ end
489
+
490
+ end
491
+
492
+ end