lib/asciidoctor/extensions.rb in asciidoctor-1.5.1 vs lib/asciidoctor/extensions.rb in asciidoctor-1.5.2
- old
+ new
@@ -1,5 +1,6 @@
+# encoding: UTF-8
module Asciidoctor
# Extensions provide a way to participate in the parsing and converting
# phases of the AsciiDoc processor or extend the AsciiDoc syntax.
#
# The various extensions participate in AsciiDoc processing as follows:
@@ -56,10 +57,12 @@
# Include the DSL class for this processor into this processor class or instance.
#
# This method automatically detects whether to use the include or extend keyword
# based on what is appropriate.
#
+ # NOTE Inspiration for this DSL design comes from https://corcoran.io/2013/09/04/simple-pattern-ruby-dsl/
+ #
# Returns nothing
def use_dsl
if self.name.nil_or_empty?
# NOTE contants(false) doesn't exist in Ruby 1.8.7
#include const_get :DSL if constants(false).grep :DSL
@@ -107,11 +110,11 @@
#--
# QUESTION is parse_content the right method name? should we wrap in open block automatically?
def parse_content parent, content, attributes = {}
reader = (content.is_a? Reader) ? reader : (Reader.new content)
while reader.has_more_lines?
- block = Parser.next_block(reader, parent, attributes)
+ block = Parser.next_block reader, parent, attributes
parent << block if block
end
nil
end
@@ -240,10 +243,40 @@
true
end
end
IncludeProcessor::DSL = ProcessorDsl
+ # Public: DocinfoProcessors are used to add additional content to
+ # the header and/or footer of the generated document.
+ #
+ # The placement of docinfo content is controlled by the converter.
+ #
+ # DocinfoProcessors implementations must extend DocinfoProcessor.
+ # If a location is not specified, the DocinfoProcessor is assumed
+ # to add content to the header.
+ class DocinfoProcessor < Processor
+ attr_accessor :location
+
+ def initialize config = {}
+ super config
+ @config[:location] ||= :header
+ end
+
+ def process document
+ raise ::NotImplementedError
+ end
+ end
+
+ module DocinfoProcessorDsl
+ include ProcessorDsl
+
+ def at_location value
+ option :location, value
+ end
+ end
+ DocinfoProcessor::DSL = DocinfoProcessorDsl
+
# Public: BlockProcessors are used to handle delimited blocks and paragraphs
# that have a custom name.
#
# When Asciidoctor encounters a delimited block or paragraph with an
# unrecognized name while parsing the document, it looks for a BlockProcessor
@@ -446,11 +479,11 @@
class ProcessorExtension < Extension
attr :process_method
def initialize kind, instance, process_method = nil
super kind, instance, instance.config
- @process_method = process_method || instance.method(:process)
+ @process_method = process_method || (instance.method :process)
end
end
# Public: A Group is used to register one or more extensions with the Registry.
#
@@ -482,11 +515,11 @@
# Public: Returns the Array of {Group} classes, instances and/or Procs that have been registered.
attr_reader :groups
def initialize groups = {}
@groups = groups
- @preprocessor_extensions = @treeprocessor_extensions = @postprocessor_extensions = @include_processor_extensions = nil
+ @preprocessor_extensions = @treeprocessor_extensions = @postprocessor_extensions = @include_processor_extensions = @docinfo_processor_extensions =nil
@block_extensions = @block_macro_extensions = @inline_macro_extensions = nil
@document = nil
end
# Public: Activates all the global extension {Group}s and the extension {Group}s
@@ -721,10 +754,83 @@
# Returns an [Array] of Extension proxy objects.
def include_processors
@include_processor_extensions
end
+ # Public: Registers an {DocinfoProcessor} with the extension registry to
+ # add additionnal docinfo to the document.
+ #
+ # The DocinfoProcessor may be one of four types:
+ #
+ # * A DocinfoProcessor subclass
+ # * An instance of a DocinfoProcessor subclass
+ # * The String name of a DocinfoProcessor subclass
+ # * A method block (i.e., Proc) that conforms to the DocinfoProcessor contract
+ #
+ # Unless the DocinfoProcessor is passed as the method block, it must be the
+ # first argument to this method.
+ #
+ # Examples
+ #
+ # # as an DocinfoProcessor subclass
+ # docinfo_processor MetaRobotsDocinfoProcessor
+ #
+ # # as an instance of a DocinfoProcessor subclass with an explicit location
+ # docinfo_processor JQueryDocinfoProcessor.new, :location => :footer
+ #
+ # # as a name of a DocinfoProcessor subclass
+ # docinfo_processor 'MetaRobotsDocinfoProcessor'
+ #
+ # # as a method block
+ # docinfo_processor do
+ # process |doc|
+ # at_location :footer
+ # 'footer content'
+ # end
+ # end
+ #
+ # Returns the [Extension] stored in the registry that proxies the
+ # instance of this DocinfoProcessor.
+ def docinfo_processor *args, &block
+ add_document_processor :docinfo_processor, args, &block
+ end
+
+ # Public: Checks whether any {DocinfoProcessor} extensions have been registered.
+ #
+ # location - A Symbol for selecting docinfo extensions at a given location (:header or :footer) (default: nil)
+ #
+ # Returns a [Boolean] indicating whether any DocinfoProcessor extensions are registered.
+ def docinfo_processors? location = nil
+ if @docinfo_processor_extensions
+ if location
+ @docinfo_processor_extensions.find {|ext| ext.config[:location] == location }
+ else
+ true
+ end
+ else
+ false
+ end
+ end
+
+ # Public: Retrieves the {Extension} proxy objects for all the
+ # DocinfoProcessor instances stored in this registry.
+ #
+ # location - A Symbol for selecting docinfo extensions at a given location (:header or :footer) (default: nil)
+ #
+ # Returns an [Array] of Extension proxy objects.
+ def docinfo_processors location = nil
+ if @docinfo_processor_extensions
+ if location
+ @docinfo_processor_extensions.select {|ext| ext.config[:location] == location }
+ else
+ @docinfo_processor_extensions
+ end
+ else
+ nil
+ end
+ end
+
# Public: Registers a {BlockProcessor} with the extension registry to
# process the block content (i.e., delimited block or paragraph) in the
# AsciiDoc source annotated with the specified block name (i.e., style).
#
# The BlockProcessor may be one of four types:
@@ -1009,13 +1115,16 @@
# style 1: specified as block
extension = if block_given?
config = resolve_args args, 1
# TODO if block arity is 0, assume block is process method
processor = kind_class.new config
- class << processor
- include_dsl
- end
+ # NOTE class << processor idiom doesn't work in Opal
+ #class << processor
+ # include_dsl
+ #end
+ # NOTE kind_class.contants(false) doesn't exist in Ruby 1.8.7
+ processor.extend kind_class.const_get :DSL if kind_class.constants.grep :DSL
processor.instance_exec(&block)
processor.freeze
unless processor.process_block_given?
raise ::ArgumentError.new %(No block specified to process #{kind_name} extension at #{block.source_location})
end
@@ -1056,12 +1165,15 @@
kind_store = instance_variable_get(%(@#{kind}_extensions).to_sym) || instance_variable_set(%(@#{kind}_extensions).to_sym, {})
# style 1: specified as block
if block_given?
name, config = resolve_args args, 2
processor = kind_class.new as_symbol(name), config
- class << processor
- include_dsl
- end
+ # NOTE class << processor idiom doesn't work in Opal
+ #class << processor
+ # include_dsl
+ #end
+ # NOTE kind_class.contants(false) doesn't exist in Ruby 1.8.7
+ processor.extend kind_class.const_get :DSL if kind_class.constants.grep :DSL
if block.arity == 1
yield processor
else
processor.instance_exec(&block)
end