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, :extension => 'html', :filters => [], :is_draft => false, :layout => 'default', :order => 0 } 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 } # 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 $stderr.puts "Exception occured while layouting page " + "'#{page[:_content_filename]}' in layout '#{page[:layout]}':" unless $quiet $stderr.puts exception unless $quiet $stderr.puts exception.backtrace.join("\n") unless $quiet exit end end end private # Copies the contents of the assets directory into the output directory def copy_assets Dir['assets/*'].each { |f| FileUtils.remove_entry_secure(f.sub('assets/', 'output/'), true) } FileUtils.cp_r(Dir['assets/*'], 'output') if File.directory?('assets') and !Dir['assets/*'].empty? 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)) + '.*')].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] } # Sort pages by order and by path pages.sort! do |x,y| if x[:order].to_i == y[:order].to_i x[:path] <=> y[:path] else x[:order].to_i <=> y[:order].to_i end end 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] + 'index.' + 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(a_pages) # Create page objects from given pages given_pages = [] a_pages.each { |page| given_pages << Page.new(page) } # Create arrays for two kinds of pages compiled_pages = [] uncompiled_pages = [] given_pages.each { |page| uncompiled_pages << page } # Compile all pages given_pages.each do |page| # Read page content = File.read(page._content_filename) # Compile page begin content = content.filter(page.filters, :assigns => { :page => page, :pages => uncompiled_pages + compiled_pages }) rescue Exception => exception $stderr.puts "Exception occured while compiling page " + "'#{page[:_content_filename]}':" unless $quiet $stderr.puts exception unless $quiet $stderr.puts exception.backtrace.join("\n") unless $quiet exit end # Move from uncompiled to compiled uncompiled_pages -= [ page ] compiled_pages += [ page ] # Create compiled page page[:content] = content end # Return all pages compiled_pages end # Layouts the page def layouted_page(a_page, a_pages) context = { :page => a_page.merge(:_content_filename => nil), :pages => a_pages } layout = layout_for_page(a_page) content = nil case layout[:type] when :eruby content = layout[:content].eruby(context) when :haml content = layout[:content].haml(context) when :markaby content = layout[:content].markaby(context) when :liquid content = layout[:content].liquid(context) end content end end end