nanoc2 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/ChangeLog +3 -0
  2. data/LICENSE +19 -0
  3. data/README +75 -0
  4. data/Rakefile +76 -0
  5. data/bin/nanoc2 +26 -0
  6. data/lib/nanoc2.rb +73 -0
  7. data/lib/nanoc2/base.rb +26 -0
  8. data/lib/nanoc2/base/asset.rb +117 -0
  9. data/lib/nanoc2/base/asset_defaults.rb +21 -0
  10. data/lib/nanoc2/base/asset_rep.rb +282 -0
  11. data/lib/nanoc2/base/binary_filter.rb +44 -0
  12. data/lib/nanoc2/base/code.rb +41 -0
  13. data/lib/nanoc2/base/compiler.rb +67 -0
  14. data/lib/nanoc2/base/core_ext.rb +2 -0
  15. data/lib/nanoc2/base/core_ext/hash.rb +78 -0
  16. data/lib/nanoc2/base/core_ext/string.rb +8 -0
  17. data/lib/nanoc2/base/data_source.rb +286 -0
  18. data/lib/nanoc2/base/defaults.rb +30 -0
  19. data/lib/nanoc2/base/filter.rb +93 -0
  20. data/lib/nanoc2/base/layout.rb +91 -0
  21. data/lib/nanoc2/base/notification_center.rb +66 -0
  22. data/lib/nanoc2/base/page.rb +132 -0
  23. data/lib/nanoc2/base/page_defaults.rb +20 -0
  24. data/lib/nanoc2/base/page_rep.rb +324 -0
  25. data/lib/nanoc2/base/plugin.rb +71 -0
  26. data/lib/nanoc2/base/proxies.rb +5 -0
  27. data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
  28. data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
  29. data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
  30. data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
  31. data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
  32. data/lib/nanoc2/base/proxy.rb +37 -0
  33. data/lib/nanoc2/base/router.rb +72 -0
  34. data/lib/nanoc2/base/site.rb +274 -0
  35. data/lib/nanoc2/base/template.rb +64 -0
  36. data/lib/nanoc2/binary_filters.rb +1 -0
  37. data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
  38. data/lib/nanoc2/cli.rb +9 -0
  39. data/lib/nanoc2/cli/base.rb +132 -0
  40. data/lib/nanoc2/cli/commands.rb +10 -0
  41. data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
  42. data/lib/nanoc2/cli/commands/compile.rb +312 -0
  43. data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
  44. data/lib/nanoc2/cli/commands/create_page.rb +85 -0
  45. data/lib/nanoc2/cli/commands/create_site.rb +323 -0
  46. data/lib/nanoc2/cli/commands/create_template.rb +76 -0
  47. data/lib/nanoc2/cli/commands/help.rb +69 -0
  48. data/lib/nanoc2/cli/commands/info.rb +125 -0
  49. data/lib/nanoc2/cli/commands/switch.rb +141 -0
  50. data/lib/nanoc2/cli/commands/update.rb +91 -0
  51. data/lib/nanoc2/cli/logger.rb +72 -0
  52. data/lib/nanoc2/data_sources.rb +2 -0
  53. data/lib/nanoc2/data_sources/filesystem.rb +707 -0
  54. data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
  55. data/lib/nanoc2/extra.rb +6 -0
  56. data/lib/nanoc2/extra/auto_compiler.rb +285 -0
  57. data/lib/nanoc2/extra/context.rb +22 -0
  58. data/lib/nanoc2/extra/core_ext.rb +2 -0
  59. data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
  60. data/lib/nanoc2/extra/core_ext/time.rb +13 -0
  61. data/lib/nanoc2/extra/file_proxy.rb +29 -0
  62. data/lib/nanoc2/extra/vcs.rb +48 -0
  63. data/lib/nanoc2/extra/vcses.rb +5 -0
  64. data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
  65. data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
  66. data/lib/nanoc2/extra/vcses/git.rb +21 -0
  67. data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
  68. data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
  69. data/lib/nanoc2/filters.rb +16 -0
  70. data/lib/nanoc2/filters/bluecloth.rb +13 -0
  71. data/lib/nanoc2/filters/erb.rb +19 -0
  72. data/lib/nanoc2/filters/erubis.rb +14 -0
  73. data/lib/nanoc2/filters/haml.rb +21 -0
  74. data/lib/nanoc2/filters/markaby.rb +14 -0
  75. data/lib/nanoc2/filters/maruku.rb +14 -0
  76. data/lib/nanoc2/filters/old.rb +19 -0
  77. data/lib/nanoc2/filters/rainpress.rb +13 -0
  78. data/lib/nanoc2/filters/rdiscount.rb +13 -0
  79. data/lib/nanoc2/filters/rdoc.rb +23 -0
  80. data/lib/nanoc2/filters/redcloth.rb +14 -0
  81. data/lib/nanoc2/filters/relativize_paths.rb +16 -0
  82. data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
  83. data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
  84. data/lib/nanoc2/filters/rubypants.rb +14 -0
  85. data/lib/nanoc2/filters/sass.rb +18 -0
  86. data/lib/nanoc2/helpers.rb +9 -0
  87. data/lib/nanoc2/helpers/blogging.rb +217 -0
  88. data/lib/nanoc2/helpers/capturing.rb +63 -0
  89. data/lib/nanoc2/helpers/filtering.rb +54 -0
  90. data/lib/nanoc2/helpers/html_escape.rb +25 -0
  91. data/lib/nanoc2/helpers/link_to.rb +113 -0
  92. data/lib/nanoc2/helpers/render.rb +49 -0
  93. data/lib/nanoc2/helpers/tagging.rb +56 -0
  94. data/lib/nanoc2/helpers/text.rb +38 -0
  95. data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
  96. data/lib/nanoc2/routers.rb +3 -0
  97. data/lib/nanoc2/routers/default.rb +54 -0
  98. data/lib/nanoc2/routers/no_dirs.rb +66 -0
  99. data/lib/nanoc2/routers/versioned.rb +79 -0
  100. metadata +185 -0
