nanoc3 3.0.0

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 (116) hide show
  1. data/ChangeLog +3 -0
  2. data/LICENSE +19 -0
  3. data/NEWS.rdoc +262 -0
  4. data/README.rdoc +80 -0
  5. data/Rakefile +11 -0
  6. data/bin/nanoc3 +16 -0
  7. data/lib/nanoc3/base/code_snippet.rb +42 -0
  8. data/lib/nanoc3/base/compiler.rb +225 -0
  9. data/lib/nanoc3/base/compiler_dsl.rb +110 -0
  10. data/lib/nanoc3/base/core_ext/array.rb +21 -0
  11. data/lib/nanoc3/base/core_ext/hash.rb +23 -0
  12. data/lib/nanoc3/base/core_ext/string.rb +14 -0
  13. data/lib/nanoc3/base/core_ext.rb +5 -0
  14. data/lib/nanoc3/base/data_source.rb +197 -0
  15. data/lib/nanoc3/base/dependency_tracker.rb +291 -0
  16. data/lib/nanoc3/base/errors.rb +95 -0
  17. data/lib/nanoc3/base/filter.rb +60 -0
  18. data/lib/nanoc3/base/item.rb +87 -0
  19. data/lib/nanoc3/base/item_rep.rb +236 -0
  20. data/lib/nanoc3/base/layout.rb +53 -0
  21. data/lib/nanoc3/base/notification_center.rb +68 -0
  22. data/lib/nanoc3/base/plugin.rb +88 -0
  23. data/lib/nanoc3/base/preprocessor_context.rb +37 -0
  24. data/lib/nanoc3/base/rule.rb +37 -0
  25. data/lib/nanoc3/base/rule_context.rb +68 -0
  26. data/lib/nanoc3/base/site.rb +334 -0
  27. data/lib/nanoc3/base.rb +25 -0
  28. data/lib/nanoc3/cli/base.rb +151 -0
  29. data/lib/nanoc3/cli/commands/autocompile.rb +89 -0
  30. data/lib/nanoc3/cli/commands/compile.rb +279 -0
  31. data/lib/nanoc3/cli/commands/create_item.rb +79 -0
  32. data/lib/nanoc3/cli/commands/create_layout.rb +94 -0
  33. data/lib/nanoc3/cli/commands/create_site.rb +320 -0
  34. data/lib/nanoc3/cli/commands/help.rb +71 -0
  35. data/lib/nanoc3/cli/commands/info.rb +114 -0
  36. data/lib/nanoc3/cli/commands/update.rb +96 -0
  37. data/lib/nanoc3/cli/commands.rb +13 -0
  38. data/lib/nanoc3/cli/logger.rb +73 -0
  39. data/lib/nanoc3/cli.rb +16 -0
  40. data/lib/nanoc3/data_sources/delicious.rb +66 -0
  41. data/lib/nanoc3/data_sources/filesystem.rb +231 -0
  42. data/lib/nanoc3/data_sources/filesystem_combined.rb +202 -0
  43. data/lib/nanoc3/data_sources/filesystem_common.rb +22 -0
  44. data/lib/nanoc3/data_sources/filesystem_compact.rb +232 -0
  45. data/lib/nanoc3/data_sources/last_fm.rb +103 -0
  46. data/lib/nanoc3/data_sources/twitter.rb +53 -0
  47. data/lib/nanoc3/data_sources.rb +20 -0
  48. data/lib/nanoc3/extra/auto_compiler.rb +97 -0
  49. data/lib/nanoc3/extra/chick.rb +119 -0
  50. data/lib/nanoc3/extra/context.rb +24 -0
  51. data/lib/nanoc3/extra/core_ext/time.rb +19 -0
  52. data/lib/nanoc3/extra/core_ext.rb +3 -0
  53. data/lib/nanoc3/extra/deployers/rsync.rb +64 -0
  54. data/lib/nanoc3/extra/deployers.rb +12 -0
  55. data/lib/nanoc3/extra/file_proxy.rb +31 -0
  56. data/lib/nanoc3/extra/validators/links.rb +0 -0
  57. data/lib/nanoc3/extra/validators/w3c.rb +71 -0
  58. data/lib/nanoc3/extra/validators.rb +12 -0
  59. data/lib/nanoc3/extra/vcs.rb +65 -0
  60. data/lib/nanoc3/extra/vcses/bazaar.rb +21 -0
  61. data/lib/nanoc3/extra/vcses/dummy.rb +20 -0
  62. data/lib/nanoc3/extra/vcses/git.rb +21 -0
  63. data/lib/nanoc3/extra/vcses/mercurial.rb +21 -0
  64. data/lib/nanoc3/extra/vcses/subversion.rb +21 -0
  65. data/lib/nanoc3/extra/vcses.rb +17 -0
  66. data/lib/nanoc3/extra.rb +16 -0
  67. data/lib/nanoc3/filters/bluecloth.rb +13 -0
  68. data/lib/nanoc3/filters/coderay.rb +17 -0
  69. data/lib/nanoc3/filters/erb.rb +19 -0
  70. data/lib/nanoc3/filters/erubis.rb +17 -0
  71. data/lib/nanoc3/filters/haml.rb +20 -0
  72. data/lib/nanoc3/filters/less.rb +13 -0
  73. data/lib/nanoc3/filters/markaby.rb +14 -0
  74. data/lib/nanoc3/filters/maruku.rb +14 -0
  75. data/lib/nanoc3/filters/rainpress.rb +13 -0
  76. data/lib/nanoc3/filters/rdiscount.rb +13 -0
  77. data/lib/nanoc3/filters/rdoc.rb +23 -0
  78. data/lib/nanoc3/filters/redcloth.rb +14 -0
  79. data/lib/nanoc3/filters/relativize_paths.rb +32 -0
  80. data/lib/nanoc3/filters/rubypants.rb +14 -0
  81. data/lib/nanoc3/filters/sass.rb +17 -0
  82. data/lib/nanoc3/filters.rb +37 -0
  83. data/lib/nanoc3/helpers/blogging.rb +226 -0
  84. data/lib/nanoc3/helpers/breadcrumbs.rb +25 -0
  85. data/lib/nanoc3/helpers/capturing.rb +71 -0
  86. data/lib/nanoc3/helpers/filtering.rb +46 -0
  87. data/lib/nanoc3/helpers/html_escape.rb +22 -0
  88. data/lib/nanoc3/helpers/link_to.rb +120 -0
  89. data/lib/nanoc3/helpers/rendering.rb +76 -0
  90. data/lib/nanoc3/helpers/tagging.rb +58 -0
  91. data/lib/nanoc3/helpers/text.rb +40 -0
  92. data/lib/nanoc3/helpers/xml_sitemap.rb +69 -0
  93. data/lib/nanoc3/helpers.rb +16 -0
  94. data/lib/nanoc3/package.rb +106 -0
  95. data/lib/nanoc3/tasks/clean.rake +16 -0
  96. data/lib/nanoc3/tasks/clean.rb +33 -0
  97. data/lib/nanoc3/tasks/deploy/rsync.rake +11 -0
  98. data/lib/nanoc3/tasks/validate.rake +35 -0
  99. data/lib/nanoc3/tasks.rb +9 -0
  100. data/lib/nanoc3.rb +19 -0
  101. data/vendor/cri/ChangeLog +0 -0
  102. data/vendor/cri/LICENSE +19 -0
  103. data/vendor/cri/NEWS +0 -0
  104. data/vendor/cri/README +4 -0
  105. data/vendor/cri/Rakefile +25 -0
  106. data/vendor/cri/lib/cri/base.rb +153 -0
  107. data/vendor/cri/lib/cri/command.rb +105 -0
  108. data/vendor/cri/lib/cri/core_ext/string.rb +41 -0
  109. data/vendor/cri/lib/cri/core_ext.rb +8 -0
  110. data/vendor/cri/lib/cri/option_parser.rb +186 -0
  111. data/vendor/cri/lib/cri.rb +12 -0
  112. data/vendor/cri/test/test_base.rb +6 -0
  113. data/vendor/cri/test/test_command.rb +6 -0
  114. data/vendor/cri/test/test_core_ext.rb +21 -0
  115. data/vendor/cri/test/test_option_parser.rb +279 -0
  116. metadata +225 -0
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::DataSources
4
+
5
+ # Nanoc3::DataSources::Delicious provides for a delicious.com bookmarks from
6
+ # a single user as items (Nanoc3::Item instances).
7
+ #
8
+ # The configuration must have a "username" attribute containing the username
9
+ # of the account from which to fetch the bookmarks.
10
+ #
11
+ # The items returned by this data source will be mounted at {root}/{id},
12
+ # where +id+ is a sequence number that is not necessarily unique for this
13
+ # bookmark (because delicious.com unfortunately does not provide unique IDs
14
+ # for each bookmark).
15
+ #
16
+ # The items returned by this data source will have the following attributes:
17
+ #
18
+ # +:url+:: The URL the bookmark leads to.
19
+ #
20
+ # +:description+:: The description (title, usually) of the bookmark.
21
+ #
22
+ # +:tags+:: An array of tags (strings) for this bookmark.
23
+ #
24
+ # +:date+:: The timestamp when this bookmark was created (a Time object).
25
+ #
26
+ # +:note+:: The personal note for this bookmark (can be nil).
27
+ #
28
+ # +:author+:: The username of the person storing the bookmark.
29
+ class Delicious < Nanoc3::DataSource
30
+
31
+ def items
32
+ @items ||= begin
33
+ require 'json'
34
+ require 'time'
35
+
36
+ # Get data
37
+ @http_client ||= Nanoc3::Extra::CHiCk::Client.new
38
+ status, headers, data = *@http_client.get("http://feeds.delicious.com/v2/json/#{self.config[:username]}")
39
+
40
+ # Parse as JSON
41
+ raw_items = JSON.parse(data)
42
+
43
+ # Convert to items
44
+ raw_items.enum_with_index.map do |raw_item, i|
45
+ # Get data
46
+ content = raw_item['n']
47
+ attributes = {
48
+ :url => raw_item['u'],
49
+ :description => raw_item['d'],
50
+ :tags => raw_item['t'],
51
+ :date => Time.parse(raw_item['dt']),
52
+ :note => raw_item['n'],
53
+ :author => raw_item['a']
54
+ }
55
+ identifier = "/#{i}/"
56
+ mtime = nil
57
+
58
+ # Build item
59
+ Nanoc3::Item.new(content, attributes, identifier, mtime)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,231 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::DataSources
4
+
5
+ # The filesystem data source is the default data source for a new nanoc
6
+ # site. It stores all data as files on the hard disk.
7
+ #
8
+ # None of the methods are documented in this file. See Nanoc3::DataSource
9
+ # for documentation on the overridden methods instead.
10
+ #
11
+ # = Items
12
+ #
13
+ # The filesystem data source stores its items in nested directories. Each
14
+ # directory represents a single item. The root directory is the 'content'
15
+ # directory.
16
+ #
17
+ # Every directory has a content file and a meta file. The content file
18
+ # contains the actual item content, while the meta file contains the item's
19
+ # metadata, formatted as YAML.
20
+ #
21
+ # Both content files and meta files are named after its parent directory
22
+ # (i.e. item). For example, a item named 'foo' will have a directory named
23
+ # 'foo', with e.g. a 'foo.markdown' content file and a 'foo.yaml' meta file.
24
+ #
25
+ # Content file extensions are not used for determining the filter that
26
+ # should be run; the meta file defines the list of filters. The meta file
27
+ # extension must always be 'yaml', though.
28
+ #
29
+ # Content files can also have the 'index' basename. Similarly, meta files
30
+ # can have the 'meta' basename. For example, a parent directory named 'foo'
31
+ # can have an 'index.txt' content file and a 'meta.yaml' meta file. This is
32
+ # to preserve backward compatibility.
33
+ #
34
+ # = Layouts
35
+ #
36
+ # Layouts are stored as directories in the 'layouts' directory. Each layout
37
+ # contains a content file and a meta file. The content file contain the
38
+ # actual layout, and the meta file describes how the item should be handled
39
+ # (contains the filter that should be used).
40
+ #
41
+ # For backward compatibility, a layout can also be a single file in the
42
+ # 'layouts' directory. Such a layout cannot have any metadata; the filter
43
+ # used for this layout is determined from the file extension.
44
+ #
45
+ # = Code Snippets
46
+ #
47
+ # Code snippets are stored in '.rb' files in the 'lib' directory. Code
48
+ # snippets can reside in sub-directories.
49
+ class Filesystem < Nanoc3::DataSource
50
+
51
+ include Nanoc3::DataSources::FilesystemCommon
52
+
53
+ ########## VCSes ##########
54
+
55
+ attr_accessor :vcs
56
+
57
+ def vcs
58
+ @vcs ||= Nanoc3::Extra::VCSes::Dummy.new
59
+ end
60
+
61
+ ########## Preparation ##########
62
+
63
+ def up
64
+ end
65
+
66
+ def down
67
+ end
68
+
69
+ def setup
70
+ # Create directories
71
+ %w( content layouts lib ).each do |dir|
72
+ FileUtils.mkdir_p(dir)
73
+ vcs.add(dir)
74
+ end
75
+ end
76
+
77
+ ########## Loading data ##########
78
+
79
+ def items
80
+ meta_filenames('content').map do |meta_filename|
81
+ # Read metadata
82
+ meta = YAML.load_file(meta_filename) || {}
83
+
84
+ # Get content
85
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
86
+ content = File.read(content_filename)
87
+
88
+ # Get attributes
89
+ attributes = meta.merge(:file => Nanoc3::Extra::FileProxy.new(content_filename))
90
+
91
+ # Get identifier
92
+ identifier = meta_filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')
93
+
94
+ # Get modification times
95
+ meta_mtime = File.stat(meta_filename).mtime
96
+ content_mtime = File.stat(content_filename).mtime
97
+ mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
98
+
99
+ # Create item object
100
+ Nanoc3::Item.new(content, attributes, identifier, mtime)
101
+ end
102
+ end
103
+
104
+ def layouts
105
+ meta_filenames('layouts').map do |meta_filename|
106
+ # Get content
107
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
108
+ content = File.read(content_filename)
109
+
110
+ # Get attributes
111
+ attributes = YAML.load_file(meta_filename) || {}
112
+
113
+ # Get identifier
114
+ identifier = meta_filename.sub(/^layouts\//, '').sub(/\/[^\/]+\.yaml$/, '')
115
+
116
+ # Get modification times
117
+ meta_mtime = File.stat(meta_filename).mtime
118
+ content_mtime = File.stat(content_filename).mtime
119
+ mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
120
+
121
+ # Create layout object
122
+ Nanoc3::Layout.new(content, attributes, identifier, mtime)
123
+ end
124
+ end
125
+
126
+ ########## Creating data ##########
127
+
128
+ # Creates a new item with the given content, attributes and identifier.
129
+ def create_item(content, attributes, identifier)
130
+ # Determine base path
131
+ last_component = identifier.split('/')[-1] || 'content'
132
+ base_path = 'content' + identifier + last_component
133
+
134
+ # Get filenames
135
+ dir_path = 'content' + identifier
136
+ meta_filename = 'content' + identifier + last_component + '.yaml'
137
+ content_filename = 'content' + identifier + last_component + '.html'
138
+
139
+ # Notify
140
+ Nanoc3::NotificationCenter.post(:file_created, meta_filename)
141
+ Nanoc3::NotificationCenter.post(:file_created, content_filename)
142
+
143
+ # Create files
144
+ FileUtils.mkdir_p(dir_path)
145
+ File.open(meta_filename, 'w') { |io| io.write(YAML.dump(attributes.stringify_keys)) }
146
+ File.open(content_filename, 'w') { |io| io.write(content) }
147
+ end
148
+
149
+ # Creates a new layout with the given content, attributes and identifier.
150
+ def create_layout(content, attributes, identifier)
151
+ # Determine base path
152
+ last_component = identifier.split('/')[-1]
153
+ base_path = 'layouts' + identifier + last_component
154
+
155
+ # Get filenames
156
+ dir_path = 'layouts' + identifier
157
+ meta_filename = 'layouts' + identifier + last_component + '.yaml'
158
+ content_filename = 'layouts' + identifier + last_component + '.html'
159
+
160
+ # Notify
161
+ Nanoc3::NotificationCenter.post(:file_created, meta_filename)
162
+ Nanoc3::NotificationCenter.post(:file_created, content_filename)
163
+
164
+ # Create files
165
+ FileUtils.mkdir_p(dir_path)
166
+ File.open(meta_filename, 'w') { |io| io.write(YAML.dump(attributes.stringify_keys)) }
167
+ File.open(content_filename, 'w') { |io| io.write(content) }
168
+ end
169
+
170
+ private
171
+
172
+ ########## Custom functions ##########
173
+
174
+ # Returns the list of all meta files in the given base directory as well
175
+ # as its subdirectories.
176
+ def meta_filenames(base)
177
+ # Find all possible meta file names
178
+ filenames = Dir[base + '/**/*.yaml']
179
+
180
+ # Filter out invalid meta files
181
+ good_filenames = []
182
+ bad_filenames = []
183
+ filenames.each do |filename|
184
+ if filename =~ /meta\.yaml$/ or filename =~ /([^\/]+)\/\1\.yaml$/
185
+ good_filenames << filename
186
+ else
187
+ bad_filenames << filename
188
+ end
189
+ end
190
+
191
+ # Warn about bad filenames
192
+ unless bad_filenames.empty?
193
+ raise RuntimeError.new(
194
+ "The following files appear to be meta files, " +
195
+ "but have an invalid name:\n - " +
196
+ bad_filenames.join("\n - ")
197
+ )
198
+ end
199
+
200
+ good_filenames
201
+ end
202
+
203
+ # Returns the filename of the content file in the given directory,
204
+ # ignoring any unwanted files (files that end with '~', '.orig', '.rej' or
205
+ # '.bak')
206
+ def content_filename_for_dir(dir)
207
+ # Find all files
208
+ filename_glob_1 = dir.sub(/([^\/]+)$/, '\1/\1.*')
209
+ filename_glob_2 = dir.sub(/([^\/]+)$/, '\1/index.*')
210
+ filenames = (Dir[filename_glob_1] + Dir[filename_glob_2]).uniq
211
+
212
+ # Reject meta files
213
+ filenames.reject! { |f| f =~ /\.yaml$/ }
214
+
215
+ # Reject backups
216
+ filenames.reject! { |f| f =~ /(~|\.orig|\.rej|\.bak)$/ }
217
+
218
+ # Make sure there is only one content file
219
+ if filenames.size != 1
220
+ raise RuntimeError.new(
221
+ "Expected 1 content file in #{dir} but found #{filenames.size}"
222
+ )
223
+ end
224
+
225
+ # Return content filename
226
+ filenames.first
227
+ end
228
+
229
+ end
230
+
231
+ end
@@ -0,0 +1,202 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::DataSources
4
+
5
+ # = Items
6
+ #
7
+ # The filesystem data source stores its items in nested directories. A item
8
+ # is represented by a single file. The root directory is the 'content'
9
+ # directory.
10
+ #
11
+ # The metadata for a item is embedded into the file itself. It is stored at
12
+ # the top of the file, between '---' (three dashes) separators. For example:
13
+ #
14
+ # ---
15
+ # title: "Moo!"
16
+ # ---
17
+ # h1. Hello!
18
+ #
19
+ # The identifier of a item is determined as follows. A file with an
20
+ # 'index.*' filename, such as 'index.txt', will have the filesystem path
21
+ # with the 'index.*' part stripped as a identifier. For example,
22
+ # 'foo/bar/index.html' will have '/foo/bar/' as identifier.
23
+ #
24
+ # A file with a filename not starting with 'index.', such as 'foo.html',
25
+ # will have an identifier ending in 'foo/'. For example, 'foo/bar.html' will
26
+ # have '/foo/bar/' as identifier.
27
+ #
28
+ # Note that it is possible for two different, separate files to have the
29
+ # same identifier. It is therefore recommended to avoid such situations.
30
+ #
31
+ # Some more examples:
32
+ #
33
+ # content/index.html --> /
34
+ # content/foo.html --> /foo/
35
+ # content/foo/index.html --> /foo/
36
+ # content/foo/bar.html --> /foo/bar/
37
+ # content/foo/bar/index.html --> /foo/bar/
38
+ #
39
+ # File extensions are ignored by nanoc. The file extension does not
40
+ # determine the filters to run on it; the metadata in the file defines the
41
+ # list of filters.
42
+ #
43
+ # = Layouts
44
+ #
45
+ # Layouts are stored as files in the 'layouts' directory. Similar to items,
46
+ # each layout consists of a metadata part and a content part, separated by
47
+ # '---' (three dashes).
48
+ #
49
+ # = Code Snippets
50
+ #
51
+ # Code snippets are stored in '.rb' files in the 'lib' directory. Code
52
+ # snippets can reside in sub-directories.
53
+ class FilesystemCombined < Nanoc3::DataSource
54
+
55
+ include Nanoc3::DataSources::FilesystemCommon
56
+
57
+ ########## VCSes ##########
58
+
59
+ attr_accessor :vcs
60
+
61
+ def vcs
62
+ @vcs ||= Nanoc3::Extra::VCSes::Dummy.new
63
+ end
64
+
65
+ ########## Preparation ##########
66
+
67
+ def up
68
+ end
69
+
70
+ def down
71
+ end
72
+
73
+ def setup
74
+ # Create directories
75
+ %w( content layouts lib ).each do |dir|
76
+ FileUtils.mkdir_p(dir)
77
+ vcs.add(dir)
78
+ end
79
+ end
80
+
81
+ ########## Loading data ##########
82
+
83
+ def items
84
+ files('content', true).map do |filename|
85
+ # Read and parse data
86
+ meta, content = *parse_file(filename, 'item')
87
+
88
+ # Get attributes
89
+ attributes = meta.merge(:file => Nanoc3::Extra::FileProxy.new(filename))
90
+
91
+ # Get actual identifier
92
+ if filename =~ /\/index\.[^\/]+$/
93
+ identifier = filename.sub(/^content/, '').sub(/index\.[^\/]+$/, '') + '/'
94
+ else
95
+ identifier = filename.sub(/^content/, '').sub(/\.[^\/]+$/, '') + '/'
96
+ end
97
+
98
+ # Get mtime
99
+ mtime = File.stat(filename).mtime
100
+
101
+ # Build item
102
+ Nanoc3::Item.new(content, attributes, identifier, mtime)
103
+ end
104
+ end
105
+
106
+ def layouts
107
+ files('layouts', true).map do |filename|
108
+ # Read and parse data
109
+ meta, content = *parse_file(filename, 'layout')
110
+
111
+ # Get actual identifier
112
+ if filename =~ /\/index\.[^\/]+$/
113
+ identifier = filename.sub(/^layouts/, '').sub(/index\.[^\/]+$/, '') + '/'
114
+ else
115
+ identifier = filename.sub(/^layouts/, '').sub(/\.[^\/]+$/, '') + '/'
116
+ end
117
+
118
+ # Get mtime
119
+ mtime = File.stat(filename).mtime
120
+
121
+ # Build layout
122
+ Nanoc3::Layout.new(content, meta, identifier, mtime)
123
+ end.compact
124
+ end
125
+
126
+ ########## Creating data ##########
127
+
128
+ # Creates a new item with the given content, attributes and identifier.
129
+ def create_item(content, attributes, identifier)
130
+ # Determine path
131
+ if identifier == '/'
132
+ path = 'content/index.html'
133
+ else
134
+ path = 'content' + identifier[0..-2] + '.html'
135
+ end
136
+ parent_path = File.dirname(path)
137
+
138
+ # Notify
139
+ Nanoc3::NotificationCenter.post(:file_created, path)
140
+
141
+ # Write item
142
+ FileUtils.mkdir_p(parent_path)
143
+ File.open(path, 'w') do |io|
144
+ io.write(YAML.dump(attributes.stringify_keys) + "\n")
145
+ io.write("---\n")
146
+ io.write(content)
147
+ end
148
+ end
149
+
150
+ # Creates a new layout with the given content, attributes and identifier.
151
+ def create_layout(content, attributes, identifier)
152
+ # Determine path
153
+ path = 'layouts' + identifier[0..-2] + '.html'
154
+ parent_path = File.dirname(path)
155
+
156
+ # Notify
157
+ Nanoc3::NotificationCenter.post(:file_created, path)
158
+
159
+ # Write layout
160
+ FileUtils.mkdir_p(parent_path)
161
+ File.open(path, 'w') do |io|
162
+ io.write(YAML.dump(attributes.stringify_keys) + "\n")
163
+ io.write("---\n")
164
+ io.write(content)
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ # Returns a list of all files in +dir+, ignoring any unwanted files (files
171
+ # that end with '~', '.orig', '.rej' or '.bak').
172
+ #
173
+ # +recursively+:: When +true+, finds files in +dir+ as well as its
174
+ # subdirectories; when +false+, only searches +dir+
175
+ # itself.
176
+ def files(dir, recursively)
177
+ glob = File.join([dir] + (recursively ? [ '**', '*' ] : [ '*' ]))
178
+ Dir[glob].reject { |f| File.directory?(f) or f =~ /(~|\.orig|\.rej|\.bak)$/ }
179
+ end
180
+
181
+ # Parses the file named +filename+ and returns an array with its first
182
+ # element a hash with the file's metadata, and with its second element the
183
+ # file content itself.
184
+ def parse_file(filename, kind)
185
+ # Split file
186
+ pieces = File.read(filename).split(/^(-{5}|-{3})/).compact
187
+ if pieces.size < 3
188
+ raise RuntimeError.new(
189
+ "The file '#{filename}' does not seem to be a nanoc #{kind}"
190
+ )
191
+ end
192
+
193
+ # Parse
194
+ meta = YAML.load(pieces[2]) || {}
195
+ content = pieces[4..-1].join.strip
196
+
197
+ [ meta, content ]
198
+ end
199
+
200
+ end
201
+
202
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::DataSources
4
+
5
+ # The Nanoc3::DataSources::FilesystemCommon module provides code
6
+ # snippet-loading and rule-loading methods that are used by both the
7
+ # filesystem and the filesystem_combined data sources.
8
+ module FilesystemCommon
9
+
10
+ def code_snippets
11
+ Dir['lib/**/*.rb'].sort.map do |filename|
12
+ Nanoc3::CodeSnippet.new(
13
+ File.read(filename),
14
+ filename.sub(/^lib\//, ''),
15
+ File.stat(filename).mtime
16
+ )
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end