lib/dokkit/resource/document.rb in dokkit-0.4.4 vs lib/dokkit/resource/document.rb in dokkit-0.5.0
- old
+ new
@@ -4,10 +4,12 @@
# See 'dokkit.rb' or +LICENSE+ for licence information.
#
# (C) 2006, 2007, 2008 Andrea Fazzi <andrea.fazzi@alca.le.it> (and contributors).
#
+require 'rubygems'
+
require 'yaml'
require 'erb'
require 'dokkit/hash'
require 'dokkit/resource/extensions/builtin'
require 'dokkit/resource/filenamehelper'
@@ -16,201 +18,191 @@
module Resource
# Document is the core resource class of dokkit. Document
# instances are usually created on demand by
# Dokkit::Resource::Factory.
class Document
- include FilenameHelper
+ include FilenameHelper, Extension::Builtin
+
+ attr_accessor :logger, :cache, :resource_factory, :filter_factory
- # Includes the builtin extensions to be used by Document objects.
- include Extension::Builtin
+ attr_reader :source_fn, :env_configuration, :configuration
+ attr_reader :configs, :targets, :layouts, :deps
+ attr_reader :formatter, :format
- attr_reader :source_fn
- attr_reader :basename, :name_noext, :dirname, :relativename
- attr_reader :config_fns, :layout_fns, :target_fns
- attr_reader :targets, :layouts, :deps
- attr_reader :default_config_ext, :default_format, :default_formatter
- attr_accessor :configuration, :default_configuration_value
-
# Initialize a Document instance.
# +source_fn+:: is the file name of the document source file.
# +configuration+:: is the configuration hash to be associated with the document.
# +logger+:: is the logger instance.
# +cache+:: is the cache manager instance.
- def initialize(source_fn, configuration, logger, cache, resource_factory, filter_factory, &blk)
- @source_fn, @logger, @cache, @resource_factory, @filter_factory = source_fn, logger, cache, resource_factory, filter_factory
- @config_fns = []
- @targets = { }
- @layouts = { }
- @deps = { }
+ def initialize(source_fn, env_configuration, &blk)
+ @source_fn = source_fn
+ @env_configuration = env_configuration
- # Get basename stripping out path and extension from +source_fn+
- @basename = File.basename(source_fn, File.extname(source_fn))
+ reconfigure(&blk)
+ end
+
+ def config(config_fn)
+ reconfigure('config' => config_fn)
+ end
+
+ def layout(layout_fn)
+ reconfigure('layout' => layout_fn)
+ end
+
+ def reconfigure(configuration = { }, &blk)
+ init_default(configuration)
- # Get the directory part of source_fn
- @dirname = File.dirname(source_fn)
-
- # Set the document name with path but without extension
- @name_noext = File.join(@dirname, @basename)
-
- # Get name relative to configuration.document_dir
- @relativename = filename_helper(@name_noext, configuration[:document_dir], '')
-
- # Set default configuration file extension
- @default_config_ext = '.yaml'
- # Set default output format
- @default_format = 'html'
- # Set default input formatter format
- @default_formatter = 'deplate'
-
- @default_configuration_value = 'not defined'
-
- # yield self to an optional configuration block
yield self if block_given?
- # Setup hashes
- setup_header_configuration
- @configuration = configuration.recursive_merge('layout' => @relativename)
- setup_targets(@default_format)
-
- # Configure the document
configure
-
- @formatter = @configuration['formatter'] || @default_formatter
-
- collect_all
end
- def method_missing(meth)
- if @configuration.has_key?(meth.to_s)
- @configuration[meth.to_s]
- else
- @logger.warn("Configuration key '#{meth.to_s}' is not defined for #{source_fn}.")
- @default_configuration_value
- end
- end
-
def get_binding
binding
end
- # Read the configuration file +fn+ and return:
- # * the resulting configuration hash
- # * an array containing the names of the read config files
- # If +fn+ contains configuration data in the
- # header then this method extract it. Note that the method read
- # *recursively* the configuration file specified in config key
- # loading it from config_dir folder.
- # For example:
- #
- # ---
- # key: value
- # config: required
- #
- #
- # The resulting configuration hash will be the key/value pair
- # *plus* the configuration in +config_dir/required.yaml+.
- def add_config(fn)
- File.exists?(fn) ? merge_configuration_file(fn) : @logger.error("Configuration file '#{fn}' not found for '#{@source_fn}'!")
- end
-
- def add_layout(name, format = @default_format)
- if File.exists?(get_layout_filename(name, format))
- @layouts[format] << get_layout_filename(name, format)
- else
- @logger.warn("Layout file '#{get_layout_filename(name, format)}' does not exists for '#{source_fn}'!")
- end
- end
-
- def default_filter_chain_for(formatter, format)
- if @configuration[:default_filter_chain][formatter].has_key?(format)
- @configuration[:default_filter_chain][formatter][format]
- else
- @logger.error("Output format '#{format}' is not available for '#{formatter}' formatter.")
- end
- end
-
- def default_postfilter_chain_for(format)
- @configuration[:default_postfilter_chain][format]
- end
-
def has_format?(format)
@targets.has_key?(format)
end
# Return the filters chain associated with the given formatter and output format.
- def filters_for(formatter, format)
- if has_format?(format)
- process_filter_key('filter', format, default_filter_chain_for(formatter, format))
- else
- default_filter_chain_for(formatter, format)
- end
+ def filters_for(format, formatter = @formatter)
+ process_filter_key('filter', format) || default_filter_chain_for(formatter, format)
end
# Return the post filters chain associated with the given format.
def post_filters_for(format)
- if has_format?(format)
- process_filter_key('postfilter', format, default_postfilter_chain_for(format))
- else
- default_postfilter_chain_for(format)
- end
+ process_filter_key('postfilter', format) || default_postfilter_chain_for(format)
end
def target_for(format)
@targets[format][:target_fn]
end
def deps_for(format)
@deps[format]
end
- def current_format
+ def format
@current_format || @default_format
end
-
+
+ def render?
+ if @configuration.has_key?('render')
+ return false unless @configuration['render']
+ end
+ true
+ end
+
# Render the document in the specified format.
def render(args = { })
- @current_format = args[:format]
- args = { :format => @default_format }.merge args
- if args.has_key?(:document)
- document = @resource_factory.get(:document, args[:document])
- @cache.add_dependency(source_fn, args[:format], document.source_fn)
- document.render(:format => args[:format])
+ if args[:format].nil?
+ unless @current_format
+ args = { :format => @default_format }.merge args
+ @current_format = args[:format]
+ end
else
- do_render!(@formatter, args[:format])
+ @current_format = args[:format]
end
+ args.has_key?(:partial) ? render_partial(args[:partial], @current_format) : do_render!(@formatter, @current_format)
end
# Return the content of the source document file.
def source
@source ||= read_source
end
-
+
private
- def process_filter_key(key, format, default_filter_chain)
- @targets[format].has_key?(key) ? @targets[format][key] : default_filter_chain
+ def method_missing(meth)
+ if @configuration.has_key?(meth.to_s)
+ @configuration[meth.to_s]
+ else
+ @logger.warn("Configuration key '#{meth.to_s}' is not defined for #{source_fn}.")
+ @default_configuration_value
+ end
end
+ def init_default(configuration)
+ @configs = []
+ @targets = { }
+ @layouts = { }
+ @deps = { }
+
+ @default_config_ext = '.yaml'
+ @default_format = 'html'
+ @default_formatter = 'deplate'
+ @default_configuration_value = 'not defined'
+ @configuration = { 'layout' => relative_name }.merge configuration
+ @configuration['render'] = false if basename =~ /^_/
+ end
+
+ # Get basename stripping out path and extension from +source_fn+
+ def basename
+ File.basename(source_fn, File.extname(source_fn))
+ end
+
+ # Get the directory part of source_fn
+ def dirname
+ File.dirname(source_fn)
+ end
+
+ def name_noext
+ File.join(dirname, basename)
+ end
+
+ # Get name relative to configuration.document_dir
+ def relative_name
+ filename_helper(name_noext, @env_configuration[:document_dir], '')
+ end
+
+ def add_config(fn)
+ File.exists?(fn) ? merge_configuration_file(fn) : @logger.error("Configuration file '#{fn}' not found for '#{@source_fn}'!")
+ end
+
+ def add_layout(name, format = @default_format)
+ if File.exists?(get_layout_filename(name, format))
+ @layouts[format] << get_layout_filename(name, format)
+ else
+ @logger.debug("Layout file '#{get_layout_filename(name, format)}' does not exist for '#{source_fn}'.")
+ end
+ end
+
+ def default_filter_chain_for(formatter, format)
+ @env_configuration[:default_filter_chain][formatter][format]
+ end
+
+ def default_postfilter_chain_for(format)
+ @env_configuration[:default_postfilter_chain][format]
+ end
+
+ # @logger.error("Output format '#{format}' is not available for '#{formatter}' formatter.")
+ # @logger.error("Formatter '#{formatter}' is not available.")
+
+ def process_filter_key(key, format)
+ @targets.has_key?(format) ? @targets[format][key] : nil
+ end
+
# Read the content of the source document file from disk.
def read_source
File.read(source_fn)
end
def process_config_configuration_key
@configuration['config'].to_a.each { |fn| load_config_file(fn) }
end
def load_config_file(fn)
- config_fn = File.join(@configuration[:config_dir], fn)
+ config_fn = File.join(@env_configuration[:config_dir], fn)
config_fn += @default_config_ext if File.extname(config_fn).empty?
@configuration.delete('config')
add_config(config_fn)
end
def merge_configuration_file(fn)
fn == @source_fn ? @configuration.recursive_merge!(@header_configuration) : @configuration.recursive_merge!(YAML::load_file(fn))
- @config_fns << fn
+ @configs << fn
process_config_configuration_key if @configuration.has_key?('config')
end
def setup_header_configuration
@header_configuration = extract_configuration_from_source!
@@ -221,28 +213,31 @@
@targets[format] = { :target_fn => target_fn(format) }
end
# Render document in the given format.
def do_render!(formatter, format)
- if @targets[format]
- render_source!(formatter, format)
- render_all_layouts!(format)
- else
- @logger.error("Don't know how to render '#{source_fn}': format '#{format}' is unknown.")
- end
+ render_source!(formatter, format)
+ render_all_layouts!(format)
@content_for_layout
end
# Produce output from source.
def render_source!(formatter, format)
- @content_for_layout = apply_filters(@source, filters_for(formatter, format), format) unless @source.nil?
+ @content_for_layout = apply_filters(@source, filters_for(format, formatter), format) unless @source.nil?
end
def render_layout!(layout_fn, format)
@content_for_layout = apply_filters(File.read(layout_fn), post_filters_for(format), format) unless File.read(layout_fn).nil?
end
+ def render_partial(source_fn, format)
+ basename, dirname = '_' + File.basename(source_fn), File.dirname(source_fn)
+ partial_fn = File.join(@env_configuration[:document_dir], File.join(dirname, basename))
+ @cache.add_dependency(@source_fn, format, partial_fn)
+ @resource_factory.get(:document, partial_fn).render(:format => format)
+ end
+
# Injects rendered content from +source_fn+ in the layout chain.
def render_all_layouts!(format)
@layouts[format].each { |layout_fn| render_layout!(layout_fn, format) } unless !@layouts[format] || @layouts[format].empty?
end
@@ -258,14 +253,22 @@
# 1. configuration in header if present.
# 2. configuration in ./+basename+.yaml if file exists.
# 3. configuration in COMMON.yaml file (or files) exist.
# 4. configuration in doc/configs/+basename+.yaml if file exists.
def configure # :doc:
+ setup_header_configuration
+ setup_targets(@default_format)
+
add_config(config_fn_relative_to(:config_dir)) if File.exists?(config_fn_relative_to(:config_dir))
add_common_config
add_config(config_fn_relative_to(:document_dir)) if File.exists?(config_fn_relative_to(:document_dir))
add_config(@source_fn) if @header_configuration
+
+ @formatter = @configuration['formatter'] || @default_formatter
+
+ collect_all
+ @configuration
end
# Collect the layout files traversing +targets+ hash.
def collect_layouts
@targets.each_key do |format|
@@ -282,11 +285,11 @@
def process_layout_configuration_key(format)
@configuration['layout'].to_a.each { |name| add_layout(name, format) }
end
def get_layout_filename(name, format)
- File.join(@configuration[:layout_dir], name + ".#{format.to_s}")
+ File.join(@env_configuration[:layout_dir], name + ".#{format.to_s}")
end
# Process +format+ key in configuration hash.
def process_formats
@targets = { }
@@ -298,18 +301,14 @@
# Process option hash related to a particular target.
def process_format_configuration_key(format)
format_key = format.keys.first
opts = format.values.first
- if opts
- ext = (opts['ext'] if opts.has_key?('ext')) || format_key
- filters = (opts['filter'] if opts.has_key?('filter')) || default_filter_chain_for(@formatter, 'html')
- post_filters = (opts['postfilter'] if opts.has_key?('postfilter')) || default_postfilter_chain_for('html')
- { :target_fn => target_fn(ext.to_sym), 'filter' => filters, 'postfilter' => post_filters }
- else
- @logger.error("You must define format '#{format}'!")
- end
+ ext = (opts['ext'] if opts.has_key?('ext')) || format_key
+ filters = (opts['filter'] if opts.has_key?('filter')) || default_filter_chain_for(@formatter, 'html')
+ post_filters = (opts['postfilter'] if opts.has_key?('postfilter')) || default_postfilter_chain_for('html')
+ { :target_fn => target_fn(ext.to_sym), 'filter' => filters, 'postfilter' => post_filters }
end
# Iterates through configuration +:targets+ key (if
# present) and setup target options.
def collect_formats
@@ -319,11 +318,11 @@
# Collect all the dependency.
def collect_deps
@targets.each do |format, target|
@deps[format] = []
@deps[format] << @source_fn # the essential dependency from source file
- @deps[format].concat @config_fns # dependency from configuration files
+ @deps[format].concat @configs # dependency from configuration files
@deps[format].concat @layouts[format] if @layouts.has_key?(format) # dependency from layout files
@deps[format].concat @cache.deps[source_fn][format] if @cache.deps[source_fn] and @cache.deps[source_fn][format]
@deps[format].uniq! # remove duplicates
end
@deps
@@ -338,11 +337,11 @@
header_configuration = YAML::load(header_configuration)
end
# Configure from commons configuration files.
def add_common_config
- resolve_common_configs(@dirname).reverse.each do |file|
+ resolve_common_configs(dirname).reverse.each do |file|
add_config(file)
end
end
# Collect common configuration files COMMON.yaml.
@@ -360,22 +359,22 @@
#
# source_fn = 'doc/pages/subdir/document.ext'
# config_fn #=> 'doc/configs/subdir/document.yaml'
#
def config_fn_relative_to(dir_config_key)
- filename_helper(@name_noext, @configuration[:document_dir], @configuration[dir_config_key], @default_config_ext)
+ filename_helper(name_noext, @env_configuration[:document_dir], @env_configuration[dir_config_key], @default_config_ext)
end
# Return target filename for a given format.
#
# Example:
#
# source_fn = 'doc/pages/document.ext'
- # format = :html
+ # format = 'html'
# target_fn(format) #=> 'output/document.html'
#
def target_fn(format)
- filename_helper(@name_noext, @configuration[:document_dir], @configuration[:output_dir], ".#{format.to_s}")
+ filename_helper(name_noext, @env_configuration[:document_dir], @env_configuration[:output_dir], ".#{format.to_s}")
end
# Apply filters on text to produce the given format.
def apply_filters(text, filters_chain, format)
filters_chain.collect { |filter| @filter_factory.get(filter, self) }.inject(text) { |s, f| f.filter(s) }