# $Id: builder.rb 92 2008-01-03 19:41:51Z tim_pease $ require 'find' require 'fileutils' require 'erb' module Webby # The Builder class performs the work of scanning the content folder, # creating Resource objects, and converting / copying the contents to the # output folder as needed. # class Builder class << self # call-seq: # Builder.run( :rebuild => false ) # # Create a new instance of the Builder class and invoke the run method. # If the :rebuild option is given as +true+, then all pages # will be recreated / copied. # def run( opts = {} ) self.new.run opts end # call-seq: # Builder.create( page, :from => template ) # # This mehod is used to create a new _page_ in the content folder based # on the specified template. _page_ is the relative path to the new page # from the content/ folder. The _template_ is the name of # the template to use from the templates/ folder. # def create( page, opts = {} ) tmpl = opts[:from] raise Error, "template not given" unless tmpl raise Error, "#{page} already exists" if test ?e, page Logging::Logger[self].info "creating #{page}" FileUtils.mkdir_p ::File.dirname(page) str = ERB.new(::File.read(tmpl), nil, '-').result ::File.open(page, 'w') {|fd| fd.write str} return nil end end # class << self # call-seq: # Builder.new # # Creates a new Builder object for creating pages from the content and # layout directories. # def initialize @log = Logging::Logger[self] end # call-seq: # run( :rebuild => false, :load_files => true ) # # Runs the Webby builder by loading in the layout files from the # layouts/ folder and the content from the # contents/ folder. Content is analyzed, and those that need # to be copied or compiled (filtered using ERB, Texttile, Markdown, etc.) # are handled. The results are placed in the output/ folder. # # If the :rebuild flag is set to +true+, then all content is # copied and/or compiled to the output folder. # # A content file can mark itself as dirty by setting the +dirty+ flag to # +true+ in the meta-data of the file. This will cause the contenet to # always be compiled when the builder is run. Conversely, setting the # dirty flag to +false+ will cause the content to never be compiled or # copied to the output folder. # # A content file needs to be built if the age of the file is less then the # age of the output product -- i.e. the content file has been modified # more recently than the output file. # def run( opts = {} ) opts[:load_files] = true unless opts.has_key?(:load_files) unless test(?d, output_dir) @log.info "creating #{output_dir}" FileUtils.mkdir output_dir end load_files if opts[:load_files] loop_check Resource.pages.each do |page| next unless page.dirty? or opts[:rebuild] @log.info "creating #{page.destination}" # make sure the directory exists FileUtils.mkdir_p ::File.dirname(page.destination) # copy the resource to the output directory if it is static if page.is_static? FileUtils.cp page.path, page.destination # otherwise, layout the resource and write the results to # the output directory else Renderer.write(page) end end # touch the cairn so we know when the website was last generated FileUtils.touch ::Webby.cairn nil end private # Scan the layouts/ folder and the content/ # folder and create a new Resource object for each file found there. # def load_files ::Find.find(layout_dir, content_dir) do |path| next unless test ?f, path next if path =~ ::Webby.exclude Resource.new path end end # Loop over all the layout resources looking for circular reference -- a # layout that eventually refers back to itself. These are bad. Raise an # error if one is detected. # def loop_check layouts = Resource.layouts layouts.each do |lyt| stack = [] while lyt if stack.include? lyt.filename stack << lyt.filename raise Error, "loop detected in layout references: #{stack.join(' > ')}" end stack << lyt.filename lyt = layouts.find :filename => lyt.layout end # while end # each end %w(output_dir layout_dir content_dir).each do |key| self.class_eval <<-CODE def #{key}( ) ::Webby.site.#{key} end CODE end end # class Builder end # module Webby # EOF