module Nanoc class Compiler DEFAULT_CONFIG = { :output_dir => 'output' } FILE_TYPES = { '.erb' => :eruby, '.rhtml' => :eruby, '.haml' => :haml, '.mab' => :markaby, '.liquid' => :liquid } PAGE_DEFAULTS = { :custom_path => nil, :filename => 'index', :extension => 'html', :filters => [], :is_draft => false, :layout => 'default' } def initialize Nanoc.ensure_in_site @config = DEFAULT_CONFIG.merge(YAML.load_file_and_clean('config.yaml')) @global_page = PAGE_DEFAULTS.merge(YAML.load_file_and_clean('meta.yaml')) end def run # Require all Ruby source files in lib/ Dir['lib/*.rb'].each { |f| require f } # Create output assets directory if necessary FileUtils.mkdir_p(@config[:output_dir]) # Copy assets to output directory copy_assets # Compile all pages pages = compile_pages(uncompiled_pages) # Put pages in their layouts pages.each do |page| # Skip pages that should not be outputed next if page.skip_output begin # Prepare layout content content = layouted_page(page, pages) # Write page with layout FileManager.create_file(path_for_page(page)) { content } rescue => exception p = page[:_content_filename] l = page[:layout] handle_exception(exception, "layouting page '#{p}' in layout '#{l}'") end end end private # Copies the contents of the assets directory into the output directory def copy_assets # Remove existing assets Dir['assets/*'].each do |f| FileUtils.remove_entry_secure(f.sub('assets/', @config[:output_dir] + '/'), true) end # Copy assets if File.directory?('assets') and !Dir['assets/*'].empty? FileUtils.cp_r(Dir['assets/*'], @config[:output_dir]) end end # Returns a list of uncompiled pages def uncompiled_pages # Read all meta files pages = Dir['content/**/meta.yaml'].collect do |filename| # Read the meta file page = @global_page.merge(YAML.load_file_and_clean(filename)) # Fix the path page[:path] = filename.sub(/^content/, '').sub('meta.yaml', '') # Get the content filename content_filenames = Dir[filename.sub('meta.yaml', File.basename(File.dirname(filename)) + '.*')] content_filenames.reject! { |f| f =~ /~$/ } content_filenames += Dir["#{File.dirname(filename)}/index.*"] # fallback for nanoc 1.0 content_filenames.ensure_single('content files', File.dirname(filename)) page[:_content_filename] = content_filenames[0] page end # Ignore drafts pages.reject! { |page| page[:is_draft] } pages end # Returns the layout for the given page def layout_for_page(a_page) if a_page[:layout].nil? { :type => :eruby, :content => "<%= @page[:content] %>" } else filenames = Dir["layouts/#{a_page[:layout]}.*"] filenames.ensure_single('layout files', a_page[:layout]) filename = filenames[0] { :type => FILE_TYPES[File.extname(filename)], :content => File.read(filename) } end end # Returns the path for the given page def path_for_page(a_page) if a_page[:custom_path].nil? @config[:output_dir] + a_page[:path] + a_page[:filename] + '.' + a_page[:extension] else @config[:output_dir] + a_page[:custom_path] end end # Compiles the given pages and returns the compiled pages def compile_pages(pages) # Create page objects from given pages given_pages = [] pages.each { |page| given_pages << Page.new(page) } # Compile pages Page.compile(given_pages) given_pages end # Layouts the page def layouted_page(a_page, a_pages) # Find layout layout = layout_for_page(a_page) # Build params assigns = { :page => a_page.merge(:_content_filename => nil), :pages => a_pages } params = { :assigns => assigns } params[:haml_options] = (a_page[:haml_options] || {}).symbolize_keys # Layout case layout[:type] when :eruby content = layout[:content].eruby(params) when :haml content = layout[:content].haml(params) when :markaby content = layout[:content].markaby(params) when :liquid content = layout[:content].liquid(params) else content = nil end content end end end