Refactoring of chunks and rendering [Denis Mertz]

This commit is contained in:
Alexey Verkhovsky 2005-03-27 18:13:43 +00:00
parent a87ef98aef
commit 78bad46419
19 changed files with 365 additions and 180 deletions

View file

@ -13,22 +13,19 @@ class Category < Chunk::Abstract
attr_reader :hidden, :list
def initialize(match_data)
super(match_data)
@hidden = match_data[1]
def initialize(match_data, content)
super(match_data, content)
@hidden = match_data[1]
@list = match_data[2].split(',').map { |c| c.strip }
@unmask_text = ''
if @hidden
@unmask_text = ''
else
category_urls = @list.map { |category| url(category) }.join(', ')
@unmask_text = '<div class="property"> category: ' + category_urls + '</div>'
end
end
# If the chunk is hidden, erase the mask and return this chunk
# otherwise, surround it with a 'div' block.
def unmask(content)
return '' if hidden
category_urls = @list.map{|category| url(category) }.join(', ')
replacement = '<div class="property"> category: ' + category_urls + '</div>'
self if content.sub!(mask(content), replacement)
end
# TODO move presentation of page metadata to controller/view
def url(category)
%{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}

View file

@ -6,35 +6,74 @@ require 'uri/common'
# +pattern+ that states what sort of text it matches.
# Chunks are initalized by passing in the result of a
# match by its pattern.
module Chunk
class Abstract
attr_reader :text
def initialize(match_data) @text = match_data[0] end
# automatically construct the array of derivatives of Chunk::Abstract
@derivatives = []
class << self
attr_reader :derivatives
end
def self::inherited( klass )
Abstract::derivatives << klass
end
# the class name part of the mask strings
def self.mask_string
self.to_s.delete(':').downcase
end
# a regexp that matches all chunk_types masks
def Abstract::mask_re(chunk_types)
tmp = chunk_types.map{|klass| klass.mask_string}.join("|")
Regexp.new("chunk(\\d+)(#{tmp})chunk")
end
attr_reader :text, :unmask_text, :unmask_mode
def initialize(match_data, content)
@text = match_data[0]
@content = content
@unmask_mode = :normal
end
# Find all the chunks of the given type in content
# Each time the pattern is matched, create a new
# chunk for it, and replace the occurance of the chunk
# in this content with its mask.
def self.apply_to(content)
content.gsub!( self.pattern ) do |match|
new_chunk = self.new($~)
content.chunks << new_chunk
new_chunk.mask(content)
new_chunk = self.new($~, content)
content.add_chunk(new_chunk)
new_chunk.mask
end
end
def mask(content)
"chunk#{self.object_id}#{self.class.to_s.delete(':').downcase}chunk"
def mask
"chunk#{self.object_id}#{self.class.mask_string}chunk"
end
def revert(content)
content.sub!( Regexp.new(mask(content)), text )
end
def unmask
@content.sub!(mask, @unmask_text)
end
def unmask(content)
self if revert(content)
end
def rendered?
@unmask_mode == :normal
end
def escaped?
@unmask_mode == :escape
end
def revert
@content.sub!(mask, @text)
# unregister
@content.delete_chunk(self)
end
end
end

View file

@ -13,38 +13,33 @@ module Engines
# Create a new chunk for the whole content and replace it with its mask.
def self.apply_to(content)
new_chunk = self.new(content)
content.chunks << new_chunk
content.replace(new_chunk.mask(content))
end
def unmask(content)
self
content.replace(new_chunk.mask)
end
private
# Never create engines by constructor - use apply_to instead
def initialize(text)
@text = text
def initialize(content)
@content = content
end
end
class Textile < AbstractEngine
def mask(content)
RedCloth.new(text,content.options[:engine_opts]).to_html
def mask
RedCloth.new(@content, @content.options[:engine_opts]).to_html
end
end
class Markdown < AbstractEngine
def mask(content)
RedCloth.new(text,content.options[:engine_opts]).to_html
def mask
RedCloth.new(@content, @content.options[:engine_opts]).to_html
end
end
class RDoc < AbstractEngine
def mask(content)
RDocSupport::RDocFormatter.new(text).to_html
def mask
RDocSupport::RDocFormatter.new(@content).to_html
end
end