@@ -0,0 +1,66 @@
1
+ module Nanoc2
2
+
3
+ # Nanoc2::NotificationCenter provides a way to send notifications between
4
+ # objects. It allows blocks associated with a certain notification name to
5
+ # be registered; these blocks will be called when the notification with the
6
+ # given name is posted.
7
+ #
8
+ # It is a slightly different implementation of the Observer pattern; the
9
+ # table of subscribers is not stored in the observable object itself, but in
10
+ # the notification center.
11
+ class NotificationCenter
12
+
13
+ class << self
14
+
15
+ # Adds the given block to the list of blocks that should be called when
16
+ # the notification with the given name is received.
17
+ #
18
+ # +name+:: The name of the notification that will be posted.
19
+ #
20
+ # +id+:: An identifier for the block. This is only used to be able to
21
+ # remove the block (using the remove method) later. Defaults to
22
+ # nil.
23
+ def on(name, id=nil, &block)
24
+ initialize_if_necessary(name)
25
+
26
+ # Add observer
27
+ @notifications[name] << { :id => id, :block => block }
28
+ end
29
+
30
+ # Posts a notification with the given name. All arguments wil be passed
31
+ # to the blocks handling the notification.
32
+ def post(name, *args)
33
+ initialize_if_necessary(name)
34
+
35
+ # Notify all observers
36
+ @notifications[name].each do |observer|
37
+ observer[:block].call(*args)
38
+ end
39
+ end
40
+
41
+ # Removes the block with the given identifier from the list of blocks
42
+ # that should be called when the notification with the given name is
43
+ # posted.
44
+ #
45
+ # +name+:: The name of the notification that will be posted.
46
+ #
47
+ # +id+:: The identifier of the block that should be removed.
48
+ def remove(name, id)
49
+ initialize_if_necessary(name)
50
+
51
+ # Remove relevant observers
52
+ @notifications[name].reject! { |i| i[:id] == id }
53
+ end
54
+
55
+ private
56
+
57
+ def initialize_if_necessary(name)
58
+ @notifications ||= {} # name => observers dictionary
59
+ @notifications[name] ||= [] # list of observers
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,132 @@
1
+ module Nanoc2
2
+
3
+ # A Nanoc2::Page represents a page in a nanoc site. It has content and
4
+ # attributes, as well as a path. It can also store the modification time to
5
+ # speed up compilation.
6
+ #
7
+ # Each page has a list of page representations or reps (Nanoc2::PageRep);
8
+ # compiling a page actually compiles all of its representations.
9
+ class Page
10
+
11
+ # Default values for pages.
12
+ DEFAULTS = {
13
+ :custom_path => nil,
14
+ :extension => 'html',
15
+ :filename => 'index',
16
+ :filters_pre => [],
17
+ :filters_post => [],
18
+ :layout => 'default',
19
+ :skip_output => false
20
+ }
21
+
22
+ # The Nanoc2::Site this page belongs to.
23
+ attr_accessor :site
24
+
25
+ # The parent page of this page. This can be nil even for non-root pages.
26
+ attr_accessor :parent
27
+
28
+ # The child pages of this page.
29
+ attr_accessor :children
30
+
31
+ # This page's raw, uncompiled content.
32
+ attr_reader :content
33
+
34
+ # A hash containing this page's attributes.
35
+ attr_accessor :attributes
36
+
37
+ # This page's path.
38
+ attr_reader :path
39
+
40
+ # The time when this page was last modified.
41
+ attr_reader :mtime
42
+
43
+ # This page's list of page representations.
44
+ attr_reader :reps
45
+
46
+ # Creates a new page.
47
+ #
48
+ # +content+:: This page's unprocessed content.
49
+ #
50
+ # +attributes+:: A hash containing this page's attributes.
51
+ #
52
+ # +path+:: This page's path.
53
+ #
54
+ # +mtime+:: The time when this page was last modified.
55
+ def initialize(content, attributes, path, mtime=nil)
56
+ # Set primary attributes
57
+ @attributes = attributes.clean
58
+ @content = content
59
+ @path = path.cleaned_path
60
+ @mtime = mtime
61
+
62
+ # Start disconnected
63
+ @parent = nil
64
+ @children = []
65
+ @reps = []
66
+ end
67
+
68
+ # Builds the individual page representations (Nanoc2::PageRep) for this
69
+ # page.
70
+ def build_reps
71
+ # Get list of rep names
72
+ rep_names_default = (@site.page_defaults.attributes[:reps] || {}).keys
73
+ rep_names_this = (@attributes[:reps] || {}).keys + [ :default ]
74
+ rep_names = rep_names_default | rep_names_this
75
+
76
+ # Get list of reps
77
+ reps = rep_names.inject({}) do |memo, rep_name|
78
+ rep = (@attributes[:reps] || {})[rep_name]
79
+ is_bad = (@attributes[:reps] || {}).has_key?(rep_name) && rep.nil?
80
+ is_bad ? memo : memo.merge(rep_name => rep || {})
81
+ end
82
+
83
+ # Build reps
84
+ @reps = []
85
+ reps.each_pair do |name, attrs|
86
+ @reps << PageRep.new(self, attrs, name)
87
+ end
88
+ end
89
+
90
+ # Returns a proxy (Nanoc2::PageProxy) for this page.
91
+ def to_proxy
92
+ @proxy ||= PageProxy.new(self)
93
+ end
94
+
95
+ # Returns the attribute with the given name.
96
+ def attribute_named(name)
97
+ return @attributes[name] if @attributes.has_key?(name)
98
+ return @site.page_defaults.attributes[name] if @site.page_defaults.attributes.has_key?(name)
99
+ return DEFAULTS[name]
100
+ end
101
+
102
+ # Saves the page in the database, creating it if it doesn't exist yet or
103
+ # updating it if it already exists. Tells the site's data source to save
104
+ # the page.
105
+ def save
106
+ @site.data_source.loading do
107
+ @site.data_source.save_page(self)
108
+ end
109
+ end
110
+
111
+ # Moves the page to a new path. Tells the site's data source to move the
112
+ # page.
113
+ def move_to(new_path)
114
+ @site.data_source.loading do
115
+ @site.data_source.move_page(self, new_path)
116
+ end
117
+ end
118
+
119
+ # Deletes the page. Tells the site's data source to delete the page.
120
+ def delete
121
+ @site.data_source.loading do
122
+ @site.data_source.delete_page(self)
123
+ end
124
+ end
125
+
126
+ def inspect
127
+ "<#{self.class} path=#{self.path}>"
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,20 @@
1
+ module Nanoc2
2
+
3
+ # Nanoc2::PageDefaults represent the default attributes for all pages in the
4
+ # site. If a specific page attribute is requested, but not found, then the
5
+ # page defaults will be queried for this attribute. (If the attribute
6
+ # doesn't even exist in the page defaults, hardcoded defaults will be used.)
7
+ class PageDefaults < Defaults
8
+
9
+ # Saves the page defaults in the database, creating it if it doesn't exist
10
+ # yet or updating it if it already exists. Tells the site's data source to
11
+ # save the page defaults.
12
+ def save
13
+ @site.data_source.loading do
14
+ @site.data_source.save_page_defaults(self)
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,324 @@
1
+ module Nanoc2
2
+
3
+ # A Nanoc2::PageRep is a single representation (rep) of a page (Nanoc2::Page).
4
+ # A page can have multiple representations. A representation has its own
5
+ # attributes and its own output file. A single page can therefore have
6
+ # multiple output files, each run through a different set of filters with a
7
+ # different layout.
8
+ #
9
+ # A page representation is observable. The following events will be
10
+ # notified:
11
+ #
12
+ # * :compilation_started
13
+ # * :compilation_ended
14
+ # * :filtering_started
15
+ # * :filtering_ended
16
+ #
17
+ # The compilation-related events have one parameters (the page
18
+ # representation); the filtering-related events have two (the page
19
+ # representation, and a symbol containing the filter class name).
20
+ class PageRep
21
+
22
+ # The page (Nanoc2::Page) to which this representation belongs.
23
+ attr_reader :page
24
+
25
+ # A hash containing this page representation's attributes.
26
+ attr_accessor :attributes
27
+
28
+ # This page representation's unique name.
29
+ attr_reader :name
30
+
31
+ # Creates a new page representation for the given page and with the given
32
+ # attributes.
33
+ #
34
+ # +page+:: The page (Nanoc2::Page) to which the new representation will
35
+ # belong.
36
+ #
37
+ # +attributes+:: A hash containing the new page representation's
38
+ # attributes. This hash must have been run through
39
+ # Hash#clean before using it here.
40
+ #
41
+ # +name+:: The unique name for the new page representation.
42
+ def initialize(page, attributes, name)
43
+ # Set primary attributes
44
+ @page = page
45
+ @attributes = attributes
46
+ @name = name
47
+
48
+ # Get page content from page
49
+ @content = { :pre => nil, :post => nil }
50
+
51
+ # Reset flags
52
+ @compiled = false
53
+ @modified = false
54
+ @created = false
55
+ end
56
+
57
+ # Returns a proxy (Nanoc2::PageRepProxy) for this page representation.
58
+ def to_proxy
59
+ @proxy ||= PageRepProxy.new(self)
60
+ end
61
+
62
+ # Returns true if this page rep's output file was created during the last
63
+ # compilation session, or false if the output file did already exist.
64
+ def created?
65
+ @created
66
+ end
67
+
68
+ # Returns true if this page rep's output file was modified during the last
69
+ # compilation session, or false if the output file wasn't changed.
70
+ def modified?
71
+ @modified
72
+ end
73
+
74
+ # Returns true if this page rep has been compiled, false otherwise.
75
+ def compiled?
76
+ @compiled
77
+ end
78
+
79
+ # Returns true if this page rep's output file is outdated and must be
80
+ # regenerated, false otherwise.
81
+ def outdated?
82
+ # Outdated if we don't know
83
+ return true if @page.mtime.nil?
84
+
85
+ # Outdated if compiled file doesn't exist
86
+ return true if !File.file?(disk_path)
87
+
88
+ # Get compiled mtime
89
+ compiled_mtime = File.stat(disk_path).mtime
90
+
91
+ # Outdated if file too old
92
+ return true if @page.mtime > compiled_mtime
93
+
94
+ # Outdated if layouts outdated
95
+ return true if @page.site.layouts.any? do |l|
96
+ l.mtime.nil? or l.mtime > compiled_mtime
97
+ end
98
+
99
+ # Outdated if page defaults outdated
100
+ return true if @page.site.page_defaults.mtime.nil?
101
+ return true if @page.site.page_defaults.mtime > compiled_mtime
102
+
103
+ # Outdated if code outdated
104
+ return true if @page.site.code.mtime.nil?
105
+ return true if @page.site.code.mtime > compiled_mtime
106
+
107
+ return false
108
+ end
109
+
110
+ # Returns the path to the output file, including the path to the output
111
+ # directory specified in the site configuration, and including the
112
+ # filename and extension.
113
+ def disk_path
114
+ @disk_path ||= @page.site.router.disk_path_for(self)
115
+ end
116
+
117
+ # Returns the path to the output file as it would be used in a web
118
+ # browser: starting with a slash (representing the web root), and only
119
+ # including the filename and extension if they cannot be ignored (i.e.
120
+ # they are not in the site configuration's list of index files).
121
+ def web_path
122
+ @web_path ||= @page.site.router.web_path_for(self)
123
+ end
124
+
125
+ # Returns the attribute with the given name. This method will look in
126
+ # several places for the requested attribute:
127
+ #
128
+ # 1. This page representation's attributes;
129
+ # 2. The attributes of this page representation's page;
130
+ # 3. The page defaults' representation corresponding to this page
131
+ # representation;
132
+ # 4. The page defaults in general;
133
+ # 5. The hardcoded page defaults, if everything else fails.
134
+ def attribute_named(name)
135
+ # Check in here
136
+ return @attributes[name] if @attributes.has_key?(name)
137
+
138
+ # Check in page
139
+ return @page.attributes[name] if @page.attributes.has_key?(name)
140
+
141
+ # Check in page defaults' page rep
142
+ page_default_reps = @page.site.page_defaults.attributes[:reps] || {}
143
+ page_default_rep = page_default_reps[@name] || {}
144
+ return page_default_rep[name] if page_default_rep.has_key?(name)
145
+
146
+ # Check in site defaults (global)
147
+ page_defaults_attrs = @page.site.page_defaults.attributes
148
+ return page_defaults_attrs[name] if page_defaults_attrs.has_key?(name)
149
+
150
+ # Check in hardcoded defaults
151
+ return Nanoc2::Page::DEFAULTS[name]
152
+ end
153
+
154
+ # Returns the page representation content at the given stage.
155
+ #
156
+ # +stage+:: The stage at which the content should be fetched. Can be
157
+ # either +:pre+ or +:post+. To get the raw, uncompiled content,
158
+ # use Nanoc2::Page#content.
159
+ def content(stage=:pre)
160
+ compile(stage == :post, true, false)
161
+
162
+ @content[stage]
163
+ end
164
+
165
+ # Returns the layout used for this page representation.
166
+ def layout
167
+ # Check whether layout is present
168
+ return nil if attribute_named(:layout).nil?
169
+
170
+ # Find layout
171
+ @layout ||= @page.site.layouts.find { |l| l.path == attribute_named(:layout).cleaned_path }
172
+ raise Nanoc2::Errors::UnknownLayoutError.new(attribute_named(:layout)) if @layout.nil?
173
+
174
+ @layout
175
+ end
176
+
177
+ # Compiles the page representation and writes the result to the disk. This
178
+ # method should not be called directly; please use Nanoc2::Compiler#run
179
+ # instead, and pass this page representation's page as its first argument.
180
+ #
181
+ # The page representation will only be compiled if it wasn't compiled
182
+ # before yet. To force recompilation of the page rep, forgetting any
183
+ # progress, set +from_scratch+ to true.
184
+ #
185
+ # +also_layout+:: true if the page rep should also be laid out and
186
+ # post-filtered, false if the page rep should only be
187
+ # pre-filtered.
188
+ #
189
+ # +even_when_not_outdated+:: true if the page rep should be compiled even
190
+ # if it is not outdated, false if not.
191
+ #
192
+ # +from_scratch+:: true if all compilation stages (pre-filter, layout,
193
+ # post-filter) should be performed again even if they
194
+ # have already been performed, false otherwise.
195
+ def compile(also_layout, even_when_not_outdated, from_scratch)
196
+ # Don't compile if already compiled
197
+ return if @content[also_layout ? :post : :pre] and !from_scratch
198
+
199
+ # Skip unless outdated
200
+ unless outdated? or even_when_not_outdated
201
+ if also_layout
202
+ Nanoc2::NotificationCenter.post(:compilation_started, self)
203
+ Nanoc2::NotificationCenter.post(:compilation_ended, self)
204
+ end
205
+ return
206
+ end
207
+
208
+ # Reset flags
209
+ @compiled = false
210
+ @modified = false
211
+ @created = false
212
+
213
+ # Forget progress if requested
214
+ @content = { :pre => nil, :post => nil } if from_scratch
215
+
216
+ # Check for recursive call
217
+ if @page.site.compiler.stack.include?(self)
218
+ @page.site.compiler.stack.push(self)
219
+ raise Nanoc2::Errors::RecursiveCompilationError.new
220
+ end
221
+
222
+ # Start
223
+ @page.site.compiler.stack.push(self)
224
+ Nanoc2::NotificationCenter.post(:compilation_started, self) if also_layout
225
+
226
+ # Pre-filter if necesary
227
+ if @content[:pre].nil?
228
+ do_filter(:pre)
229
+ end
230
+
231
+ # Post-filter if necessary
232
+ if @content[:post].nil? and also_layout
233
+ do_layout
234
+ do_filter(:post)
235
+
236
+ # Update status
237
+ @compiled = true
238
+ unless attribute_named(:skip_output)
239
+ @created = !File.file?(self.disk_path)
240
+ @modified = @created ? true : File.read(self.disk_path) != @content[:post]
241
+ end
242
+
243
+ # Write if necessary
244
+ write unless attribute_named(:skip_output)
245
+ end
246
+
247
+ # Stop
248
+ Nanoc2::NotificationCenter.post(:compilation_ended, self) if also_layout
249
+ @page.site.compiler.stack.pop
250
+ end
251
+
252
+ private
253
+
254
+ # Runs the content through the filters in the given stage.
255
+ def do_filter(stage)
256
+ # Get content if necessary
257
+ content = (stage == :pre ? @page.content : @content[:post])
258
+
259
+ # Get filters
260
+ check_for_outdated_filters
261
+ filters = attribute_named(stage == :pre ? :filters_pre : :filters_post)
262
+
263
+ # Run each filter
264
+ filters.each do |filter_name|
265
+ # Create filter
266
+ klass = Nanoc2::Filter.named(filter_name)
267
+ raise Nanoc2::Errors::UnknownFilterError.new(filter_name) if klass.nil?
268
+ filter = klass.new(self)
269
+
270
+ # Run filter
271
+ Nanoc2::NotificationCenter.post(:filtering_started, self, klass.identifier)
272
+ content = filter.run(content)
273
+ # FIXME hacky
274
+ content.force_encoding('utf-8') if content.respond_to?(:'force_encoding')
275
+ Nanoc2::NotificationCenter.post(:filtering_ended, self, klass.identifier)
276
+ end
277
+
278
+ # Set content
279
+ @content[stage] = content
280
+ end
281
+
282
+ # Runs the content through this rep's layout.
283
+ def do_layout
284
+ # Don't layout if not necessary
285
+ if attribute_named(:layout).nil?
286
+ @content[:post] = @content[:pre]
287
+ return
288
+ end
289
+
290
+ # Create filter
291
+ klass = layout.filter_class
292
+ raise Nanoc2::Errors::CannotDetermineFilterError.new(layout.path) if klass.nil?
293
+ filter = klass.new(self, :layout => layout.to_proxy)
294
+
295
+ # Layout
296
+ Nanoc2::NotificationCenter.post(:filtering_started, self, klass.identifier)
297
+ @content[:post] = filter.run(layout.content)
298
+ Nanoc2::NotificationCenter.post(:filtering_ended, self, klass.identifier)
299
+ end
300
+
301
+ # Writes the compiled content to the disk.
302
+ def write
303
+ # TODO add ruby 1.9 support
304
+ FileUtils.mkdir_p(File.dirname(self.disk_path))
305
+ File.open(self.disk_path, 'w') { |io| io.write(@content[:post]) }
306
+ end
307
+
308
+ # Raises an error when the outdated 'filters' attribute is used.
309
+ def check_for_outdated_filters
310
+ unless attribute_named(:filters).nil?
311
+ raise Nanoc2::Errors::NoLongerSupportedError.new(
312
+ 'The `filters` property is no longer supported; please use ' +
313
+ '`filters_pre` instead.'
314
+ )
315
+ end
316
+ end
317
+
318
+ def inspect
319
+ "<#{self.class} name=#{self.name} page.path=#{self.page.path}>"
320
+ end
321
+
322
+ end
323
+
324
+ end