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.
- data/ChangeLog +3 -0
- data/LICENSE +19 -0
- data/README +75 -0
- data/Rakefile +76 -0
- data/bin/nanoc2 +26 -0
- data/lib/nanoc2.rb +73 -0
- data/lib/nanoc2/base.rb +26 -0
- data/lib/nanoc2/base/asset.rb +117 -0
- data/lib/nanoc2/base/asset_defaults.rb +21 -0
- data/lib/nanoc2/base/asset_rep.rb +282 -0
- data/lib/nanoc2/base/binary_filter.rb +44 -0
- data/lib/nanoc2/base/code.rb +41 -0
- data/lib/nanoc2/base/compiler.rb +67 -0
- data/lib/nanoc2/base/core_ext.rb +2 -0
- data/lib/nanoc2/base/core_ext/hash.rb +78 -0
- data/lib/nanoc2/base/core_ext/string.rb +8 -0
- data/lib/nanoc2/base/data_source.rb +286 -0
- data/lib/nanoc2/base/defaults.rb +30 -0
- data/lib/nanoc2/base/filter.rb +93 -0
- data/lib/nanoc2/base/layout.rb +91 -0
- data/lib/nanoc2/base/notification_center.rb +66 -0
- data/lib/nanoc2/base/page.rb +132 -0
- data/lib/nanoc2/base/page_defaults.rb +20 -0
- data/lib/nanoc2/base/page_rep.rb +324 -0
- data/lib/nanoc2/base/plugin.rb +71 -0
- data/lib/nanoc2/base/proxies.rb +5 -0
- data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
- data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
- data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
- data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
- data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
- data/lib/nanoc2/base/proxy.rb +37 -0
- data/lib/nanoc2/base/router.rb +72 -0
- data/lib/nanoc2/base/site.rb +274 -0
- data/lib/nanoc2/base/template.rb +64 -0
- data/lib/nanoc2/binary_filters.rb +1 -0
- data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
- data/lib/nanoc2/cli.rb +9 -0
- data/lib/nanoc2/cli/base.rb +132 -0
- data/lib/nanoc2/cli/commands.rb +10 -0
- data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
- data/lib/nanoc2/cli/commands/compile.rb +312 -0
- data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
- data/lib/nanoc2/cli/commands/create_page.rb +85 -0
- data/lib/nanoc2/cli/commands/create_site.rb +323 -0
- data/lib/nanoc2/cli/commands/create_template.rb +76 -0
- data/lib/nanoc2/cli/commands/help.rb +69 -0
- data/lib/nanoc2/cli/commands/info.rb +125 -0
- data/lib/nanoc2/cli/commands/switch.rb +141 -0
- data/lib/nanoc2/cli/commands/update.rb +91 -0
- data/lib/nanoc2/cli/logger.rb +72 -0
- data/lib/nanoc2/data_sources.rb +2 -0
- data/lib/nanoc2/data_sources/filesystem.rb +707 -0
- data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
- data/lib/nanoc2/extra.rb +6 -0
- data/lib/nanoc2/extra/auto_compiler.rb +285 -0
- data/lib/nanoc2/extra/context.rb +22 -0
- data/lib/nanoc2/extra/core_ext.rb +2 -0
- data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
- data/lib/nanoc2/extra/core_ext/time.rb +13 -0
- data/lib/nanoc2/extra/file_proxy.rb +29 -0
- data/lib/nanoc2/extra/vcs.rb +48 -0
- data/lib/nanoc2/extra/vcses.rb +5 -0
- data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
- data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
- data/lib/nanoc2/extra/vcses/git.rb +21 -0
- data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
- data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
- data/lib/nanoc2/filters.rb +16 -0
- data/lib/nanoc2/filters/bluecloth.rb +13 -0
- data/lib/nanoc2/filters/erb.rb +19 -0
- data/lib/nanoc2/filters/erubis.rb +14 -0
- data/lib/nanoc2/filters/haml.rb +21 -0
- data/lib/nanoc2/filters/markaby.rb +14 -0
- data/lib/nanoc2/filters/maruku.rb +14 -0
- data/lib/nanoc2/filters/old.rb +19 -0
- data/lib/nanoc2/filters/rainpress.rb +13 -0
- data/lib/nanoc2/filters/rdiscount.rb +13 -0
- data/lib/nanoc2/filters/rdoc.rb +23 -0
- data/lib/nanoc2/filters/redcloth.rb +14 -0
- data/lib/nanoc2/filters/relativize_paths.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
- data/lib/nanoc2/filters/rubypants.rb +14 -0
- data/lib/nanoc2/filters/sass.rb +18 -0
- data/lib/nanoc2/helpers.rb +9 -0
- data/lib/nanoc2/helpers/blogging.rb +217 -0
- data/lib/nanoc2/helpers/capturing.rb +63 -0
- data/lib/nanoc2/helpers/filtering.rb +54 -0
- data/lib/nanoc2/helpers/html_escape.rb +25 -0
- data/lib/nanoc2/helpers/link_to.rb +113 -0
- data/lib/nanoc2/helpers/render.rb +49 -0
- data/lib/nanoc2/helpers/tagging.rb +56 -0
- data/lib/nanoc2/helpers/text.rb +38 -0
- data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
- data/lib/nanoc2/routers.rb +3 -0
- data/lib/nanoc2/routers/default.rb +54 -0
- data/lib/nanoc2/routers/no_dirs.rb +66 -0
- data/lib/nanoc2/routers/versioned.rb +79 -0
- 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
|