View file

@ -2,29 +2,40 @@ require 'chunks/wiki'
# Includes the contents of another page for rendering.
# The include command looks like this: "[[!include PageName]]".
# It is a WikiLink since it refers to another page (PageName)
# It is a WikiReference since it refers to another page (PageName)
# and the wiki content using this command must be notified
# of changes to that page.
# If the included page could not be found, a warning is displayed.
class Include < WikiChunk::WikiLink
INCLUDE_PATTERN = /^\[\[!include(.*)\]\]\s*$/i
class Include < WikiChunk::WikiReference
INCLUDE_PATTERN = /\[\[!include(.*)\]\]\s*/i
def self.pattern() INCLUDE_PATTERN end
attr_reader :page_name
def initialize(match_data)
super(match_data)
def initialize(match_data, content)
super
@page_name = match_data[1].strip
@unmask_text = get_unmask_text_avoiding_recursion_loops
end
# This replaces the [[!include PageName]] text with
# the contents of PageName if it exists. Otherwise
# a warning is displayed.
def mask(content)
page = content.web.pages[page_name]
(page ? page.content : "<em>Could not include #{page_name}</em>")
private
def get_unmask_text_avoiding_recursion_loops
if refpage then
if refpage.wiki_includes.include?(@content.page_name)
# this will break the recursion
@content.delete_chunk(self)
refpage.clear_display_cache
return "<em>Recursive include detected; #{@page_name} --> #{@content.page_name} " +
"--> #{@page_name}</em>\n"
else
@content.merge_chunks(refpage.display_content)
return refpage.display_content.pre_rendered
end
else
return "<em>Could not include #{@page_name}</em>\n"
end
end
# Keep this chunk regardless of what happens.
def unmask(content) self end
end

View file

@ -5,15 +5,25 @@ require 'chunks/chunk'
# occuring within literal areas such as <code> and <pre> blocks
# and within HTML tags.
module Literal
class AbstractLiteral < Chunk::Abstract
def initialize(match_data, content)
super
@unmask_text = @text
end
end
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
class Pre < Chunk::Abstract
class Pre < AbstractLiteral
PRE_BLOCKS = "a|pre|code"
PRE_PATTERN = Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE)
def self.pattern() PRE_PATTERN end
end
# A literal chunk that protects HTML tags from wiki rendering.
class Tags < Chunk::Abstract
class Tags < AbstractLiteral
TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
TAGS_PATTERN = Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE)
def self.pattern() TAGS_PATTERN end

View file

@ -14,18 +14,15 @@ require 'chunks/chunk'
# Author: Mark Reid <mark at threewordslong dot com>
# Created: 8th June 2004
class NoWiki < Chunk::Abstract
NOWIKI_PATTERN = Regexp.new('<nowiki>(.*?)</nowiki>')
def self.pattern() NOWIKI_PATTERN end
attr_reader :plain_text
def initialize(match_data)
super(match_data)
@plain_text = match_data[1]
def initialize(match_data, content)
super
@plain_text = @unmask_text = match_data[1]
end
# The nowiki content is not unmasked. This means the chunk will be reverted
# using the plain text.
def unmask(content) nil end
def revert(content) content.sub!(mask(content), plain_text) end
end

View file

