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,8 @@
1
+ class String
2
+
3
+ # Transforms string into an actual path
4
+ def cleaned_path
5
+ "/#{self}/".gsub(/^\/+|\/+$/, '/')
6
+ end
7
+
8
+ end
@@ -0,0 +1,286 @@
1
+ module Nanoc2
2
+
3
+ # Nanoc2::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
16
+
17
+ # Creates a new data source for the given site.
18
+ def initialize(site)
19
+ @site = site
20
+ @references = 0
21
+ end
22
+
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.
29
+ def loading
30
+ # Load if necessary
31
+ up if @references == 0
32
+ @references += 1
33
+
34
+ yield
35
+ ensure
36
+ # Unload if necessary
37
+ @references -= 1
38
+ down if @references == 0
39
+ end
40
+
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 Nanoc2::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 Nanoc2::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
156
+
157
+ # Returns the page defaults (represented by Nanoc2::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
164
+
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
171
+
172
+ ########## Asset defaults
173
+
174
+ # Returns the asset defaults (represented by Nanoc2::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
181
+
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')
187
+ end
188
+
189
+ ########## Layouts
190
+
191
+ # Returns the list of layouts (represented by Nanoc2::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')
197
+ end
198
+
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 Nanoc2::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 Nanoc2::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
+ )
283
+ end
284
+
285
+ end
286
+ end
@@ -0,0 +1,30 @@
1
+ module Nanoc2
2
+
3
+ # Nanoc2::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
@@ -0,0 +1,93 @@
1
+ module Nanoc2
2
+
3
+ # Nanoc2::Filter is responsible for filtering pages and textual assets
4
+ # (binary assets are filtered using Nanoc2::BinaryFilter). It is the
5
+ # (abstract) superclass for all textual filters. Subclasses should override
6
+ # the +run+ method.
7
+ class Filter < Plugin
8
+
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 (Nanoc2::PageRep
18
+ # or Nanoc2::AssetRep) that should be compiled by this filter.
19
+ #
20
+ # +obj+:: A proxy for the page or asset's page (Nanoc2::Page or
21
+ # Nanoc2::Asset).
22
+ #
23
+ # +site+:: The site (Nanoc2::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?(Nanoc2::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
49
+ end
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.
56
+ def run(content)
57
+ raise NotImplementedError.new("Nanoc2::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
+ # Returns the filename associated with the item that is being filtered.
78
+ # The returned filename is in the format "page <path> (rep <name>)".
79
+ def filename
80
+ if assigns[:layout]
81
+ "layout #{assigns[:layout].path}"
82
+ elsif assigns[:page]
83
+ "page #{assigns[:_obj].path} (rep #{assigns[:_obj_rep].name})"
84
+ elsif assigns[:asset]
85
+ "asset #{assigns[:_obj].path} (rep #{assigns[:_obj_rep].name})"
86
+ else
87
+ '?'
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
@@ -0,0 +1,91 @@
1
+ module Nanoc2
2
+
3
+ # A Nanoc2::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 Nanoc2::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 (Nanoc2::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
+ Nanoc2::Filter.named(attribute_named(:filter))
59
+ end
60
+
61
+ # Saves the layout in the database, creating it if it doesn't exist yet or
62
+ # updating it if it already exists. Tells the site's data source to save
63
+ # the layout.
64
+ def save
65
+ @site.data_source.loading do
66
+ @site.data_source.save_layout(self)
67
+ end
68
+ end
69
+
70
+ # Moves the layout to a new path. Tells the site's data source to move the
71
+ # layout.
72
+ def move_to(new_path)
73
+ @site.data_source.loading do
74
+ @site.data_source.move_layout(self, new_path)
75
+ end
76
+ end
77
+
78
+ # Deletes the layout. Tells the site's data source to delete the layout.
79
+ def delete
80
+ @site.data_source.loading do
81
+ @site.data_source.delete_layout(self)
82
+ end
83
+ end
84
+
85
+ def inspect
86
+ "<#{self.class} path=#{self.path}>"
87
+ end
88
+
89
+ end
90
+
91
+ end