nanoc 2.0 → 2.1

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 (99) hide show
  1. data/ChangeLog +54 -0
  2. data/LICENSE +1 -1
  3. data/README +63 -3
  4. data/Rakefile +61 -14
  5. data/bin/nanoc +7 -199
  6. data/lib/nanoc/base/asset.rb +113 -0
  7. data/lib/nanoc/base/asset_defaults.rb +21 -0
  8. data/lib/nanoc/base/asset_rep.rb +277 -0
  9. data/lib/nanoc/base/binary_filter.rb +44 -0
  10. data/lib/nanoc/base/code.rb +41 -0
  11. data/lib/nanoc/base/compiler.rb +46 -34
  12. data/lib/nanoc/base/core_ext/hash.rb +51 -7
  13. data/lib/nanoc/base/core_ext/string.rb +8 -0
  14. data/lib/nanoc/base/data_source.rb +253 -20
  15. data/lib/nanoc/base/defaults.rb +30 -0
  16. data/lib/nanoc/base/enhancements.rb +9 -84
  17. data/lib/nanoc/base/filter.rb +109 -6
  18. data/lib/nanoc/base/layout.rb +91 -0
  19. data/lib/nanoc/base/notification_center.rb +66 -0
  20. data/lib/nanoc/base/page.rb +94 -121
  21. data/lib/nanoc/base/page_defaults.rb +20 -0
  22. data/lib/nanoc/base/page_rep.rb +318 -0
  23. data/lib/nanoc/base/plugin.rb +57 -9
  24. data/lib/nanoc/base/proxies/asset_proxy.rb +29 -0
  25. data/lib/nanoc/base/proxies/asset_rep_proxy.rb +26 -0
  26. data/lib/nanoc/base/proxies/layout_proxy.rb +25 -0
  27. data/lib/nanoc/base/proxies/page_proxy.rb +35 -0
  28. data/lib/nanoc/base/proxies/page_rep_proxy.rb +28 -0
  29. data/lib/nanoc/base/proxy.rb +37 -0
  30. data/lib/nanoc/base/router.rb +72 -0
  31. data/lib/nanoc/base/site.rb +220 -91
  32. data/lib/nanoc/base/template.rb +64 -0
  33. data/lib/nanoc/binary_filters/image_science_thumbnail.rb +28 -0
  34. data/lib/nanoc/cli/base.rb +219 -0
  35. data/lib/nanoc/cli/cli.rb +16 -0
  36. data/lib/nanoc/cli/command.rb +105 -0
  37. data/lib/nanoc/cli/commands/autocompile.rb +80 -0
  38. data/lib/nanoc/cli/commands/compile.rb +273 -0
  39. data/lib/nanoc/cli/commands/create_layout.rb +85 -0
  40. data/lib/nanoc/cli/commands/create_page.rb +85 -0
  41. data/lib/nanoc/cli/commands/create_site.rb +327 -0
  42. data/lib/nanoc/cli/commands/create_template.rb +76 -0
  43. data/lib/nanoc/cli/commands/help.rb +69 -0
  44. data/lib/nanoc/cli/commands/info.rb +114 -0
  45. data/lib/nanoc/cli/commands/switch.rb +141 -0
  46. data/lib/nanoc/cli/commands/update.rb +91 -0
  47. data/lib/nanoc/cli/ext.rb +37 -0
  48. data/lib/nanoc/cli/logger.rb +66 -0
  49. data/lib/nanoc/cli/option_parser.rb +168 -0
  50. data/lib/nanoc/cli.rb +1 -0
  51. data/lib/nanoc/data_sources/filesystem.rb +650 -215
  52. data/lib/nanoc/data_sources/filesystem_combined.rb +495 -0
  53. data/lib/nanoc/extra/auto_compiler.rb +265 -0
  54. data/lib/nanoc/extra/context.rb +22 -0
  55. data/lib/nanoc/extra/core_ext/hash.rb +54 -0
  56. data/lib/nanoc/extra/core_ext/time.rb +13 -0
  57. data/lib/nanoc/extra/file_proxy.rb +29 -0
  58. data/lib/nanoc/extra/vcs.rb +48 -0
  59. data/lib/nanoc/extra/vcses/bazaar.rb +21 -0
  60. data/lib/nanoc/extra/vcses/dummy.rb +20 -0
  61. data/lib/nanoc/extra/vcses/git.rb +21 -0
  62. data/lib/nanoc/extra/vcses/mercurial.rb +21 -0
  63. data/lib/nanoc/extra/vcses/subversion.rb +21 -0
  64. data/lib/nanoc/filters/bluecloth.rb +13 -0
  65. data/lib/nanoc/filters/erb.rb +6 -22
  66. data/lib/nanoc/filters/erubis.rb +14 -0
  67. data/lib/nanoc/filters/haml.rb +11 -6
  68. data/lib/nanoc/filters/markaby.rb +5 -5
  69. data/lib/nanoc/filters/maruku.rb +14 -0
  70. data/lib/nanoc/filters/old.rb +19 -0
  71. data/lib/nanoc/filters/rdiscount.rb +13 -0
  72. data/lib/nanoc/filters/rdoc.rb +5 -4
  73. data/lib/nanoc/filters/redcloth.rb +14 -0
  74. data/lib/nanoc/filters/rubypants.rb +14 -0
  75. data/lib/nanoc/filters/sass.rb +13 -0
  76. data/lib/nanoc/helpers/blogging.rb +170 -0
  77. data/lib/nanoc/helpers/capturing.rb +59 -0
  78. data/lib/nanoc/helpers/html_escape.rb +23 -0
  79. data/lib/nanoc/helpers/link_to.rb +69 -0
  80. data/lib/nanoc/helpers/render.rb +47 -0
  81. data/lib/nanoc/helpers/tagging.rb +52 -0
  82. data/lib/nanoc/helpers/xml_sitemap.rb +58 -0
  83. data/lib/nanoc/routers/default.rb +54 -0
  84. data/lib/nanoc/routers/no_dirs.rb +66 -0
  85. data/lib/nanoc/routers/versioned.rb +79 -0
  86. data/lib/nanoc.rb +83 -12
  87. metadata +144 -47
  88. data/lib/nanoc/base/auto_compiler.rb +0 -124
  89. data/lib/nanoc/base/layout_processor.rb +0 -33
  90. data/lib/nanoc/base/page_proxy.rb +0 -31
  91. data/lib/nanoc/base/plugin_manager.rb +0 -33
  92. data/lib/nanoc/data_sources/database.rb +0 -259
  93. data/lib/nanoc/data_sources/trivial.rb +0 -145
  94. data/lib/nanoc/filters/markdown.rb +0 -13
  95. data/lib/nanoc/filters/smartypants.rb +0 -13
  96. data/lib/nanoc/filters/textile.rb +0 -13
  97. data/lib/nanoc/layout_processors/erb.rb +0 -35
  98. data/lib/nanoc/layout_processors/haml.rb +0 -18
  99. data/lib/nanoc/layout_processors/markaby.rb +0 -16