@ -86,23 +86,24 @@ class URIChunk < Chunk::Abstract
def self.apply_to(content)
content.gsub!( self.pattern ) do |matched_text|
chunk = self.new($~)
chunk = self.new($~, content)
if chunk.avoid_autolinking?
# do not substitute
# do not substitute nor register the chunk
matched_text
else
content.chunks << chunk
chunk.mask(content)
content.add_chunk(chunk)
chunk.mask
end
end
end
def initialize(match_data)
super(match_data)
def initialize(match_data, content)
super
@link_text = match_data[0]
@suspicious_preceding_character = match_data[1]
@original_scheme, @user, @host, @port, @path, @query, @fragment = match_data[2..-1]
treat_trailing_character
@unmask_text = "<a href=\"#{uri}\">#{link_text}</a>"
end
def avoid_autolinking?
@ -123,18 +124,6 @@ class URIChunk < Chunk::Abstract
end
end
# If the text should be escaped then don't keep this chunk.
# Otherwise only keep this chunk if it was substituted back into the
# content.
def unmask(content)
return nil if escaped_text
return self if content.sub!(mask(content), "<a href=\"#{uri}\">#{link_text}</a>")
end
# If there is no hostname in the URI, do not render it
# It's probably only contains the scheme, eg 'something:'
def escaped_text() ( host.nil? ? @uri : nil ) end
def scheme
@original_scheme or (@user ? 'mailto' : 'http')
end

View file

@ -7,62 +7,53 @@ require 'cgi'
module WikiChunk
include Chunk
# A wiki link is the top-level class for anything that refers to
# A wiki reference is the top-level class for anything that refers to
# another wiki page.
class WikiLink < Chunk::Abstract
class WikiReference < Chunk::Abstract
attr_reader :page_name, :link_text, :link_type
# Name of the referenced page
attr_reader :page_name
# the referenced page
def refpage
@content.web.pages[@page_name]
end
end
def initialize(*args)
# A wiki link is the top-level class for links that refers to
# another wiki page.
class WikiLink < WikiReference
attr_reader :link_text, :link_type
def initialize(match_data, content)
super
@link_type = :show
end
def self.apply_to(content)
content.gsub!( self.pattern ) do |matched_text|
chunk = self.new($~)
chunk = self.new($~, content)
if chunk.textile_url?
# do not substitute
matched_text
else
content.chunks << chunk
chunk.mask(content)
content.add_chunk(chunk)
chunk.mask
end
end
end
# the referenced page
def refpage
@content.web.pages[@page_name]
end
def textile_url?
not @textile_link_suffix.nil?
end
# By default, no escaped text
def escaped_text() nil end
# Replace link with a mask, but if the word is escaped, then don't replace it
def mask(content)
escaped_text || super(content)
end
def revert(content) content.sub!(mask(content), text) end
# Do not keep this chunk if it is escaped.
# Otherwise, pass the link procedure a page_name and link_text and
# get back a string of HTML to replace the mask with.
def unmask(content)
if escaped_text
return self
else
chunk_found = content.sub!(mask(content)) do |match|
content.page_link(page_name, link_text, link_type)
end
if chunk_found
return self
else
return nil
end
end
end
end
# This chunk matches a WikiWord. WikiWords can be escaped
@ -70,6 +61,9 @@ module WikiChunk
# method will return the WikiWord instead of the usual +nil+.
# The +page_name+ method returns the matched WikiWord.
class Word < WikiLink
attr_reader :escaped_text
unless defined? WIKI_WORD
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8")
end
@ -78,15 +72,19 @@ module WikiChunk
WIKI_WORD
end
def initialize(match_data)
super(match_data)
def initialize(match_data, content)
super
@textile_link_suffix, @escape, @page_name = match_data[1..3]
if @escape
@unmask_mode = :escape
@escaped_text = @page_name
else
@escaped_text = nil
end
@link_text = WikiWords.separate(@page_name)
@unmask_text = (@escaped_text || @content.page_link(@page_name, @link_text, @link_type))
end
def escaped_text
page_name unless @escape.nil?
end
def link_text() WikiWords.separate(page_name) end
end
# This chunk handles [[bracketted wiki words]] and
@ -108,12 +106,13 @@ module WikiChunk
def self.pattern() WIKI_LINK end
def initialize(match_data)
super(match_data)
def initialize(match_data, content)
super
@textile_link_suffix, @page_name = match_data[1..2]
@link_text = @page_name
separate_link_type
separate_alias
@unmask_text = @content.page_link(@page_name, @link_text, @link_type)
end
private