nanoc2 2.2.3

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 (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