@@ -1,15 +1,31 @@
1
1
  module Nanoc
2
- class DataSource < Plugin
3
2
 
4
- attr_reader :config
3
+ # Nanoc::DataSource is responsible for loading data. It is the (abstract)
4
+ # superclass for all data sources. Subclasses must at least implement the
5
+ # data reading methods (+pages+, +page_defaults+, +layouts+, +templates+,
6
+ # and +code+); all other methods involving data manipulation are optional.
7
+ #
8
+ # Apart from the methods for loading and storing data, there are the +up+
9
+ # and +down+ methods for bringing up and tearing down the connection to the
10
+ # data source. These should be overridden in subclasses. The +loading+
11
+ # method wraps +up+ and +down+.
12
+ #
13
+ # The +setup+ method is used for setting up a site's data source for the
14
+ # first time. This method should be overridden in subclasses.
15
+ class DataSource < Plugin
5
16
 
17
+ # Creates a new data source for the given site.
6
18
  def initialize(site)
7
19
  @site = site
8
20
  @references = 0
9
21
  end
10
22
 
11
- # Preparation
12
-
23
+ # Loads the data source when necessary (calling +up+), yields, and unloads
24
+ # the data source when it is not being used elsewhere. All data source
25
+ # queries and data manipulations should be wrapped in a +loading+ block;
26
+ # it ensures that the data source is loaded when necessary and makes sure
27
+ # the data source does not get unloaded while it is still being used
28
+ # elsewhere.
13
29
  def loading
