nanoc3 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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