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| # Prepare layout content content = nil context = { :page => page.merge(:_content_filename => nil), :pages => pages } layout = layout_for_page(page) begin 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 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 # Write page with layout FileManager.create_file(path_for_page(page)) { content } 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) a_pages.inject([]) do |pages, page| # Read page content = File.read(page[:_content_filename]) # Filter page begin content = content.filter(page[:filters], :assigns => { :page => Page.new(page), :pages => 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 # Create compiled page compiled_page = page.merge( :content => content ) # Remember page pages + [ Page.new(compiled_page) ] end end end end