14
30
  # Load if necessary
15
31
  up if @references == 0
@@ -22,31 +38,248 @@ module Nanoc
22
38
  down if @references == 0
23
39
  end
24
40
 
25
- def up ; end
26
- def down ; end
41
+ ########## Loading and unloading
42
+
43
+ # Brings up the connection to the data. This is an abstract method
44
+ # implemented by the subclass. Depending on the way data is stored, this
45
+ # may not be necessary. This is the ideal place to connect to the
46
+ # database, for example.
47
+ #
48
+ # Subclasses may implement this method.
49
+ def up
50
+ end
51
+
52
+ # Brings down the connection to the data. This is an abstract method
53
+ # implemented by the subclass. This method should undo the effects of
54
+ # +up+.
55
+ #
56
+ # Subclasses may implement this method.
57
+ def down
58
+ end
59
+
60
+ ########## Creating/updating
61
+
62
+ # Creates the bare minimum essentials for this data source to work. This
63
+ # action will likely be destructive. This method should not create sample
64
+ # data such as a default home page, a default layout, etc. For example, if
65
+ # you're using a database, this is where you should create the necessary
66
+ # tables for the data source to function properly.
67
+ #
68
+ # Subclasses must implement this method.
69
+ def setup
70
+ not_implemented('setup')
71
+ end
72
+
73
+ # Removes all data stored by this data source. This method undoes the
74
+ # effects of the +setup+ method.
75
+ #
76
+ # Subclasses must implement this method.
77
+ def destroy
78
+ not_implemented('destroy')
79
+ end
80
+
81
+ # Updated the content stored in this site to a newer version. A newer
82
+ # version of a data source may store content in a different format, and
83
+ # this method will update the stored content to this newer format.
84
+ #
85
+ # Subclasses may implement this method.
86
+ def update
87
+ end
88
+
89
+ ########## Pages
90
+
91
+ # Returns the list of pages (represented by Nanoc::Page) in this site.
92
+ # This is an abstract method implemented by the subclass.
93
+ #
94
+ # Subclasses must implement this method.
95
+ def pages
96
+ not_implemented('pages')
97
+ end
98
+
99
+ # Saves the given page in the data source, creating it if it doesn't exist
100
+ # yet and updating the existing copy otherwise.
101
+ #
102
+ # Subclasses must implement this method.
103
+ def save_page(page)
104
+ not_implemented('save_page')
105
+ end
106
+
107
+ # Changes the path of the given page to the given new path. When changing
108
+ # a page's path, this method must be used (save_page will not work).
109
+ #
110
+ # Subclasses must implement this method.
111
+ def move_page(page, new_path)
112
+ not_implemented('move_page')
113
+ end
114
+
115
+ # Removes the given page from the data source.
116
+ #
117
+ # Subclasses must implement this method.
118
+ def delete_page(page)
119
+ not_implemented('delete_page')
120
+ end
121
+
122
+ ########## Assets
123
+
124
+ # Returns the list of assets (represented by Nanoc::Asset) in this site.
125
+ # This is an abstract method implemented by the subclass.
126
+ #
127
+ # Subclasses must implement this method.
128
+ def assets
129
+ not_implemented('assets')
130
+ end
131
+
132
+ # Saves the given asset in the data source, creating it if it doesn't
133
+ # exist yet and updating the existing copy otherwise.
134
+ #
135
+ # Subclasses must implement this method.
136
+ def save_asset(asset)
137
+ not_implemented('save_asset')
138
+ end
139
+
140
+ # Changes the path of the given asset to the given new path. When changing
141
+ # a asset's path, this method must be used (save_asset will not work).
142
+ #
143
+ # Subclasses must implement this method.
144
+ def move_asset(asset, new_path)
145
+ not_implemented('move_asset')
146
+ end
147
+
148
+ # Removes the given asset from the data source.
149
+ #
150
+ # Subclasses must implement this method.
151
+ def delete_asset(asset)
152
+ not_implemented('delete_asset')
153
+ end
154
+
155
+ ########## Page defaults
27
156
 
