lib/nanoc/data_sources/filesystem.rb in nanoc-2.0.4 vs lib/nanoc/data_sources/filesystem.rb in nanoc-2.1
- old
+ new
@@ -1,278 +1,588 @@
-module Nanoc::DataSource::Filesystem
+module Nanoc::DataSources
- class FileProxy
+ # The filesystem data source is the default data source for a new nanoc
+ # site. It stores all data as files on the hard disk.
+ #
+ # None of the methods are documented in this file. See Nanoc::DataSource for
+ # documentation on the overridden methods instead.
+ #
+ # = Pages
+ #
+ # The filesystem data source stores its pages in nested directories. Each
+ # directory represents a single page. The root directory is the 'content'
+ # directory.
+ #
+ # Every directory has a content file and a meta file. The content file
+ # contains the actual page content, while the meta file contains the page's
+ # metadata, formatted as YAML.
+ #
+ # Both content files and meta files are named after its parent directory
+ # (i.e. page). For example, a page named 'foo' will have a directorynamed
+ # 'foo', with e.g. a 'foo.markdown' content file and a 'foo.yaml' meta file.
+ #
+ # Content file extensions are not used for determining the filter that
+ # should be run; the meta file defines the list of filters. The meta file
+ # extension must always be 'yaml', though.
+ #
+ # Content files can also have the 'index' basename. Similarly, meta files
+ # can have the 'meta' basename. For example, a parent directory named 'foo'
+ # can have an 'index.txt' content file and a 'meta.yaml' meta file. This is
+ # to preserve backward compatibility.
+ #
+ # = Page defaults
+ #
+ # The page defaults are loaded from a YAML-formatted file named
+ # 'page_defaults.yaml' at the top level of the nanoc site directory. For
+ # backward compatibility, the file can also be named 'meta.yaml'.
+ #
+ # = Assets
+ #
+ # Assets are stored in the 'assets' directory (surprise!). The structure is
+ # very similar to the structure of the 'content' directory, so see the Pages
+ # section for details on how this directory is structured.
+ #
+ # = Asset defaults
+ #
+ # The asset defaults are stored similar to the way page defaults are stored,
+ # except that the asset defaults file is named 'asset_defaults.yaml'
+ # instead.
+ #
+ # = Layouts
+ #
+ # Layouts are stored as directories in the 'layouts' directory. Each layout
+ # contains a content file and a meta file. The content file contain the
+ # actual layout, and the meta file describes how the page should be handled
+ # (contains the filter that should be used).
+ #
+ # For backward compatibility, a layout can also be a single file in the
+ # 'layouts' directory. Such a layout cannot have any metadata; the filter
+ # used for this layout is determined from the file extension.
+ #
+ # = Templates
+ #
+ # Templates are located in the 'templates' directroy. Every template is a
+ # directory consisting of a content file and a meta file, both named after
+ # the template. This is very similar to the way pages are stored, except
+ # that templates cannot be nested.
+ #
+ # = Code
+ #
+ # Code is stored in '.rb' files in the 'lib' directory. Code can reside in
+ # sub-directories.
+ class Filesystem < Nanoc::DataSource
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
+ PAGE_DEFAULTS_FILENAME = 'page_defaults.yaml'
+ PAGE_DEFAULTS_FILENAME_OLD = 'meta.yaml'
+ ASSET_DEFAULTS_FILENAME = 'asset_defaults.yaml'
- def initialize(path)
- @path = path
- end
+ ########## Attributes ##########
- def method_missing(sym, *args, &block)
- File.new(@path).__send__(sym, *args, &block)
+ identifier :filesystem
+
+ ########## VCSes ##########
+
+ attr_accessor :vcs
+
+ def vcs
+ @vcs ||= Nanoc::Extra::VCSes::Dummy.new
end
- end
+ ########## Preparation ##########
- class FilesystemDataSource < Nanoc::DataSource
+ def up # :nodoc:
+ end
- ########## Attributes ##########
+ def down # :nodoc:
+ end
- identifier :filesystem
+ def setup # :nodoc:
+ # Create directories
+ %w( assets content templates layouts lib ).each do |dir|
+ FileUtils.mkdir_p(dir)
+ vcs.add(dir)
+ end
+ end
- ########## Preparation ##########
+ def destroy # :nodoc:
+ # Remove files
+ vcs.remove(ASSET_DEFAULTS_FILENAME) if File.file?(ASSET_DEFAULTS_FILENAME)
+ vcs.remove(PAGE_DEFAULTS_FILENAME) if File.file?(PAGE_DEFAULTS_FILENAME)
+ vcs.remove(PAGE_DEFAULTS_FILENAME_OLD) if File.file?(PAGE_DEFAULTS_FILENAME_OLD)
- def up
+ # Remove directories
+ vcs.remove('assets')
+ vcs.remove('content')
+ vcs.remove('templates')
+ vcs.remove('layouts')
+ vcs.remove('lib')
end
- def down
+ def update # :nodoc:
+ update_page_defaults
+ update_pages
+ update_layouts
+ update_templates
end
- def setup
- # Create page
- FileManager.create_file 'content/content.txt' do
- "I'm a brand new root page. Please edit me!\n"
+ ########## Pages ##########
+
+ def pages # :nodoc:
+ meta_filenames('content').map do |meta_filename|
+ # Read metadata
+ meta = YAML.load_file(meta_filename) || {}
+
+ if meta['is_draft']
+ # Skip drafts
+ nil
+ else
+ # Get content
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
+ content = File.read(content_filename)
+
+ # Get attributes
+ attributes = meta.merge(:file => Nanoc::Extra::FileProxy.new(content_filename))
+
+ # Get path
+ path = meta_filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')
+
+ # Get modification times
+ meta_mtime = File.stat(meta_filename).mtime
+ content_mtime = File.stat(content_filename).mtime
+ mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
+
+ # Create page object
+ Nanoc::Page.new(content, attributes, path, mtime)
+ end
+ end.compact
+ end
+
+ def save_page(page) # :nodoc:
+ # Determine possible meta file paths
+ last_component = page.path.split('/')[-1]
+ meta_filename_worst = 'content' + page.path + 'index.yaml'
+ meta_filename_best = 'content' + page.path + (last_component || 'content') + '.yaml'
+
+ # Get existing path
+ existing_path = nil
+ existing_path = meta_filename_best if File.file?(meta_filename_best)
+ existing_path = meta_filename_worst if File.file?(meta_filename_worst)
+
+ if existing_path.nil?
+ # Get filenames
+ dir_path = 'content' + page.path
+ meta_filename = meta_filename_best
+ content_filename = 'content' + page.path + (last_component || 'content') + '.html'
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_created, meta_filename)
+ Nanoc::NotificationCenter.post(:file_created, content_filename)
+
+ # Create directories if necessary
+ FileUtils.mkdir_p(dir_path)
+ else
+ # Get filenames
+ meta_filename = existing_path
+ content_filename = content_filename_for_dir(File.dirname(existing_path))
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_updated, meta_filename)
+ Nanoc::NotificationCenter.post(:file_updated, content_filename)
end
- FileManager.create_file 'content/content.yaml' do
- "# Built-in\n" +
- "\n" +
- "# Custom\n" +
- "title: \"A New Root Page\"\n"
+
+ # Write files
+ File.open(meta_filename, 'w') { |io| io.write(page.attributes.to_split_yaml) }
+ File.open(content_filename, 'w') { |io| io.write(page.content) }
+
+ # Add to working copy if possible
+ if existing_path.nil?
+ vcs.add(meta_filename)
+ vcs.add(content_filename)
end
+ end
- # Create page defaults
- FileManager.create_file 'meta.yaml' do
- "# This file contains the default values for all metafiles.\n" +
- "# Other metafiles can override the contents of this one.\n" +
- "\n" +
- "# Built-in\n" +
- "custom_path: none\n" +
- "extension: \"html\"\n" +
- "filename: \"index\"\n" +
- "filters_post: []\n" +
- "filters_pre: []\n" +
- "is_draft: false\n" +
- "layout: \"default\"\n" +
- "skip_output: false\n" +
- "\n" +
- "# Custom\n"
+ def move_page(page, new_path) # :nodoc:
+ # TODO implement
+ end
+
+ def delete_page(page) # :nodoc:
+ # TODO implement
+ end
+
+ ########## Assets ##########
+
+ def assets # :nodoc:
+ meta_filenames('assets').map do |meta_filename|
+ # Read metadata
+ meta = YAML.load_file(meta_filename) || {}
+
+ # Get content file
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
+ content_file = File.new(content_filename)
+
+ # Get attributes
+ attributes = meta.merge(:extension => File.extname(content_filename)[1..-1])
+
+ # Get path
+ path = meta_filename.sub(/^assets/, '').sub(/[^\/]+\.yaml$/, '')
+
+ # Get modification times
+ meta_mtime = File.stat(meta_filename).mtime
+ content_mtime = File.stat(content_filename).mtime
+ mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
+
+ # Create asset object
+ Nanoc::Asset.new(content_file, attributes, path, mtime)
end
+ end
- # Create template
- FileManager.create_file 'templates/default/default.txt' do
- "Hi, I'm a new page!\n"
+ def save_asset(asset) # :nodoc:
+ # Determine meta file path
+ last_component = asset.path.split('/')[-1]
+ meta_filename = 'assets' + asset.path + last_component + '.yaml'
+
+ # Get existing path
+ existing_path = nil
+ existing_path = meta_filename_best if File.file?(meta_filename_best)
+ existing_path = meta_filename_worst if File.file?(meta_filename_worst)
+
+ if meta_filename.nil?
+ # Get filenames
+ dir_path = 'assets' + asset.path
+ content_filename = 'assets' + asset.path + last_component + '.dat'
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_created, meta_filename)
+ Nanoc::NotificationCenter.post(:file_created, content_filename)
+
+ # Create directories if necessary
+ FileUtils.mkdir_p(dir_path)
+ else
+ # Get filenames
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_updated, meta_filename)
+ Nanoc::NotificationCenter.post(:file_updated, content_filename)
end
- FileManager.create_file 'templates/default/default.yaml' do
- "# Built-in\n" +
- "\n" +
- "# Custom\n" +
- "title: \"A New Page\"\n"
+
+ # Write files
+ File.open(meta_filename, 'w') { |io| io.write(asset.attributes.to_split_yaml) }
+ File.open(content_filename, 'w') { }
+
+ # Add to working copy if possible
+ if meta_filename.nil?
+ vcs.add(meta_filename)
+ vcs.add(content_filename)
end
+ end
- # Create layout
- FileManager.create_file 'layouts/default.erb' do
- "<html>\n" +
- " <head>\n" +
- " <title><%= @page.title %></title>\n" +
- " </head>\n" +
- " <body>\n" +
- "<%= @page.content %>\n" +
- " </body>\n" +
- "</html>\n"
+ def move_asset(asset, new_path) # :nodoc:
+ # TODO implement
+ end
+
+ def delete_asset(asset) # :nodoc:
+ # TODO implement
+ end
+
+ ########## Page Defaults ##########
+
+ def page_defaults # :nodoc:
+ # Get attributes
+ filename = File.file?(PAGE_DEFAULTS_FILENAME) ? PAGE_DEFAULTS_FILENAME : PAGE_DEFAULTS_FILENAME_OLD
+ attributes = YAML.load_file(filename) || {}
+
+ # Get mtime
+ mtime = File.stat(filename).mtime
+
+ # Build page defaults
+ Nanoc::PageDefaults.new(attributes, mtime)
+ end
+
+ def save_page_defaults(page_defaults) # :nodoc:
+ # Notify
+ if File.file?(PAGE_DEFAULTS_FILENAME)
+ filename = PAGE_DEFAULTS_FILENAME
+ created = false
+ Nanoc::NotificationCenter.post(:file_updated, filename)
+ elsif File.file?(PAGE_DEFAULTS_FILENAME_OLD)
+ filename = PAGE_DEFAULTS_FILENAME_OLD
+ created = false
+ Nanoc::NotificationCenter.post(:file_updated, filename)
+ else
+ filename = PAGE_DEFAULTS_FILENAME
+ created = true
+ Nanoc::NotificationCenter.post(:file_created, filename)
end
- # Create code
- FileManager.create_file 'lib/default.rb' do
- "\# All files in the 'lib' directory will be loaded\n" +
- "\# before nanoc starts compiling.\n" +
- "\n" +
- "def html_escape(str)\n" +
- " str.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('\"', '"')\n" +
- "end\n" +
- "alias h html_escape\n"
+ # Write
+ File.open(filename, 'w') do |io|
+ io.write(page_defaults.attributes.to_split_yaml)
end
+ # Add to working copy if possible
+ vcs.add(filename) if created
end
- ########## Loading data ##########
+ ########## Asset Defaults ##########
- # The filesystem data source stores its pages in nested directories. Each
- # directory represents a single page. The root directory is the 'content'
- # directory.
- #
- # Every directory has a content file and a meta file. The content file
- # contains the actual page content, while the meta file contains the
- # page's metadata.
- #
- # Both content files and meta files are named after its parent directory
- # (i.e. page). For example, a page named 'foo' will have a directory named
- # 'foo', with e.g. a 'foo.markdown' content file and a 'foo.yaml' meta
- # file.
- #
- # Content file extensions are ignored by nanoc. The content file extension
- # does not determine the filters to run on it; the meta file defines the
- # list of filters. The meta file extension must always be 'yaml', though.
- #
- # Content files can also have the 'index' basename. Similarly, meta files
- # can have the 'meta' basename. For example, a parent directory named
- # 'foo' can have an 'index.txt' content file and a 'meta.yaml' meta file.
- # This is to preserve backward compatibility.
- def pages
- meta_filenames.inject([]) do |pages, filename|
- # Read metadata
- meta = (YAML.load_file(filename) || {}).clean
+ def asset_defaults # :nodoc:
+ if File.file?(ASSET_DEFAULTS_FILENAME)
+ # Get attributes
+ attributes = YAML.load_file(ASSET_DEFAULTS_FILENAME) || {}
- if meta[:is_draft]
- # Skip drafts
- pages
- else
- # Get extra info
- path = filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')
- file = content_file_for_dir(File.dirname(filename))
- extras = {
- :path => path,
- :file => FileProxy.new(file.path),
- :uncompiled_content => file.read
- }
+ # Get mtime
+ mtime = File.stat(ASSET_DEFAULTS_FILENAME).mtime
- # Add to list of pages
- pages + [ meta.merge(extras) ]
- end
+ # Build asset defaults
+ Nanoc::AssetDefaults.new(attributes, mtime)
+ else
+ Nanoc::AssetDefaults.new({})
end
end
- # The page defaults are loaded from a 'meta.yaml' file
- def page_defaults
- (YAML.load_file('meta.yaml') || {}).clean
+ def save_asset_defaults(asset_defaults) # :nodoc:
+ # Notify
+ if File.file?(ASSET_DEFAULTS_FILENAME)
+ Nanoc::NotificationCenter.post(:file_updated, ASSET_DEFAULTS_FILENAME)
+ created = false
+ else
+ Nanoc::NotificationCenter.post(:file_created, ASSET_DEFAULTS_FILENAME)
+ created = true
+ end
+
+ # Write
+ File.open(ASSET_DEFAULTS_FILENAME, 'w') do |io|
+ io.write(asset_defaults.attributes.to_split_yaml)
+ end
+
+ # Add to working copy if possible
+ vcs.add(ASSET_DEFAULTS_FILENAME) if created
end
- # Layouts are stored as files in the 'layouts' directory. Each layout has
- # a basename (the part before the extension) and an extension. Unlike page
- # content files, the extension _is_ used for determining the layout
- # processor; which extension maps to which layout processor is defined in
- # the layout processors.
- def layouts
- Dir["layouts/*"].reject { |f| f =~ /~$/ }.map do |filename|
- # Get layout details
- extension = File.extname(filename)
- name = File.basename(filename, extension)
- content = File.read(filename)
+ ########## Layouts ##########
- # Build hash for layout
- { :name => name, :content => content, :extension => extension }
+ def layouts # :nodoc:
+ # Determine what layout directory structure is being used
+ dir_count = Dir[File.join('layouts', '*')].select { |f| File.directory?(f) }.size
+ is_old_school = (dir_count == 0)
+
+ if is_old_school
+ # Warn about deprecation
+ warn(
+ 'nanoc 2.1 changes the way layouts are stored. Future versions will not support these outdated sites. To update your site, issue \'nanoc update\'.',
+ 'DEPRECATION WARNING'
+ )
+
+ Dir[File.join('layouts', '*')].reject { |f| f =~ /~$/ }.map do |filename|
+ # Get content
+ content = File.read(filename)
+
+ # Get attributes
+ attributes = { :extension => File.extname(filename)}
+
+ # Get path
+ path = File.basename(filename, attributes[:extension])
+
+ # Get modification time
+ mtime = File.stat(filename).mtime
+
+ # Create layout object
+ Nanoc::Layout.new(content, attributes, path, mtime)
+ end
+ else
+ meta_filenames('layouts').map do |meta_filename|
+ # Get content
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
+ content = File.read(content_filename)
+
+ # Get attributes
+ attributes = YAML.load_file(meta_filename) || {}
+
+ # Get path
+ path = meta_filename.sub(/^layouts\//, '').sub(/\/[^\/]+\.yaml$/, '')
+
+ # Get modification times
+ meta_mtime = File.stat(meta_filename).mtime
+ content_mtime = File.stat(content_filename).mtime
+ mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
+
+ # Create layout object
+ Nanoc::Layout.new(content, attributes, path, mtime)
+ end
end
end
- # Templates are located in the 'templates' directroy. Every template is a
- # directory consisting of a content file and a meta file, both named after
- # the template. This is very similar to the way pages are stored, except
- # that templates cannot be nested.
- def templates
- meta_filenames('templates').inject([]) do |templates, filename|
- # Get template name
- name = filename.sub(/^templates\/(.*)\/[^\/]+\.yaml$/, '\1')
+ def save_layout(layout) # :nodoc:
+ # Determine what layout directory structure is being used
+ layout_file_count = Dir[File.join('layouts', '*')].select { |f| File.file?(f) }.size
+ error_outdated if layout_file_count > 0
- # Get file names
- meta_filename = filename
- content_filenames = Dir['templates/' + name + '/' + name + '.*'] +
- Dir['templates/' + name + '/index.*'] -
- Dir['templates/' + name + '/*.yaml' ]
+ # Get paths
+ last_component = layout.path.split('/')[-1]
+ dir_path = 'layouts' + layout.path
+ meta_filename = dir_path + last_component + '.yaml'
+ content_filename = Dir[dir_path + last_component + '.*'][0]
- # Read files
- extension = nil
- content = nil
- content_filenames.each do |content_filename|
- content = File.read(content_filename)
- extension = File.extname(content_filename)
- end
- meta = File.read(meta_filename)
+ if File.file?(meta_filename)
+ created = false
- # Add it to the list of templates
- templates + [{
- :name => name,
- :extension => extension,
- :content => content,
- :meta => meta
- }]
+ # Notify
+ Nanoc::NotificationCenter.post(:file_updated, meta_filename)
+ Nanoc::NotificationCenter.post(:file_updated, content_filename)
+ else
+ created = true
+
+ # Create dir
+ FileUtils.mkdir_p(dir_path)
+
+ # Get content filename
+ content_filename = dir_path + last_component + '.html'
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_created, meta_filename)
+ Nanoc::NotificationCenter.post(:file_created, content_filename)
end
+
+ # Write files
+ File.open(meta_filename, 'w') { |io| io.write(layout.attributes.to_split_yaml) }
+ File.open(content_filename, 'w') { |io| io.write(layout.content) }
+
+ # Add to working copy if possible
+ if created
+ vcs.add(meta_filename)
+ vcs.add(content_filename)
+ end
end
- # Code is stored in '.rb' files in the 'lib' directory. Code can reside
- # in sub-directories.
- def code
- Dir['lib/**/*.rb'].sort.inject('') { |m, f| m + File.read(f) + "\n" }
+ def move_layout(layout, new_path) # :nodoc:
+ # TODO implement
end
- ########## Creating data ##########
+ def delete_layout(layout) # :nodoc:
+ # TODO implement
+ end
- # Creating a page creates a page directory with the name of the page in
- # the 'content' directory, as well as a content file named xxx.txt and a
- # meta file named xxx.yaml (with xxx being the name of the page).
- def create_page(path, template)
- # Make sure path does not start or end with a slash
- sanitized_path = path.gsub(/^\/+|\/+$/, '')
+ ########## Templates ##########
- # Get paths
- dir_path = 'content/' + sanitized_path
- name = sanitized_path.sub(/.*\/([^\/]+)$/, '\1')
- meta_path = dir_path + '/' + name + '.yaml'
- content_path = dir_path + '/' + name + template[:extension]
+ def templates # :nodoc:
+ meta_filenames('templates').map do |meta_filename|
+ # Get name
+ name = meta_filename.sub(/^templates\/(.*)\/[^\/]+\.yaml$/, '\1')
- # Make sure the page doesn't exist yet
- error "A page named '#{path}' already exists." if File.exist?(meta_path)
+ # Get content
+ content_filename = content_filename_for_dir(File.dirname(meta_filename))
+ content = File.read(content_filename)
- # Create index and meta file
- FileManager.create_file(meta_path) { template[:meta] }
- FileManager.create_file(content_path) { template[:content] }
+ # Get attributes
+ attributes = YAML.load_file(meta_filename) || {}
+
+ # Build template
+ Nanoc::Template.new(content, attributes, name)
+ end
end
- # Creating a layout creates a single file in the 'layouts' directory,
- # named xxx.erb (with xxx being the name of the layout).
- def create_layout(name)
- # Get details
- path = 'layouts/' + name + '.erb'
+ def save_template(template) # :nodoc:
+ # Determine possible meta file paths
+ meta_filename_worst = 'templates/' + template.name + '/index.yaml'
+ meta_filename_best = 'templates/' + template.name + '/' + template.name + '.yaml'
- # Make sure the layout doesn't exist yet
- error "A layout named '#{name}' already exists." if File.exist?(path)
+ # Get existing path
+ existing_path = nil
+ existing_path = meta_filename_best if File.file?(meta_filename_best)
+ existing_path = meta_filename_worst if File.file?(meta_filename_worst)
- # Create layout file
- FileManager.create_file(path) do
- "<html>\n" +
- " <head>\n" +
- " <title><%= @page.title %></title>\n" +
- " </head>\n" +
- " <body>\n" +
- "<%= @page.content %>\n" +
- " </body>\n" +
- "</html>\n"
+ if existing_path.nil?
+ # Get filenames
+ dir_path = 'templates/' + template.name
+ meta_filename = meta_filename_best
+ content_filename = 'templates/' + template.name + '/' + template.name + '.html'
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_created, meta_filename)
+ Nanoc::NotificationCenter.post(:file_created, content_filename)
+
+ # Create directories if necessary
+ FileUtils.mkdir_p(dir_path)
+ else
+ # Get filenames
+ meta_filename = existing_path
+ content_filename = content_filename_for_dir(File.dirname(existing_path))
+
+ # Notify
+ Nanoc::NotificationCenter.post(:file_updated, meta_filename)
+ Nanoc::NotificationCenter.post(:file_updated, content_filename)
end
+
+ # Write files
+ File.open(meta_filename, 'w') { |io| io.write(template.page_attributes.to_split_yaml) }
+ File.open(content_filename, 'w') { |io| io.write(template.page_content) }
+
+ # Add to working copy if possible
+ if existing_path.nil?
+ vcs.add(meta_filename)
+ vcs.add(content_filename)
+ end
end
- # Creating a template creates a template directory with the name of the
- # template in the 'templates' directory, as well as a content file named
- # xxx.txt and a meta file named xxx.yaml (with xxx being the name of the
- # template).
- def create_template(name)
- # Get paths
- meta_path = 'templates/' + name + '/' + name + '.yaml'
- content_path = 'templates/' + name + '/' + name + '.txt'
+ def move_template(template, new_name) # :nodoc:
+ # TODO implement
+ end
- # Make sure the template doesn't exist yet
- error "A template named '#{name}' already exists." if File.exist?(meta_path)
+ def delete_template(template) # :nodoc:
+ # TODO implement
+ end
- # Create index and meta file
- FileManager.create_file(meta_path) { "# Built-in\n\n# Custom\ntitle: A New Page\n" }
- FileManager.create_file(content_path) { "Hi, I'm new here!\n" }
+ ########## Code ##########
+
+ def code # :nodoc:
+ # Get files
+ filenames = Dir['lib/**/*.rb'].sort
+
+ # Get data
+ data = filenames.map { |filename| File.read(filename) + "\n" }.join('')
+
+ # Get modification time
+ mtimes = filenames.map { |filename| File.stat(filename).mtime }
+ mtime = mtimes.inject { |memo, mtime| memo > mtime ? mtime : memo }
+
+ # Build code
+ Nanoc::Code.new(data, mtime)
end
+ def save_code(code) # :nodoc:
+ # Check whether code existed
+ existed = File.file?('lib/default.rb')
+
+ # Remove all existing code files
+ Dir['lib/**/*.rb'].each do |file|
+ vcs.remove(file) unless file == 'lib/default.rb'
+ end
+
+ # Notify
+ if existed
+ Nanoc::NotificationCenter.post(:file_updated, 'lib/default.rb')
+ else
+ Nanoc::NotificationCenter.post(:file_created, 'lib/default.rb')
+ end
+
+ # Write new code
+ File.open('lib/default.rb', 'w') do |io|
+ io.write(code.data)
+ end
+
+ # Add to working copy if possible
+ vcs.add('lib/default.rb') unless existed
+ end
+
private
########## Custom functions ##########
- # Returns the list of meta files in the given (optional) base directory.
- def meta_filenames(base='content')
+ # Returns the list of all meta files in the given base directory as well
+ # as its subdirectories.
+ def meta_filenames(base)
# Find all possible meta file names
filenames = Dir[base + '/**/*.yaml']
# Filter out invalid meta files
good_filenames = []
@@ -285,37 +595,148 @@
end
end
# Warn about bad filenames
unless bad_filenames.empty?
- error "The following files appear to be meta files, " +
- "but have an invalid name:\n - " +
- bad_filenames.join("\n - ")
+ raise RuntimeError.new(
+ "The following files appear to be meta files, " +
+ "but have an invalid name:\n - " +
+ bad_filenames.join("\n - ")
+ )
end
good_filenames
end
- # Returns a File object for the content file in the given directory
- def content_file_for_dir(dir)
+ # Returns the filename of the content file in the given directory,
+ # ignoring any unwanted files (files that end with '~', '.orig', '.rej' or
+ # '.bak')
+ def content_filename_for_dir(dir)
# Find all files
filename_glob_1 = dir.sub(/([^\/]+)$/, '\1/\1.*')
filename_glob_2 = dir.sub(/([^\/]+)$/, '\1/index.*')
filenames = Dir[filename_glob_1] + Dir[filename_glob_2]
# Reject meta files
filenames.reject! { |f| f =~ /\.yaml$/ }
# Reject backups
- filenames.reject! { |f| f =~ /~$/ }
+ filenames.reject! { |f| f =~ /(~|\.orig|\.rej|\.bak)$/ }
# Make sure there is only one content file
if filenames.size != 1
- error "Expected 1 content file in #{dir} but found #{filenames.size}"
+ raise RuntimeError.new(
+ "Expected 1 content file in #{dir} but found #{filenames.size}"
+ )
end
- # Read content file
- File.new(filenames.first)
+ # Return content filename
+ filenames.first
+ end
+
+ # Raises an "outdated data format" error.
+ def error_outdated
+ raise RuntimeError.new(
+ 'This site\'s data is stored in an old format and must be updated. ' +
+ 'To do so, issue the \'nanoc update\' command. For help on ' +
+ 'updating a site\'s data, issue \'nanoc help update\'.'
+ )
+ end
+
+ # Updated outdated page defaults (renames page defaults file)
+ def update_page_defaults
+ return unless File.file?(PAGE_DEFAULTS_FILENAME_OLD)
+
+ vcs.move(PAGE_DEFAULTS_FILENAME_OLD, PAGE_DEFAULTS_FILENAME)
+ end
+
+ # Updates outdated pages (both content and meta file names).
+ def update_pages
+ # Update content files
+ # content/foo/bar/baz/index.ext -> content/foo/bar/baz/baz.ext
+ Dir['content/**/index.*'].select { |f| File.file?(f) }.each do |old_filename|
+ # Determine new name
+ if old_filename =~ /^content\/index\./
+ new_filename = old_filename.sub(/^content\/index\./, 'content/content.')
+ else
+ new_filename = old_filename.sub(/([^\/]+)\/index\.([^\/]+)$/, '\1/\1.\2')
+ end
+
+ # Move
+ vcs.move(old_filename, new_filename)
+ end
+
+ # Update meta files
+ # content/foo/bar/baz/meta.yaml -> content/foo/bar/baz/baz.yaml
+ Dir['content/**/meta.yaml'].select { |f| File.file?(f) }.each do |old_filename|
+ # Determine new name
+ if old_filename == 'content/meta.yaml'
+ new_filename = 'content/content.yaml'
+ else
+ new_filename = old_filename.sub(/([^\/]+)\/meta.yaml$/, '\1/\1.yaml')
+ end
+
+ # Move
+ vcs.move(old_filename, new_filename)
+ end
+ end
+
+ # Updates outdated layouts.
+ def update_layouts
+ # layouts/abc.ext -> layouts/abc/abc.{html,yaml}
+ Dir[File.join('layouts', '*')].select { |f| File.file?(f) }.each do |filename|
+ # Get filter class
+ filter_class = Nanoc::Filter.with_extension(File.extname(filename))
+
+ # Get data
+ content = File.read(filename)
+ attributes = { :filter => filter_class.identifier.to_s }
+ path = File.basename(filename, File.extname(filename))
+
+ # Get layout
+ tmp_layout = Nanoc::Layout.new(content, attributes, path)
+
+ # Get filenames
+ last_component = tmp_layout.path.split('/')[-1]
+ dir_path = 'layouts' + tmp_layout.path
+ meta_filename = dir_path + last_component + '.yaml'
+ content_filename = dir_path + last_component + File.extname(filename)
+
+ # Create new files
+ FileUtils.mkdir_p(dir_path)
+ File.open(meta_filename, 'w') { |io| io.write(tmp_layout.attributes.to_split_yaml) }
+ File.open(content_filename, 'w') { |io| io.write(tmp_layout.content) }
+
+ # Add
+ vcs.add(meta_filename)
+ vcs.add(content_filename)
+
+ # Delete old files
+ vcs.remove(filename)
+ end
+ end
+
+ # Updates outdated templates (both content and meta file names).
+ def update_templates
+ # Update content files
+ # templates/foo/index.ext -> templates/foo/foo.ext
+ Dir['templates/**/index.*'].select { |f| File.file?(f) }.each do |old_filename|
+ # Determine new name
+ new_filename = old_filename.sub(/([^\/]+)\/index\.([^\/]+)$/, '\1/\1.\2')
+
+ # Move
+ vcs.move(old_filename, new_filename)
+ end
+
+ # Update meta files
+ # templates/foo/meta.yaml -> templates/foo/foo.yaml
+ Dir['templates/**/meta.yaml'].select { |f| File.file?(f) }.each do |old_filename|
+ # Determine new name
+ new_filename = old_filename.sub(/([^\/]+)\/meta.yaml$/, '\1/\1.yaml')
+
+ # Move
+ vcs.move(old_filename, new_filename)
+ end
end
end
end