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