28
- def setup ; end
157
+ # Returns the page defaults (represented by Nanoc::PageDefaults) of this
158
+ # site. This is an abstract method implemented by the subclass.
159
+ #
160
+ # Subclasses must implement this method.
161
+ def page_defaults
162
+ not_implemented('page_defaults')
163
+ end
29
164
 
30
- # Loading data
165
+ # Saves the given page defaults in the data source.
166
+ #
167
+ # Subclasses must implement this method.
168
+ def save_page_defaults(page_defaults)
169
+ not_implemented('save_page_defaults')
170
+ end
31
171
 
32
- def pages ; error 'DataSource#pages must be overridden' ; end
33
- def page_defaults ; error 'DataSource#page_defaults must be overridden' ; end
34
- def layouts ; error 'DataSource#layouts must be overridden' ; end
35
- def templates ; error 'DataSource#templates must be overridden' ; end
36
- def code ; error 'DataSource#code must be overridden' ; end
172
+ ########## Asset defaults
37
173
 
38
- # Creating data
174
+ # Returns the asset defaults (represented by Nanoc::AssetDefaults) of this
175
+ # site. This is an abstract method implemented by the subclass.
176
+ #
177
+ # Subclasses must implement this method.
178
+ def asset_defaults
179
+ not_implemented('asset_defaults')
180
+ end
39
181
 
40
- def create_page(name, template)
41
- error 'DataSource#create_page must be overridden'
182
+ # Saves the given asset defaults in the data source.
183
+ #
184
+ # Subclasses must implement this method.
185
+ def save_asset_defaults(asset_defaults)
186
+ not_implemented('save_asset_defaults')
42
187
  end
43
188
 
44
- def create_layout(name)
45
- error 'DataSource#create_layout must be overridden'
189
+ ########## Layouts
190
+
191
+ # Returns the list of layouts (represented by Nanoc::Layout) in this site.
192
+ # This is an abstract method implemented by the subclass.
193
+ #
194
+ # Subclasses must implement this method.
195
+ def layouts
196
+ not_implemented('layouts')
46
197
  end
47
198
 
48
- def create_template(name)
49
- error 'DataSource#create_template must be overridden'
199
+ # Saves the given layout in the data source, creating it if it doesn't
200
+ # exist yet and updating the existing copy otherwise.
201
+ #
202
+ # Subclasses must implement this method.
203
+ def save_layout(layout)
204
+ not_implemented('save_layout')
205
+ end
206
+
207
+ # Changes the path of the given layout to the given new path. When
208
+ # changing a layout's path, this method must be used (save_layout will not
209
+ # work).
210
+ #
211
+ # Subclasses must implement this method.
212
+ def move_layout(layout, new_path)
213
+ not_implemented('move_layout')
214
+ end
215
+
216
+ # Removes the given layout from the data source.
217
+ #
218
+ # Subclasses must implement this method.
219
+ def delete_layout(layout)
220
+ not_implemented('delete_layout')
221
+ end
222
+
223
+ ########## Templates
224
+
225
+ # Returns the list of templates (represented by Nanoc::Template) in this
226
+ # site. This is an abstract method implemented by the subclass.
227
+ #
228
+ # Subclasses must implement this method.
229
+ def templates
230
+ not_implemented('templates')
231
+ end
232
+
233
+ # Saves the given template in the data source, creating it if it doesn't
234
+ # exist yet and updating the existing copy otherwise.
235
+ #
236
+ # Subclasses must implement this method.
237
+ def save_template(template)
238
+ not_implemented('save_template')
239
+ end
240
+
241
+ # Changes the name of the given template to the given new name. When
242
+ # changing a template's name, this method must be used (save_template will
243
+ # not work).
244
+ #
245
+ # Subclasses must implement this method.
246
+ def move_template(template, new_name)
247
+ not_implemented('move_template')
248
+ end
249
+
250
+ # Removes the given template from the data source.
251
+ #
252
+ # Subclasses must implement this method.
253
+ def delete_template(template)
254
+ not_implemented('delete_template')
255
+ end
256
+
257
+ ########## Code
258
+
259
+ # Returns the custom code (represented by Nanoc::Code) for this site.
260
+ # This is an abstract method implemented by the subclass. This can be code
261
+ # for custom filters, routers, and more, but pretty much any code can
262
+ # be put in there (global helper functions are very useful).
263
+ #
264
+ # Subclasses must implement this method.
265
+ def code
266
+ not_implemented('code')
267
+ end
268
+
269
+ # Saves the given code in the data source.
270
+ #
271
+ # Subclasses must implement this method.
272
+ def save_code(code)
273
+ not_implemented('save_code')
274
+ end
275
+
276
+ private
277
+
278
+ def not_implemented(name)
279
+ raise NotImplementedError.new(
280
+ "#{self.class} does not override ##{name}, which is required for " +
281
+ "this data source to be used."
282
+ )
50
283
  end
