docs: add documentation for various classes in app/logical.
This commit is contained in:
@@ -1,20 +1,38 @@
|
||||
require 'cgi'
|
||||
require 'uri'
|
||||
|
||||
# The DText class handles Danbooru's markup language, DText. Parsing DText is
|
||||
# handled by the DTextRagel class in the dtext_rb gem.
|
||||
#
|
||||
# @see https://github.com/evazion/dtext_rb
|
||||
# @see https://danbooru.donmai.us/wiki_pages/help:dtext
|
||||
class DText
|
||||
MENTION_REGEXP = /(?<=^| )@\S+/
|
||||
|
||||
def self.format_text(text, data: nil, **options)
|
||||
# Convert a string of DText to HTML.
|
||||
# @param text [String] The DText input
|
||||
# @param inline [Boolean] if true, allow only inline constructs. Ignore
|
||||
# block-level constructs, such as paragraphs, quotes, lists, and tables.
|
||||
# @param disable_mentions [Boolean] if true, don't generate @mentions.
|
||||
# @param base_url [String, nil] if present, convert relative URLs to absolute URLs.
|
||||
# @param data cached wiki/tag/artist data generated by {#preprocess}.
|
||||
# @return [String, nil] The HTML output
|
||||
def self.format_text(text, data: nil, inline: false, disable_mentions: false, base_url: nil)
|
||||
return nil if text.nil?
|
||||
data = preprocess([text]) if data.nil?
|
||||
text = parse_embedded_tag_request(text)
|
||||
html = DTextRagel.parse(text, **options)
|
||||
html = DTextRagel.parse(text, inline: inline, disable_mentions: disable_mentions, base_url: base_url)
|
||||
html = postprocess(html, *data)
|
||||
html
|
||||
rescue DTextRagel::Error
|
||||
""
|
||||
end
|
||||
|
||||
# Preprocess a set of DText messages and collect all tag, artist, and wiki
|
||||
# page references. Called before rendering a collection of DText messages
|
||||
# (e.g. comments or forum posts) to do all database lookups in one batch.
|
||||
# @param [Array<String>] a list of DText strings
|
||||
# @return an array of wiki pages, tags, and artists
|
||||
def self.preprocess(dtext_messages)
|
||||
dtext_messages = dtext_messages.map { |message| parse_embedded_tag_request(message) }
|
||||
names = dtext_messages.map { |message| parse_wiki_titles(message) }.flatten.uniq
|
||||
@@ -25,6 +43,11 @@ class DText
|
||||
[wiki_pages, tags, artists]
|
||||
end
|
||||
|
||||
# Rewrite the HTML produced by {#format_text} to colorize wiki links.
|
||||
# @param wiki_pages [Array<WikiPage>]
|
||||
# @param tags [Array<Tag>]
|
||||
# @param artists [Array<Artist>]
|
||||
# @return [String] the HTML output
|
||||
def self.postprocess(html, wiki_pages, tags, artists)
|
||||
fragment = Nokogiri::HTML.fragment(html)
|
||||
|
||||
@@ -69,11 +92,18 @@ class DText
|
||||
fragment.to_s
|
||||
end
|
||||
|
||||
# Wrap a DText message in a [quote] block.
|
||||
# @param message [String] the DText to quote
|
||||
# @param creator_name [String] the name of the user to quote.
|
||||
# @return [String] the quoted DText
|
||||
def self.quote(message, creator_name)
|
||||
stripped_body = DText.strip_blocks(message, "quote")
|
||||
"[quote]\n#{creator_name} said:\n\n#{stripped_body}\n[/quote]\n\n"
|
||||
end
|
||||
|
||||
# Convert `[bur:<id>]`, `[ta:<id>]`, `[ti:<id>]` tags to DText.
|
||||
# @param text [String] the DText input
|
||||
# @return [String] the DText output
|
||||
def self.parse_embedded_tag_request(text)
|
||||
text = parse_embedded_tag_request_type(text, TagAlias, /\[ta:(?<id>\d+)\]/m)
|
||||
text = parse_embedded_tag_request_type(text, TagImplication, /\[ti:(?<id>\d+)\]/m)
|
||||
@@ -81,6 +111,11 @@ class DText
|
||||
text
|
||||
end
|
||||
|
||||
# Convert a `[bur:<id>]`, `[ta:<id>]`, or `[ti:<id>]` tag to DText.
|
||||
# @param text [String] the DText input
|
||||
# @param tag_request [BulkUpdateRequest, TagAlias, TagImplication]
|
||||
# @param pattern [Regexp]
|
||||
# @return [String] the DText output
|
||||
def self.parse_embedded_tag_request_type(text, tag_request, pattern)
|
||||
text.gsub(pattern) do |match|
|
||||
obj = tag_request.find_by_id($~[:id])
|
||||
@@ -88,6 +123,9 @@ class DText
|
||||
end
|
||||
end
|
||||
|
||||
# Convert a `[bur:<id>]`, `[ta:<id>]`, or `[ti:<id>]` tag to DText.
|
||||
# @param obj [BulkUpdateRequest, TagAlias, TagImplication] the object to convert
|
||||
# @return [String] the DText output
|
||||
def self.tag_request_message(obj)
|
||||
if obj.is_a?(TagRelationship)
|
||||
if obj.is_active?
|
||||
@@ -118,6 +156,9 @@ class DText
|
||||
end
|
||||
end
|
||||
|
||||
# Return a list of user names mentioned in a string of DText. Ignore mentions in [quote] blocks.
|
||||
# @param text [String] the string of DText
|
||||
# @return [Array<String>] the list of user names
|
||||
def self.parse_mentions(text)
|
||||
text = strip_blocks(text.to_s, "quote")
|
||||
|
||||
@@ -128,6 +169,9 @@ class DText
|
||||
names.uniq
|
||||
end
|
||||
|
||||
# Return a list of wiki pages mentioned in a string of DText.
|
||||
# @param text [String] the string of DText
|
||||
# @return [Array<String>] the list of wiki page names
|
||||
def self.parse_wiki_titles(text)
|
||||
html = DTextRagel.parse(text)
|
||||
fragment = Nokogiri::HTML.fragment(html)
|
||||
@@ -142,6 +186,9 @@ class DText
|
||||
titles.uniq
|
||||
end
|
||||
|
||||
# Return a list of external links mentioned in a string of DText.
|
||||
# @param text [String] the string of DText
|
||||
# @return [Array<String>] the list of external URLs
|
||||
def self.parse_external_links(text)
|
||||
html = DTextRagel.parse(text)
|
||||
fragment = Nokogiri::HTML.fragment(html)
|
||||
@@ -150,6 +197,10 @@ class DText
|
||||
links.uniq
|
||||
end
|
||||
|
||||
# Return whether the two strings of DText have the same set of links.
|
||||
# @param a [String] a string of DText
|
||||
# @param b [String] a string of DText
|
||||
# @return [Boolean]
|
||||
def self.dtext_links_differ?(a, b)
|
||||
Set.new(parse_wiki_titles(a)) != Set.new(parse_wiki_titles(b)) ||
|
||||
Set.new(parse_external_links(a)) != Set.new(parse_external_links(b))
|
||||
@@ -159,6 +210,10 @@ class DText
|
||||
# the capitalization of the old tag when rewriting it to the new tag, but if
|
||||
# we can't determine how the new tag should be capitalized based on some
|
||||
# simple heuristics, then we skip rewriting the tag.
|
||||
# @param dtext [String] the DText input
|
||||
# @param old_name [String] the old wiki name
|
||||
# @param new_name [String] the new wiki name
|
||||
# @return [String] the DText output
|
||||
def self.rewrite_wiki_links(dtext, old_name, new_name)
|
||||
old_name = old_name.downcase.squeeze("_").tr("_", " ").strip
|
||||
new_name = new_name.downcase.squeeze("_").tr("_", " ").strip
|
||||
@@ -201,6 +256,10 @@ class DText
|
||||
end
|
||||
end
|
||||
|
||||
# Remove all [<tag>] blocks from the DText.
|
||||
# @param string [String] the DText input
|
||||
# @param tag [String] the type of block to remove
|
||||
# @return [String] the DText output
|
||||
def self.strip_blocks(string, tag)
|
||||
n = 0
|
||||
stripped = ""
|
||||
@@ -229,12 +288,18 @@ class DText
|
||||
stripped.strip
|
||||
end
|
||||
|
||||
# Remove all DText formatting from a string of DText, converting it to plain text.
|
||||
# @param dtext [String] the DText input
|
||||
# @return [String] the plain text output
|
||||
def self.strip_dtext(dtext)
|
||||
html = DTextRagel.parse(dtext)
|
||||
text = to_plaintext(html)
|
||||
text
|
||||
end
|
||||
|
||||
# Remove all formatting from a string of HTML, converting it to plain text.
|
||||
# @param html [String] the HTML input
|
||||
# @return [String] the plain text output
|
||||
def self.to_plaintext(html)
|
||||
text = from_html(html) do |node|
|
||||
case node.name
|
||||
@@ -250,10 +315,16 @@ class DText
|
||||
text.gsub(/\A[[:space:]]+|[[:space:]]+\z/, "")
|
||||
end
|
||||
|
||||
# Convert DText formatting to Markdown.
|
||||
# @param dtext [String] the DText input
|
||||
# @return [String] the Markdown output
|
||||
def self.to_markdown(dtext)
|
||||
html_to_markdown(format_text(dtext))
|
||||
end
|
||||
|
||||
# Convert HTML to Markdown.
|
||||
# @param html [String] the HTML input
|
||||
# @return [String] the Markdown output
|
||||
def self.html_to_markdown(html)
|
||||
html = Nokogiri::HTML.fragment(html)
|
||||
|
||||
@@ -273,6 +344,10 @@ class DText
|
||||
end.join
|
||||
end
|
||||
|
||||
# Convert HTML to DText.
|
||||
# @param html [String] the HTML input
|
||||
# @param inline [Boolean] if true, convert <img> tags to plaintext
|
||||
# @return [String] the DText output
|
||||
def self.from_html(text, inline: false, &block)
|
||||
html = Nokogiri::HTML.fragment(text)
|
||||
|
||||
@@ -336,13 +411,19 @@ class DText
|
||||
dtext
|
||||
end
|
||||
|
||||
# extract the first paragraph `needle` occurs in.
|
||||
# Return the first paragraph the search string `needle` occurs in.
|
||||
# @param needle [String] the string to search for
|
||||
# @param dtext [String] the DText input
|
||||
# @return [String] the first paragraph mentioning the search string
|
||||
def self.extract_mention(dtext, needle)
|
||||
dtext = dtext.gsub(/\r\n|\r|\n/, "\n")
|
||||
excerpt = ActionController::Base.helpers.excerpt(dtext, needle, separator: "\n\n", radius: 1, omission: "")
|
||||
excerpt
|
||||
end
|
||||
|
||||
# Generate a short plain text excerpt from a DText string.
|
||||
# @param length [Integer] the max length of the output
|
||||
# @return [String] a plain text string
|
||||
def self.excerpt(text, length: 160)
|
||||
strip_dtext(text).split(/\r\n|\r|\n/).first.to_s.truncate(length)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user