51
284
 
52
285
  end
@@ -0,0 +1,30 @@
1
+ module Nanoc
2
+
3
+ # Nanoc::Defaults represent the default attributes for a given set of
4
+ # objects in the site. It is basically a hash with an optional modification
5
+ # time.
6
+ class Defaults
7
+
8
+ # Th site where this set of defaults belongs to.
9
+ attr_accessor :site
10
+
11
+ # A hash containing the default attributes.
12
+ attr_reader :attributes
13
+
14
+ # The time when this set of defaults was last modified.
15
+ attr_reader :mtime
16
+
17
+ # Creates a new set of defaults.
18
+ #
19
+ # +attributes+:: The hash containing the metadata that individual objects
20
+ # will override.
21
+ #
22
+ # +mtime+:: The time when the defaults were last modified (optional).
23
+ def initialize(attributes, mtime=nil)
24
+ @attributes = attributes.clean
25
+ @mtime = mtime
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -1,89 +1,14 @@
1
- # Get requirements
2
- begin ; require 'rubygems' ; rescue LoadError ; end
3
- require 'yaml'
4
- require 'fileutils'
5
-
6
- # Logging (level can be :off, :high, :low)
7
- $log_level = :high
8
- def log(log_level, s, io=$stdout)
9
- io.puts s if ($log_level == :low or $log_level == log_level) and $log_level != :off
1
+ # Convenience function for printing warnings
2
+ def warn(s, pre='WARNING')
3
+ $stderr.puts "#{pre}: #{s}"
10
4
  end
11
5
 
12
- # Convenience function for printing errors
13
- def error(s, pre='ERROR')
14
- log(:high, pre + ': ' + s, $stderr)
15
- exit(1)
16
- end
6
+ ############################# OLD AND DEPRECATED #############################
17
7
 
18
- # Convenience function for requiring libraries
19
- def nanoc_require(x, message="'#{x}' is required to compile this site.")
8
+ def nanoc_require(x)
9
+ warn(
10
+ "'nanoc_require' is deprecated and will be removed in a future version. Please use 'require' instead.",
11
+ 'DEPRECATION WARNING'
12
+ )
20
13
  require x
21
- rescue LoadError
22
- error(message)
23
- end
24
-
25
- # Rendering sub-layouts
26
- def render(name, other_assigns={})
27
- layout = @site.layouts.find { |l| l[:name] == name }
28
- layout_processor_class = Nanoc::PluginManager.layout_processor_for_extension(layout[:extension])
29
- layout_processor = layout_processor_class.new(@page, @pages, @site.config, @site, other_assigns)
30
- layout_processor.run(layout[:content])
31
- end
32
-
33
- # Convenience function for cd'ing in and out of a directory
34
- def in_dir(path)
35
- FileUtils.cd(File.join(path))
36
- yield
37
- ensure
38
- FileUtils.cd(File.join(path.map { |n| '..' }))
39
- end
40
-
41
- class FileManager
42
-
43
- ACTION_COLORS = {
44
- :create => "\e[1m" + "\e[32m", # bold + green
45
- :update => "\e[1m" + "\e[33m", # bold + yellow
46
- :identical => "\e[1m" # bold
47
- }
48
-
49
- def self.file_log(log_level, action, path)
50
- log(log_level, '%s%12s%s %s' % [ACTION_COLORS[action.to_sym], action, "\e[0m", path])
51
- end
52
-
53
- def self.create_dir(name)
54
- # Check whether directory exists
55
- return if File.exist?(name)
56
-
57
- # Create dir
58
- FileUtils.mkdir_p(name)
59
- log(:create, name)
60
- end
61
-
62
- def self.create_file(path)
63
- # Create parent directory if necessary
64
- if path =~ /\//
65
- parent_path = path.sub(/\/[^\/]+$/, '')
66
- FileManager.create_dir(parent_path)
67
- end
68
-
69
- # Get contents
70
- content_old = File.exist?(path) ? File.read(path) : nil
71
- content_new = yield
72
- content_new = content_new.force_encoding(content_old.encoding) if content_old and String.method_defined?(:force_encoding)
73
- modified = (content_old != content_new)
74
-
75
- # Log
76
- if File.exist?(path)
77
- file_log(*(modified ? [ :high, :update, path ] : [ :low, :identical, path ]))
78
- else
79
- file_log(:high, :create, path)
80
- end
81
-
82
- # Write
83
- open(path, 'w') { |io| io.write(content_new) }
84
-
85
- # Report back
86
- modified
87
- end
88
-
89
14
  end
@@ -1,16 +1,119 @@
1
1
  module Nanoc
2
+
3
+ # Nanoc::Filter is responsible for filtering pages and textual assets
4
+ # (binary assets are filtered using Nanoc::BinaryFilter). It is the
5
+ # (abstract) superclass for all textual filters. Subclasses should override
6
+ # the +run+ method.
2
7
  class Filter < Plugin
3
8
 
4
- def initialize(page, pages, config, site)
5
- @page = page
6
- @pages = pages
7
- @config = config
8
- @site = site
9
+ # Deprecated
10
+ EXTENSIONS_MAP = {}
11
+
12
+ # Creates a new filter for the given object (page or asset) and site.
13
+ #
14
+ # +kind+:: The kind of object that is passed. Can be either +:page+ or
15
+ # +:asset+.
16
+ #
17
+ # +obj_rep+:: A proxy for the page or asset representation (Nanoc::PageRep
18
+ # or Nanoc::AssetRep) that should be compiled by this filter.
19
+ #
20
+ # +obj+:: A proxy for the page or asset's page (Nanoc::Page or
21
+ # Nanoc::Asset).
22
+ #
23
+ # +site+:: The site (Nanoc::Site) this filter belongs to.
24
+ #
25
+ # +other_assigns+:: A hash containing other variables that should be made
26
+ # available during filtering.
27
+ def initialize(obj_rep, other_assigns={})
28
+ # Determine kind
29
+ @kind = obj_rep.is_a?(Nanoc::PageRep) ? :page : :asset
30
+
31
+ # Set object
32
+ @obj_rep = obj_rep
33
+ @obj = (@kind == :page ? @obj_rep.page : @obj_rep.asset)
34
+
35
+ # Set page/asset and page/asset reps
36
+ if @kind == :page
37
+ @page = @obj
38
+ @page_rep = @obj_rep
39
+ else
40
+ @asset = @obj
41
+ @asset_rep = @obj_rep
42
+ end
43
+
44
+ # Set site
45
+ @site = @obj.site
46
+
47
+ # Set other assigns
48
+ @other_assigns = other_assigns
9
49
  end
10
50
 
51
+ # Runs the filter. This method returns the filtered content.
52
+ #
53
+ # +content+:: The unprocessed content that should be filtered.
54
+ #
55
+ # Subclasses must implement this method.
11
56
  def run(content)
12
- error 'Filter#run must be overridden'
57
+ raise NotImplementedError.new("Nanoc::Filter subclasses must implement #run")
58
+ end
59
+
60
+ # Returns a hash with data that should be available.
61
+ def assigns
62
+ @assigns ||= @other_assigns.merge({
63
+ :_obj_rep => @obj_rep,
64
+ :_obj => @obj,
65
+ :page_rep => @kind == :page ? @page_rep.to_proxy : nil,
66
+ :page => @kind == :page ? @page.to_proxy : nil,
67
+ :asset_rep => @kind == :asset ? @asset_rep.to_proxy : nil,
68
+ :asset => @kind == :asset ? @asset.to_proxy : nil,
69
+ :pages => @site.pages.map { |obj| obj.to_proxy },
70
+ :assets => @site.assets.map { |obj| obj.to_proxy },
71
+ :layouts => @site.layouts.map { |obj| obj.to_proxy },
72
+ :config => @site.config,
73
+ :site => @site
74
+ })
75
+ end
76
+
77
+ class << self
78
+
79
+ # Deprecated
80
+ def extensions(*extensions) # :nodoc:
81
+ # Initialize
82
+ @extensions = [] unless instance_variables.include?('@extensions')
83
+
84
+ if extensions.empty?
85
+ @extensions
86
+ else
87
+ @extensions = extensions
88
+ @extensions.each { |e| register_extension(e, self) }
89
+ end
90
+ end
91
+
92
+ # Deprecated
93
+ def extension(extension=nil) # :nodoc:
94
+ # Initialize
95
+ @extensions = [] unless instance_variables.include?('@extensions')
96
+
97
+ if extension.nil?
98
+ @extensions.first
99
+ else
100
+ @extensions = [ extension ]
101
+ register_extension(extension, self)
102
+ end
103
+ end
104
+
105
+ # Deprecated
106
+ def register_extension(extension, klass)
107
+ EXTENSIONS_MAP[extension] = klass
108
+ end
109
+
110
+ # Deprecated
111
+ def with_extension(extension)
112
+ EXTENSIONS_MAP[extension]
113
+ end
114
+
13
115
  end
14
116
 
15
117
  end
118
+
16
119
  end
@@ -0,0 +1,91 @@
1
+ module Nanoc
2
+
3
+ # A Nanoc::Layout represents a layout in a nanoc site. It has content,
4
+ # attributes (for determining which filter to use for laying out a page), a
5
+ # path (because layouts are organised hierarchically), and a modification
6
+ # time (to speed up compilation).
7
+ class Layout
8
+
9
+ # Default values for layouts.
10
+ DEFAULTS = {
11
+ :filter => 'erb'
12
+ }
13
+
14
+ # The Nanoc::Site this layout belongs to.
15
+ attr_accessor :site
16
+
17
+ # The raw content of this layout.
18
+ attr_reader :content
19
+
20
+ # A hash containing this layout's attributes.
21
+ attr_reader :attributes
22
+
23
+ # This layout's path, starting and ending with a slash.
24
+ attr_reader :path
25
+
26
+ # The time when this layout was last modified.
27
+ attr_reader :mtime
28
+
29
+ # Creates a new layout.
30
+ #
31
+ # +content+:: The raw content of this layout.
32
+ #
33
+ # +attributes+:: A hash containing this layout's attributes.
34
+ #
35
+ # +path+:: This layout's path, starting and ending with a slash.
36
+ #
37
+ # +mtime+:: The time when this layout was last modified.
38
+ def initialize(content, attributes, path, mtime=nil)
39
+ @content = content
40
+ @attributes = attributes.clean
41
+ @path = path.cleaned_path
42
+ @mtime = mtime
43
+ end
44
+
45
+ # Returns a proxy (Nanoc::LayoutProxy) for this layout.
46
+ def to_proxy
47
+ @proxy ||= LayoutProxy.new(self)
48
+ end
49
+
50
+ # Returns the attribute with the given name.
51
+ def attribute_named(name)
52
+ return @attributes[name] if @attributes.has_key?(name)
53
+ return DEFAULTS[name]
54
+ end
55
+
56
+ # Returns the filter class needed for this layout.
57
+ def filter_class
58
+ if attribute_named(:extension).nil?
59
+ Nanoc::Filter.named(attribute_named(:filter))
60
+ else
61
+ Nanoc::Filter.with_extension(attribute_named(:extension))
62
+ end
63
+ end
64
+
65
+ # Saves the layout in the database, creating it if it doesn't exist yet or
66
+ # updating it if it already exists. Tells the site's data source to save
67
+ # the layout.
68
+ def save
69
+ @site.data_source.loading do
70
+ @site.data_source.save_layout(self)
71
+ end
72
+ end
73
+
74
+ # Moves the layout to a new path. Tells the site's data source to move the
75
+ # layout.
76
+ def move_to(new_path)
77
+ @site.data_source.loading do
78
+ @site.data_source.move_layout(self, new_path)
79
+ end
80
+ end
81
+
82
+ # Deletes the layout. Tells the site's data source to delete the layout.
83
+ def delete
84
+ @site.data_source.loading do
85
+ @site.data_source.delete_layout(self)
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end