mirror of
https://github.com/foo-dogsquared/asciidoctor-foodogsquared-extensions.git
synced 2025-01-30 22:57:56 +00:00
Restructure extensions with Ruby modules
Also restructured how they're named in the filesystem and the class names as well.
This commit is contained in:
parent
69c8015292
commit
de9f3a0e9c
@ -1,3 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'asciidoctor/foodogsquared-extensions'
|
||||
require 'asciidoctor/foodogsquared/extensions'
|
||||
|
@ -1,61 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ChatBlock < Asciidoctor::Extensions::BlockProcessor
|
||||
use_dsl
|
||||
|
||||
named :chat
|
||||
on_context :example
|
||||
name_positional_attributes 'avatar', 'state'
|
||||
default_attributes 'state' => 'default'
|
||||
|
||||
def process(parent, reader, attrs)
|
||||
block = create_block parent, :pass, nil, attrs, content_model: :compound
|
||||
block.add_role('dialogblock')
|
||||
|
||||
# You can think of this section as a pipeline constructing the HTML
|
||||
# component for this block. Specifically, we're building one component that
|
||||
# contains two output: the dialog image of our avatar and its content.
|
||||
attrs['name'] ||= attrs['avatar']
|
||||
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
<div role="figure" class="dialogblock dialogblock__box dialogblock__avatar--#{attrs['avatar']} #{attrs['role']}">
|
||||
<div class="dialogblock dialogblock__avatar">
|
||||
HTML
|
||||
|
||||
)
|
||||
|
||||
attrs['avatarsdir'] ||= File.expand_path('./avatars', attrs['iconsdir'])
|
||||
attrs['avatarstype'] ||= parent.attributes['avatarstype'] || 'avif'
|
||||
|
||||
avatar_sticker = "#{attrs['avatar'].to_kebab}/#{attrs['state'].to_kebab}.#{attrs['avatarstype']}"
|
||||
avatar_img_attrs = {
|
||||
'target' => parent.image_uri(avatar_sticker, 'avatarsdir'),
|
||||
'alt' => attrs['name']
|
||||
}
|
||||
avatar_imgblock = create_image_block block, avatar_img_attrs
|
||||
|
||||
block << avatar_imgblock
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
</div>
|
||||
<div class="dialogblock dialogblock__text">
|
||||
<small class="dialogblock dialogblock__avatar-name">#{attrs['name']}</small>
|
||||
HTML
|
||||
)
|
||||
|
||||
parse_content block, reader
|
||||
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
)
|
||||
|
||||
block
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_html_fragment(parent, html, attributes = nil)
|
||||
create_block parent, :pass, html, attributes
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open-uri'
|
||||
require 'yaml'
|
||||
|
||||
class FDroidLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :fdroid
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'lang' => 'en'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
app_id = target
|
||||
app_metadata_uri = %(https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/#{app_id}.yml)
|
||||
|
||||
metadata = OpenURI.open_uri(app_metadata_uri) { |f| YAML.safe_load(f.read) }
|
||||
attrs['caption'] ||= metadata['AutoName']
|
||||
|
||||
url = %(https://f-droid.org/#{attrs['lang']}/packages/#{app_id})
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
@ -1,33 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
|
||||
class FlathubLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :flathub
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
# FlatHub API seems to have no documentation aside from the source code.
|
||||
# You can easily infer the API with its source code at
|
||||
# https://github.com/flathub/website.
|
||||
app_id = target
|
||||
app_metadata_uri = %(https://flathub.org/api/v2/appstream/#{app_id})
|
||||
|
||||
headers = {
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => ::Asciidoctor::FoodogsquaredCustomExtensions::USER_AGENT
|
||||
}
|
||||
|
||||
metadata = OpenURI.open_uri(app_metadata_uri, headers) { |f| JSON.parse(f.read) }
|
||||
attrs['caption'] ||= metadata['name']
|
||||
|
||||
url = %(https://flathub.org/apps/#{app_id})
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
@ -1,55 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'asciidoctor'
|
||||
require 'asciidoctor/extensions'
|
||||
|
||||
require_relative 'helpers'
|
||||
|
||||
require_relative 'man-inline-macro/extension'
|
||||
require_relative 'swhid-inline-macro/extension'
|
||||
require_relative 'swhid-include-processor/extension'
|
||||
require_relative 'github-link-inline-macro/extension'
|
||||
require_relative 'github-raw-content-include-processor/extension'
|
||||
require_relative 'gitlab-link-inline-macro/extension'
|
||||
require_relative 'gitlab-raw-content-include-processor/extension'
|
||||
require_relative 'chat-block-processor/extension'
|
||||
require_relative 'git-blob-include-processor/extension'
|
||||
require_relative 'wikipedia-inline-macro/extension'
|
||||
require_relative 'package-indices-link-macro/extension'
|
||||
require_relative 'fdroid-link-inline-macro/extension'
|
||||
require_relative 'musicbrainz-link-inline-macro/extension'
|
||||
require_relative 'flathub-link-inline-macro/extension'
|
||||
require_relative 'repology-link-inline-macro/extension'
|
||||
require_relative 'ietf-rfc-link-inline-macro/extension'
|
||||
|
||||
Asciidoctor::Extensions.register do
|
||||
inline_macro ManInlineMacro
|
||||
inline_macro IETFRFCLinkInlineMacro
|
||||
block ChatBlock if @document.basebackend? 'html'
|
||||
|
||||
inline_macro SWHInlineMacro
|
||||
include_processor SWHIDIncludeProcessor
|
||||
|
||||
inline_macro GitHubLinkInlineMacro
|
||||
include_processor GitHubRawIncludeProcessor
|
||||
|
||||
inline_macro GitLabLinkInlineMacro
|
||||
include_processor GitLabRawIncludeProcessor
|
||||
|
||||
include_processor GitBlobIncludeProcessor
|
||||
|
||||
inline_macro WikipediaInlineMacro
|
||||
|
||||
# Package indices
|
||||
inline_macro CtanLinkInlineMacro
|
||||
inline_macro PypiLinkInlineMacro
|
||||
inline_macro CratesIOLinkInlineMacro
|
||||
|
||||
# App stores
|
||||
inline_macro FDroidLinkInlineMacro
|
||||
inline_macro FlathubLinkInlineMacro
|
||||
|
||||
# Databases
|
||||
inline_macro MusicBrainzLinkInlineMacro
|
||||
inline_macro RepologyLinkInlineMacro
|
||||
end
|
63
lib/asciidoctor/foodogsquared/chat-block/extension.rb
Normal file
63
lib/asciidoctor/foodogsquared/chat-block/extension.rb
Normal file
@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class ChatBlock < Asciidoctor::Extensions::BlockProcessor
|
||||
use_dsl
|
||||
|
||||
named :chat
|
||||
on_context :example
|
||||
name_positional_attributes 'avatar', 'state'
|
||||
default_attributes 'state' => 'default'
|
||||
|
||||
def process(parent, reader, attrs)
|
||||
# You can think of this section as a pipeline constructing the HTML
|
||||
# component for this block. Specifically, we're building one component that
|
||||
# contains two output: the dialog image of our avatar and its content.
|
||||
attrs['name'] ||= attrs['avatar']
|
||||
|
||||
block = create_block parent, :pass, nil, attrs, content_model: :compound
|
||||
block.add_role('dialogblock')
|
||||
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
<div role="figure" class="dialogblock dialogblock__box dialogblock__avatar--#{attrs['avatar']} #{attrs['role']}">
|
||||
<div class="dialogblock dialogblock__avatar">
|
||||
HTML
|
||||
|
||||
)
|
||||
|
||||
attrs['avatarsdir'] ||= File.expand_path('./avatars', attrs['iconsdir'])
|
||||
attrs['avatarstype'] ||= parent.attributes['avatarstype'] || 'avif'
|
||||
|
||||
avatar_sticker = "#{attrs['avatar'].to_kebab}/#{attrs['state'].to_kebab}.#{attrs['avatarstype']}"
|
||||
avatar_img_attrs = {
|
||||
'target' => parent.image_uri(avatar_sticker, 'avatarsdir'),
|
||||
'alt' => attrs['name']
|
||||
}
|
||||
avatar_imgblock = create_image_block block, avatar_img_attrs
|
||||
|
||||
block << avatar_imgblock
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
</div>
|
||||
<div class="dialogblock dialogblock__text">
|
||||
<small class="dialogblock dialogblock__avatar-name">#{attrs['name']}</small>
|
||||
HTML
|
||||
)
|
||||
|
||||
parse_content block, reader
|
||||
|
||||
block << (create_html_fragment block, <<~HTML
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
)
|
||||
|
||||
block
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_html_fragment(parent, html, attributes = nil)
|
||||
create_block parent, :pass, html, attributes
|
||||
end
|
||||
end
|
||||
end
|
57
lib/asciidoctor/foodogsquared/extensions.rb
Normal file
57
lib/asciidoctor/foodogsquared/extensions.rb
Normal file
@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'asciidoctor'
|
||||
require 'asciidoctor/extensions'
|
||||
|
||||
require_relative 'helpers'
|
||||
|
||||
require_relative 'man-inline-macro/extension'
|
||||
require_relative 'swhid-inline-macro/extension'
|
||||
require_relative 'swhid-include-processor/extension'
|
||||
require_relative 'github-inline-macro/extension'
|
||||
require_relative 'github-include-processor/extension'
|
||||
require_relative 'gitlab-inline-macro/extension'
|
||||
require_relative 'gitlab-include-processor/extension'
|
||||
require_relative 'chat-block/extension'
|
||||
require_relative 'git-blob-include-processor/extension'
|
||||
require_relative 'wikipedia-inline-macro/extension'
|
||||
require_relative 'package-indices-macro/extension'
|
||||
require_relative 'fdroid-inline-macro/extension'
|
||||
require_relative 'musicbrainz-inline-macro/extension'
|
||||
require_relative 'flathub-inline-macro/extension'
|
||||
require_relative 'repology-inline-macro/extension'
|
||||
require_relative 'ietf-rfc-inline-macro/extension'
|
||||
|
||||
include Asciidoctor::Foodogsquared::Extensions
|
||||
Asciidoctor::Extensions.register do
|
||||
|
||||
inline_macro ManInlineMacro
|
||||
inline_macro IETFRFCInlineMacro
|
||||
block ChatBlock if @document.basebackend? 'html'
|
||||
|
||||
inline_macro SWHInlineMacro
|
||||
include_processor SWHIncludeProcessor
|
||||
|
||||
inline_macro GitHubInlineMacro
|
||||
include_processor GitHubIncludeProcessor
|
||||
|
||||
inline_macro GitLabInlineMacro
|
||||
include_processor GitLabIncludeProcessor
|
||||
|
||||
include_processor GitBlobIncludeProcessor
|
||||
|
||||
inline_macro WikipediaInlineMacro
|
||||
|
||||
# Package indices
|
||||
inline_macro CtanInlineMacro
|
||||
inline_macro PypiInlineMacro
|
||||
inline_macro CratesIOInlineMacro
|
||||
|
||||
# App stores
|
||||
inline_macro FDroidInlineMacro
|
||||
inline_macro FlathubInlineMacro
|
||||
|
||||
# Databases
|
||||
inline_macro MusicBrainzInlineMacro
|
||||
inline_macro RepologyInlineMacro
|
||||
end
|
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open-uri'
|
||||
require 'yaml'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class FDroidInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :fdroid
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'lang' => 'en'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
app_id = target
|
||||
app_metadata_uri = %(https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/#{app_id}.yml)
|
||||
|
||||
metadata = OpenURI.open_uri(app_metadata_uri) { |f| YAML.safe_load(f.read) }
|
||||
attrs['caption'] ||= metadata['AutoName']
|
||||
|
||||
url = %(https://f-droid.org/#{attrs['lang']}/packages/#{app_id})
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class FlathubInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :flathub
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
# FlatHub API seems to have no documentation aside from the source code.
|
||||
# You can easily infer the API with its source code at
|
||||
# https://github.com/flathub/website.
|
||||
app_id = target
|
||||
app_metadata_uri = %(https://flathub.org/api/v2/appstream/#{app_id})
|
||||
|
||||
headers = {
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => ::Asciidoctor::Foodogsquared::USER_AGENT
|
||||
}
|
||||
|
||||
metadata = OpenURI.open_uri(app_metadata_uri, headers) { |f| JSON.parse(f.read) }
|
||||
attrs['caption'] ||= metadata['name']
|
||||
|
||||
url = %(https://flathub.org/apps/#{app_id})
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,68 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rugged'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class GitBlobIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'git:'
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
attrs['gitrepo'] ||= doc.attributes['gitrepo'] || doc.base_dir
|
||||
repo = Rugged::Repository.discover(attrs['gitrepo'])
|
||||
|
||||
git_object_ref = target.delete_prefix 'git:'
|
||||
git_object_ref = doc.attributes['doccontentref'] if git_object_ref.empty?
|
||||
|
||||
begin
|
||||
git_object = repo.rev_parse git_object_ref
|
||||
|
||||
if attrs.key? 'diff-option'
|
||||
options = {}
|
||||
|
||||
options[:paths] = attrs['path'].split(';') if attrs.key? 'path'
|
||||
options[:context_lines] = attrs['context-lines'] if attrs.key? 'context-lines'
|
||||
options[:reverse] = true if attrs.key? 'reverse-option'
|
||||
|
||||
if attrs.key? 'other'
|
||||
other = repo.rev_parse attrs['other'] || nil
|
||||
reader.push_include git_object.diff(other, **options).patch
|
||||
else
|
||||
reader.push_include git_object.diff(**options).patch
|
||||
end
|
||||
else
|
||||
inner_entry = case git_object.type
|
||||
when :blob
|
||||
git_object
|
||||
when :commit
|
||||
git_object.tree.path attrs['path']
|
||||
when :tree
|
||||
git_object.path attrs['path']
|
||||
when :tag
|
||||
git_object.target.tree.path attrs['path']
|
||||
end
|
||||
|
||||
content = repo.lookup(inner_entry[:oid]).content
|
||||
|
||||
if attrs.key? 'lines'
|
||||
content_lines = content.lines
|
||||
new_content = +''
|
||||
doc.resolve_lines_to_highlight(content, attrs['lines']).each do |line_no|
|
||||
new_content << content_lines.at(line_no - 1)
|
||||
end
|
||||
|
||||
content = new_content
|
||||
end
|
||||
|
||||
reader.push_include content
|
||||
end
|
||||
rescue StandardError => e
|
||||
reader.push_include "Unresolved directive for '#{target}' with the following error:\n#{e}"
|
||||
warn e
|
||||
end
|
||||
|
||||
reader
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class GitHubIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'github:'
|
||||
end
|
||||
|
||||
def warn_or_raise(doc, warning)
|
||||
if (doc.safe > Asciidoctor::SafeMode::SERVER) && !(doc.attr? 'allow-uri-read')
|
||||
raise warning
|
||||
else
|
||||
warn warning
|
||||
end
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
src = target.delete_prefix('github:').split('/', 3)
|
||||
owner = src.at 0
|
||||
repo = src.at 1
|
||||
namespaced_repo = "#{owner}/#{repo}"
|
||||
|
||||
path = attrs['path'] || ''
|
||||
|
||||
# For more information, see https://docs.github.com/en/rest/repos/contents.
|
||||
uri = URI.parse %(https://api.github.com/repos/#{owner}/#{repo}/contents/#{path})
|
||||
|
||||
if attrs['rev']
|
||||
query = { ref: attrs['rev'] }
|
||||
uri.query = URI.encode_www_form query
|
||||
end
|
||||
|
||||
begin
|
||||
headers = {
|
||||
'Header' => 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version' => '2022-11-28'
|
||||
}
|
||||
|
||||
headers['Authorization'] = "Token #{ENV['GITHUB_API_BEARER_TOKEN']}" if ENV['GITHUB_API_BEARER_TOKEN']
|
||||
|
||||
OpenURI.open_uri(uri, headers) do |f|
|
||||
response = JSON.parse(f.read)
|
||||
|
||||
# If the response is an array, it is likely to be a directory. In this
|
||||
# usecase, we'll just list them.
|
||||
content = if response.is_a? Array
|
||||
warning = %(given path '#{path}' from GitHub repo '#{repo}' is a directory)
|
||||
warn_or_raise doc, warning
|
||||
warning
|
||||
elsif response.is_a? Object
|
||||
Base64.decode64 response['content'] if response['content'] && response['encoding'] == 'base64'
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attrs
|
||||
end
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{path}' in GitHub repo '#{namespaced_repo}: #{e}')
|
||||
warn_or_raise doc, warning
|
||||
reader.push_include warning, target, target, 1, attrs
|
||||
end
|
||||
|
||||
reader
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class GitHubInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :github
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
default_caption = if attrs.key?('repo-option')
|
||||
target.split('/').at(1)
|
||||
else
|
||||
target
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
uri = URI.parse %(https://github.com/#{target})
|
||||
|
||||
if attrs.key? 'issue'
|
||||
uri.path += %(/issues/#{attrs['issue']})
|
||||
text << "##{attrs['issue']}" if text == target
|
||||
else
|
||||
uri.path += %(/tree/#{attrs['rev']}) if attrs.key? 'rev'
|
||||
uri.path += %(/#{attrs['path']}) if attrs.key? 'path'
|
||||
text << "@#{attrs['rev']}" if attrs.key?('rev') && text == target
|
||||
end
|
||||
|
||||
target = uri.to_s
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class GitLabIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'gitlab:'
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
src = target.delete_prefix('gitlab:').split('/', 2)
|
||||
owner = src.at 0
|
||||
repo = src.at 1
|
||||
namespaced_repo = "#{owner}/#{repo}"
|
||||
|
||||
raise %(there is no 'path' attribute given for GitLab repo '#{namespaced_repo}') unless attrs.key? 'path'
|
||||
raise %(no given ref for getting file in '#{namespaced_repo}') unless attrs.key? 'rev'
|
||||
|
||||
path = attrs['path']
|
||||
rev = attrs['rev']
|
||||
|
||||
domain = attrs['domain'] || 'gitlab.com'
|
||||
version = attrs['version'] || 'v4'
|
||||
|
||||
uri = URI.parse %(https://#{domain}/api/#{version})
|
||||
|
||||
# Set the project.
|
||||
uri += %(/projects/#{URI.encode_www_form_component namespaced_repo})
|
||||
|
||||
# Then the filename.
|
||||
uri += %(/repository/files/#{URI.encode_www_form_component path})
|
||||
|
||||
# Then the revision.
|
||||
query = { ref: rev }
|
||||
uri.query = URI.encode_www_form query
|
||||
|
||||
content = begin
|
||||
headers = { 'Content-Type' => 'application-json' }
|
||||
header['PRIVATE-TOKEN'] = ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN'] if ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN']
|
||||
|
||||
OpenURI.open_uri(uri, headers) do |f|
|
||||
response = JSON.parse(f.read)
|
||||
|
||||
Base64.decode64 response['content'] if response['content'] && response['encoding'] == 'base64'
|
||||
end
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{path}' in GitLab repo '#{repo}': #{e})
|
||||
warn_or_raise doc, warning
|
||||
warning
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attrs
|
||||
reader
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class GitLabInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :gitlab
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'domain' => 'gitlab.com'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
default_caption = if attrs.key?('repo-option')
|
||||
target.split('/').at(1)
|
||||
else
|
||||
target
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
uri = URI.parse %(https://#{attrs['domain']}/#{target})
|
||||
|
||||
if attrs.key? 'issue'
|
||||
uri.path += %(/-/issues/#{attrs['issue']})
|
||||
text << "##{attrs['issue']}" if text == target
|
||||
else
|
||||
uri.path += %(/-/tree/#{attrs['rev']}) if attrs.key? 'rev'
|
||||
uri.path += %(/#{attrs['path']}) if attrs.key? 'path'
|
||||
text << "@#{attrs['rev']}" if attrs.key?('rev') && text == target
|
||||
end
|
||||
|
||||
target = uri.to_s
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
||||
end
|
26
lib/asciidoctor/foodogsquared/helpers.rb
Normal file
26
lib/asciidoctor/foodogsquared/helpers.rb
Normal file
@ -0,0 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class String
|
||||
def to_kebab
|
||||
self.gsub(/\s+/, '-') # Replace all spaces with dashes.
|
||||
.gsub(/[^a-zA-Z0-9-]/, '') # Remove all non-alphanumerical (and dashes) characters.
|
||||
.gsub(/-+/, '-') # Reduce all dashes into only one.
|
||||
.gsub(/^-|-+$/, '') # Remove all leading and trailing dashes.
|
||||
.downcase
|
||||
end
|
||||
end
|
||||
|
||||
# The namespace for storing Asciidoctor. This is the entry point for the
|
||||
# entirety of this project.
|
||||
module Asciidoctor::Foodogsquared
|
||||
NAME = 'asciidoctor-foodogsquared-custom-extensions'
|
||||
VERSION = '1.2.0'
|
||||
CONTACT_EMAIL = 'foodogsquared@foodogsquared.one'
|
||||
USER_AGENT = "#{NAME}/#{VERSION} ( #{CONTACT_EMAIL} )".freeze
|
||||
|
||||
def warn_or_raise(doc, warning)
|
||||
return raise warning if (doc.safe > Asciidoctor::SafeMode::SERVER) && !(doc.attr? 'allow-uri-read')
|
||||
|
||||
warn warning
|
||||
end
|
||||
end
|
@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class IETFRFCInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :rfc
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
url = %(https://datatracker.ietf.org/doc/html/#{target})
|
||||
attrs['caption'] ||= "RFC#{target}"
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
||||
end
|
53
lib/asciidoctor/foodogsquared/man-inline-macro/extension.rb
Normal file
53
lib/asciidoctor/foodogsquared/man-inline-macro/extension.rb
Normal file
@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class ManInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :man
|
||||
name_positional_attributes 'volnum'
|
||||
default_attributes 'service' => 'debian', 'subpath' => ''
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
manname = target
|
||||
|
||||
text = %(#{manname}(#{attrs['volnum']}))
|
||||
|
||||
if doc.basebackend? 'html'
|
||||
domain = case attrs['service']
|
||||
when 'debian'
|
||||
'https://manpages.debian.org'
|
||||
when 'ubuntu'
|
||||
'https://manpages.ubuntu.org'
|
||||
when 'arch'
|
||||
'https://man.archlinux.org/man'
|
||||
when 'opensuse'
|
||||
'https://manpages.opensuse.org'
|
||||
when 'voidlinux'
|
||||
'https://man.voidlinux.org'
|
||||
when 'openbsd'
|
||||
'https://man.openbsd.org'
|
||||
when 'none'
|
||||
nil
|
||||
else
|
||||
raise "no available manpage service #{attrs['service']}"
|
||||
end
|
||||
|
||||
if !domain.nil?
|
||||
target = %(#{domain}/#{attrs['subpath'].delete_prefix '/'}#{manname}.#{attrs['volnum']})
|
||||
doc.register :links, target
|
||||
node = create_anchor parent, text, type: :link, target: target
|
||||
else
|
||||
node = create_inline parent, :quoted, text
|
||||
end
|
||||
elsif doc.backend == 'manpage'
|
||||
node = create_inline parent, :quoted, text, type: :strong
|
||||
else
|
||||
node = create_inline parent, :quoted, text
|
||||
end
|
||||
|
||||
node
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,47 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class MusicBrainzInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :musicbrainz
|
||||
name_positional_attributes 'caption', 'type'
|
||||
default_attributes 'type' => 'release'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
root_endpoint = 'https://musicbrainz.org/ws/2'
|
||||
|
||||
begin
|
||||
headers = {
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => ::Asciidoctor::Foodogsquared::USER_AGENT
|
||||
}
|
||||
|
||||
uri = %(#{root_endpoint}/#{attrs['type']}/#{target})
|
||||
|
||||
metadata = OpenURI.open_uri(uri, headers) { |f| JSON.parse(f.read) }
|
||||
attrs['caption'] ||= case attrs['type']
|
||||
when 'artist', 'area', 'events', 'genre', 'instrument', 'label', 'place', 'series'
|
||||
metadata['name']
|
||||
when 'recording', 'release-group', 'release', 'cdstub', 'work'
|
||||
metadata['title']
|
||||
when 'url'
|
||||
metadata['resource']
|
||||
end
|
||||
|
||||
target = %(https://musicbrainz.org/#{attrs['type']}/#{target})
|
||||
doc.register :links, target
|
||||
create_anchor parent, attrs['caption'], type: :link, target: target
|
||||
rescue StandardError
|
||||
warning = %(error while getting Musicbrainz database object '#{target}: #{e}')
|
||||
warn_or_raise doc, warning
|
||||
reader.push_include warning, target, target, 1, attrs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# I'm fairly sure this could be programmed since Ruby has nice metaprogramming
|
||||
# capabilities. Though, we'll be keeping it manually defining classes for now
|
||||
# for initial versions since there could be additional features for each macro.
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class CtanInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :ctan
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://ctan.org/pkg/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
|
||||
class PypiInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :pypi
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://pypi.org/project/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
|
||||
class CratesIOInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :cratesio
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://crates.io/crates/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class RepologyInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :repology
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://repology.org/project/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
end
|
26
lib/asciidoctor/foodogsquared/spdx-inline-macro/README.adoc
Normal file
26
lib/asciidoctor/foodogsquared/spdx-inline-macro/README.adoc
Normal file
@ -0,0 +1,26 @@
|
||||
= SPDX link inline macro
|
||||
:toc:
|
||||
|
||||
It's an inline macro for easily creating licenses from SPDX license data.
|
||||
|
||||
|
||||
== Synopsis
|
||||
|
||||
[source, asciidoc]
|
||||
----
|
||||
spdx:$LICENSE_ID[$CAPTION]
|
||||
----
|
||||
|
||||
Where...
|
||||
|
||||
* `$LICENSE_ID` is the identifier for the license.
|
||||
|
||||
* `$CAPTION` is the link caption to be used.
|
||||
By default, it will use the name of the license.
|
||||
|
||||
|
||||
== Example usage
|
||||
|
||||
* `spdx:MIT[]` will result to the link:https://spdx.org/licenses/MIT.html[MIT license page on SPDX] with the caption `MIT License`.
|
||||
|
||||
* `spdx:MIT[the MIT license]` is the same as the previous example but with a different caption of `the MIT license`.
|
@ -0,0 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class SWHIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'swh:'
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attributes)
|
||||
swhid = target
|
||||
swhid_core_identifier = swhid.split(';').at(0)
|
||||
swhid_object_type = (swhid_core_identifier.split ':').at 2
|
||||
|
||||
unless (doc.safe <= Asciidoctor::SafeMode::SERVER) && (doc.attr? 'allow-uri-read')
|
||||
raise %('swh:' include cannot be used in safe mode level > SERVER and without attribute 'allow-uri-read')
|
||||
end
|
||||
|
||||
# We're already going to throw out anything that is not content object type
|
||||
# just to make the later pipelines easier to construct.
|
||||
if swhid_object_type != 'cnt'
|
||||
warn %(SWHID '#{swhid_core_identifier}' is not of 'cnt' type; ignoring)
|
||||
return reader
|
||||
end
|
||||
|
||||
version = '1'
|
||||
|
||||
content = begin
|
||||
uri = URI.parse %(https://archive.softwareheritage.org/api/#{version}/resolve/#{target}/)
|
||||
|
||||
headers = {
|
||||
'Accept' => 'application/json'
|
||||
}
|
||||
|
||||
headers['Authorization'] = "Bearer #{ENV['SWH_API_BEARER_TOKEN']}" if ENV['SWH_API_BEARER_TOKEN']
|
||||
|
||||
metadata = OpenURI.open_uri(uri, headers) { |f| JSON.parse(f.read) }
|
||||
object_hash = metadata['object_id']
|
||||
|
||||
uri = URI.parse %(https://archive.softwareheritage.org/api/#{version}/content/sha1_git:#{object_hash}/raw/)
|
||||
OpenURI.open_uri(uri, headers, &:read)
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{swhid_core_identifier}': #{e})
|
||||
warn warning
|
||||
warning
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attributes
|
||||
reader
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class SWHInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :swh
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
# We're only considering `swh:` starting with the scheme version. Also, it
|
||||
# looks nice aesthetically.
|
||||
swhid = target.start_with?('swh:') ? target : %(swh:#{target})
|
||||
default_caption = if attrs.key? 'full-option'
|
||||
swhid
|
||||
else
|
||||
swhid.split(';').at(0)
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
target = %(https://archive.softwareheritage.org/#{swhid})
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
module Asciidoctor::Foodogsquared::Extensions
|
||||
class WikipediaInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :wikipedia
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'lang' => 'en'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
caption = attrs['caption'] || target
|
||||
parser = URI::Parser.new
|
||||
page = parser.escape target
|
||||
link = %(https://#{attrs['lang']}.wikipedia.org/wiki/#{page})
|
||||
node = create_anchor parent, caption, type: :link, target: link
|
||||
|
||||
create_inline parent, :quoted, node.convert
|
||||
end
|
||||
end
|
||||
end
|
@ -1,66 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rugged'
|
||||
|
||||
class GitBlobIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'git:'
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
attrs['gitrepo'] ||= doc.attributes['gitrepo'] || doc.base_dir
|
||||
repo = Rugged::Repository.discover(attrs['gitrepo'])
|
||||
|
||||
git_object_ref = target.delete_prefix 'git:'
|
||||
git_object_ref = doc.attributes['doccontentref'] if git_object_ref.empty?
|
||||
|
||||
begin
|
||||
git_object = repo.rev_parse git_object_ref
|
||||
|
||||
if attrs.key? 'diff-option'
|
||||
options = {}
|
||||
|
||||
options[:paths] = attrs['path'].split(';') if attrs.key? 'path'
|
||||
options[:context_lines] = attrs['context-lines'] if attrs.key? 'context-lines'
|
||||
options[:reverse] = true if attrs.key? 'reverse-option'
|
||||
|
||||
if attrs.key? 'other'
|
||||
other = repo.rev_parse attrs['other'] || nil
|
||||
reader.push_include git_object.diff(other, **options).patch
|
||||
else
|
||||
reader.push_include git_object.diff(**options).patch
|
||||
end
|
||||
else
|
||||
inner_entry = case git_object.type
|
||||
when :blob
|
||||
git_object
|
||||
when :commit
|
||||
git_object.tree.path attrs['path']
|
||||
when :tree
|
||||
git_object.path attrs['path']
|
||||
when :tag
|
||||
git_object.target.tree.path attrs['path']
|
||||
end
|
||||
|
||||
content = repo.lookup(inner_entry[:oid]).content
|
||||
|
||||
if attrs.key? 'lines'
|
||||
content_lines = content.lines
|
||||
new_content = +''
|
||||
doc.resolve_lines_to_highlight(content, attrs['lines']).each do |line_no|
|
||||
new_content << content_lines.at(line_no - 1)
|
||||
end
|
||||
|
||||
content = new_content
|
||||
end
|
||||
|
||||
reader.push_include content
|
||||
end
|
||||
rescue StandardError => e
|
||||
reader.push_include "Unresolved directive for '#{target}' with the following error:\n#{e}"
|
||||
warn e
|
||||
end
|
||||
|
||||
reader
|
||||
end
|
||||
end
|
@ -1,36 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
class GitHubLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :github
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
default_caption = if attrs.key?('repo-option')
|
||||
target.split('/').at(1)
|
||||
else
|
||||
target
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
uri = URI.parse %(https://github.com/#{target})
|
||||
|
||||
if attrs.key? 'issue'
|
||||
uri.path += %(/issues/#{attrs['issue']})
|
||||
text << "##{attrs['issue']}" if text == target
|
||||
else
|
||||
uri.path += %(/tree/#{attrs['rev']}) if attrs.key? 'rev'
|
||||
uri.path += %(/#{attrs['path']}) if attrs.key? 'path'
|
||||
text << "@#{attrs['rev']}" if attrs.key?('rev') && text == target
|
||||
end
|
||||
|
||||
target = uri.to_s
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
@ -1,68 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
class GitHubRawIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'github:'
|
||||
end
|
||||
|
||||
def warn_or_raise(doc, warning)
|
||||
if (doc.safe > Asciidoctor::SafeMode::SERVER) && !(doc.attr? 'allow-uri-read')
|
||||
raise warning
|
||||
else
|
||||
warn warning
|
||||
end
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
src = target.delete_prefix('github:').split('/', 3)
|
||||
owner = src.at 0
|
||||
repo = src.at 1
|
||||
namespaced_repo = "#{owner}/#{repo}"
|
||||
|
||||
path = attrs['path'] || ''
|
||||
|
||||
# For more information, see https://docs.github.com/en/rest/repos/contents.
|
||||
uri = URI.parse %(https://api.github.com/repos/#{owner}/#{repo}/contents/#{path})
|
||||
|
||||
if attrs['rev']
|
||||
query = { ref: attrs['rev'] }
|
||||
uri.query = URI.encode_www_form query
|
||||
end
|
||||
|
||||
begin
|
||||
headers = {
|
||||
'Header' => 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version' => '2022-11-28'
|
||||
}
|
||||
|
||||
headers['Authorization'] = "Token #{ENV['GITHUB_API_BEARER_TOKEN']}" if ENV['GITHUB_API_BEARER_TOKEN']
|
||||
|
||||
OpenURI.open_uri(uri, headers) do |f|
|
||||
response = JSON.parse(f.read)
|
||||
|
||||
# If the response is an array, it is likely to be a directory. In this
|
||||
# usecase, we'll just list them.
|
||||
content = if response.is_a? Array
|
||||
warning = %(given path '#{path}' from GitHub repo '#{repo}' is a directory)
|
||||
warn_or_raise doc, warning
|
||||
warning
|
||||
elsif response.is_a? Object
|
||||
Base64.decode64 response['content'] if response['content'] && response['encoding'] == 'base64'
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attrs
|
||||
end
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{path}' in GitHub repo '#{namespaced_repo}: #{e}')
|
||||
warn_or_raise doc, warning
|
||||
reader.push_include warning, target, target, 1, attrs
|
||||
end
|
||||
|
||||
reader
|
||||
end
|
||||
end
|
@ -1,37 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
class GitLabLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :gitlab
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'domain' => 'gitlab.com'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
default_caption = if attrs.key?('repo-option')
|
||||
target.split('/').at(1)
|
||||
else
|
||||
target
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
uri = URI.parse %(https://#{attrs['domain']}/#{target})
|
||||
|
||||
if attrs.key? 'issue'
|
||||
uri.path += %(/-/issues/#{attrs['issue']})
|
||||
text << "##{attrs['issue']}" if text == target
|
||||
else
|
||||
uri.path += %(/-/tree/#{attrs['rev']}) if attrs.key? 'rev'
|
||||
uri.path += %(/#{attrs['path']}) if attrs.key? 'path'
|
||||
text << "@#{attrs['rev']}" if attrs.key?('rev') && text == target
|
||||
end
|
||||
|
||||
target = uri.to_s
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
@ -1,66 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
class GitLabRawIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'gitlab:'
|
||||
end
|
||||
|
||||
def warn_or_raise(doc, warning)
|
||||
if (doc.safe > Asciidoctor::SafeMode::SERVER) && !(doc.attr? 'allow-uri-read')
|
||||
raise warning
|
||||
else
|
||||
warn warning
|
||||
end
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attrs)
|
||||
src = target.delete_prefix('gitlab:').split('/', 2)
|
||||
owner = src.at 0
|
||||
repo = src.at 1
|
||||
namespaced_repo = "#{owner}/#{repo}"
|
||||
|
||||
raise %(there is no 'path' attribute given for GitLab repo '#{namespaced_repo}') unless attrs.key? 'path'
|
||||
raise %(no given ref for getting file in '#{namespaced_repo}') unless attrs.key? 'rev'
|
||||
|
||||
path = attrs['path']
|
||||
rev = attrs['rev']
|
||||
|
||||
domain = attrs['domain'] || 'gitlab.com'
|
||||
version = attrs['version'] || 'v4'
|
||||
|
||||
uri = URI.parse %(https://#{domain}/api/#{version})
|
||||
|
||||
# Set the project.
|
||||
uri += %(/projects/#{URI.encode_www_form_component namespaced_repo})
|
||||
|
||||
# Then the filename.
|
||||
uri += %(/repository/files/#{URI.encode_www_form_component path})
|
||||
|
||||
# Then the revision.
|
||||
query = { ref: rev }
|
||||
uri.query = URI.encode_www_form query
|
||||
|
||||
content = begin
|
||||
headers = { 'Content-Type' => 'application-json' }
|
||||
header['PRIVATE-TOKEN'] = ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN'] if ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN']
|
||||
|
||||
OpenURI.open_uri(uri, headers) do |f|
|
||||
response = JSON.parse(f.read)
|
||||
|
||||
Base64.decode64 response['content'] if response['content'] && response['encoding'] == 'base64'
|
||||
end
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{path}' in GitLab repo '#{repo}': #{e})
|
||||
warn_or_raise doc, warning
|
||||
warning
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attrs
|
||||
reader
|
||||
end
|
||||
end
|
@ -1,20 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class String
|
||||
def to_kebab
|
||||
self.gsub(/\s+/, '-') # Replace all spaces with dashes.
|
||||
.gsub(/[^a-zA-Z0-9-]/, '') # Remove all non-alphanumerical (and dashes) characters.
|
||||
.gsub(/-+/, '-') # Reduce all dashes into only one.
|
||||
.gsub(/^-|-+$/, '') # Remove all leading and trailing dashes.
|
||||
.downcase
|
||||
end
|
||||
end
|
||||
|
||||
module Asciidoctor
|
||||
module FoodogsquaredCustomExtensions
|
||||
NAME = 'asciidoctor-foodogsquared-custom-extensions'
|
||||
VERSION = '1.0.0'
|
||||
CONTACT_EMAIL = 'foodogsquared@foodogsquared.one'
|
||||
USER_AGENT = "#{NAME}/#{VERSION} ( #{CONTACT_EMAIL} )"
|
||||
end
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IETFRFCLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :rfc
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
url = %(https://datatracker.ietf.org/doc/html/#{target})
|
||||
attrs['caption'] ||= "RFC#{target}"
|
||||
doc.register :links, url
|
||||
create_anchor parent, attrs['caption'], type: :link, target: url
|
||||
end
|
||||
end
|
@ -1,51 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ManInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :man
|
||||
name_positional_attributes 'volnum'
|
||||
default_attributes 'service' => 'debian', 'subpath' => ''
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
manname = target
|
||||
|
||||
text = %(#{manname}(#{attrs['volnum']}))
|
||||
|
||||
if doc.basebackend? 'html'
|
||||
domain = case attrs['service']
|
||||
when 'debian'
|
||||
'https://manpages.debian.org'
|
||||
when 'ubuntu'
|
||||
'https://manpages.ubuntu.org'
|
||||
when 'arch'
|
||||
'https://man.archlinux.org/man'
|
||||
when 'opensuse'
|
||||
'https://manpages.opensuse.org'
|
||||
when 'voidlinux'
|
||||
'https://man.voidlinux.org'
|
||||
when 'openbsd'
|
||||
'https://man.openbsd.org'
|
||||
when 'none'
|
||||
nil
|
||||
else
|
||||
raise "no available manpage service #{attrs['service']}"
|
||||
end
|
||||
|
||||
if !domain.nil?
|
||||
target = %(#{domain}/#{attrs['subpath'].delete_prefix '/'}#{manname}.#{attrs['volnum']})
|
||||
doc.register :links, target
|
||||
node = create_anchor parent, text, type: :link, target: target
|
||||
else
|
||||
node = create_inline parent, :quoted, text
|
||||
end
|
||||
elsif doc.backend == 'manpage'
|
||||
node = create_inline parent, :quoted, text, type: :strong
|
||||
else
|
||||
node = create_inline parent, :quoted, text
|
||||
end
|
||||
|
||||
node
|
||||
end
|
||||
end
|
@ -1,45 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
class MusicBrainzLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :musicbrainz
|
||||
name_positional_attributes 'caption', 'type'
|
||||
default_attributes 'type' => 'release'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
root_endpoint = 'https://musicbrainz.org/ws/2'
|
||||
|
||||
begin
|
||||
headers = {
|
||||
'Accept' => 'application/json',
|
||||
'User-Agent' => ::Asciidoctor::FoodogsquaredCustomExtensions::USER_AGENT
|
||||
}
|
||||
|
||||
uri = %(#{root_endpoint}/#{attrs['type']}/#{target})
|
||||
|
||||
metadata = OpenURI.open_uri(uri, headers) { |f| JSON.parse(f.read) }
|
||||
attrs['caption'] ||= case attrs['type']
|
||||
when 'artist', 'area', 'events', 'genre', 'instrument', 'label', 'place', 'series'
|
||||
metadata['name']
|
||||
when 'recording', 'release-group', 'release', 'cdstub', 'work'
|
||||
metadata['title']
|
||||
when 'url'
|
||||
metadata['resource']
|
||||
end
|
||||
|
||||
target = %(https://musicbrainz.org/#{attrs['type']}/#{target})
|
||||
doc.register :links, target
|
||||
create_anchor parent, attrs['caption'], type: :link, target: target
|
||||
rescue
|
||||
warning = %(error while getting Musicbrainz database object '#{target}: #{e}')
|
||||
warn_or_raise doc, warning
|
||||
reader.push_include warning, target, target, 1, attrs
|
||||
end
|
||||
end
|
||||
end
|
@ -1,56 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# I'm fairly sure this could be programmed since Ruby has nice metaprogramming
|
||||
# capabilities. Though, we'll be keeping it manually defining classes for now
|
||||
# for initial versions since there could be additional features for each macro.
|
||||
|
||||
class CtanLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :ctan
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://ctan.org/pkg/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
|
||||
class PypiLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :pypi
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://pypi.org/project/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
||||
|
||||
class CratesIOLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :cratesio
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://crates.io/crates/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RepologyLinkInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :repology
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
text = attrs['caption'] || target
|
||||
url = %(https://repology.org/project/#{target})
|
||||
|
||||
doc.register :links, url
|
||||
|
||||
create_anchor parent, text, type: :link, target: url
|
||||
end
|
||||
end
|
@ -1,53 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require 'open-uri'
|
||||
require 'uri'
|
||||
|
||||
class SWHIDIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
|
||||
def handles?(target)
|
||||
target.start_with? 'swh:'
|
||||
end
|
||||
|
||||
def process(doc, reader, target, attributes)
|
||||
swhid = target
|
||||
swhid_core_identifier = swhid.split(';').at(0)
|
||||
swhid_object_type = (swhid_core_identifier.split ':').at 2
|
||||
|
||||
unless (doc.safe <= Asciidoctor::SafeMode::SERVER) && (doc.attr? 'allow-uri-read')
|
||||
raise %('swh:' include cannot be used in safe mode level > SERVER and without attribute 'allow-uri-read')
|
||||
end
|
||||
|
||||
# We're already going to throw out anything that is not content object type
|
||||
# just to make the later pipelines easier to construct.
|
||||
if swhid_object_type != 'cnt'
|
||||
warn %(SWHID '#{swhid_core_identifier}' is not of 'cnt' type; ignoring)
|
||||
return reader
|
||||
end
|
||||
|
||||
version = '1'
|
||||
|
||||
content = begin
|
||||
uri = URI.parse %(https://archive.softwareheritage.org/api/#{version}/resolve/#{target}/)
|
||||
|
||||
headers = {
|
||||
'Accept' => 'application/json'
|
||||
}
|
||||
|
||||
headers['Authorization'] = "Bearer #{ENV['SWH_API_BEARER_TOKEN']}" if ENV['SWH_API_BEARER_TOKEN']
|
||||
|
||||
metadata = OpenURI.open_uri(uri, headers) { |f| JSON.parse(f.read) }
|
||||
object_hash = metadata['object_id']
|
||||
|
||||
uri = URI.parse %(https://archive.softwareheritage.org/api/#{version}/content/sha1_git:#{object_hash}/raw/)
|
||||
OpenURI.open_uri(uri, headers, &:read)
|
||||
rescue OpenURI::HTTPError => e
|
||||
warning = %(error while getting '#{swhid_core_identifier}': #{e})
|
||||
warn warning
|
||||
warning
|
||||
end
|
||||
|
||||
reader.push_include content, target, target, 1, attributes
|
||||
reader
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SWHInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :swh
|
||||
name_positional_attributes 'caption'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
doc = parent.document
|
||||
|
||||
# We're only considering `swh:` starting with the scheme version. Also, it
|
||||
# looks nice aesthetically.
|
||||
swhid = target.start_with?('swh:') ? target : %(swh:#{target})
|
||||
default_caption = if attrs.key? 'full-option'
|
||||
swhid
|
||||
else
|
||||
swhid.split(';').at(0)
|
||||
end
|
||||
text = attrs['caption'] || default_caption
|
||||
target = %(https://archive.softwareheritage.org/#{swhid})
|
||||
|
||||
doc.register :links, target
|
||||
create_anchor parent, text, type: :link, target: target
|
||||
end
|
||||
end
|
@ -1,21 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
|
||||
class WikipediaInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
||||
use_dsl
|
||||
|
||||
named :wikipedia
|
||||
name_positional_attributes 'caption'
|
||||
default_attributes 'lang' => 'en'
|
||||
|
||||
def process(parent, target, attrs)
|
||||
caption = attrs['caption'] || target
|
||||
parser = URI::Parser.new
|
||||
page = parser.escape target
|
||||
link = %(https://#{attrs['lang']}.wikipedia.org/wiki/#{page})
|
||||
node = create_anchor parent, caption, type: :link, target: link
|
||||
|
||||
create_inline parent, :quoted, node.convert
|
||||
end
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe FDroidLinkInlineMacro do
|
||||
describe FDroidInlineMacro do
|
||||
it 'should create a FDroid link' do
|
||||
input = 'fdroid:org.moire.ultrasonic[]'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe FlathubLinkInlineMacro do
|
||||
describe FlathubInlineMacro do
|
||||
it 'should create a Flathub link to Icon Library app' do
|
||||
input = 'flathub:org.gnome.design.IconLibrary[]'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe GitHubLinkInlineMacro do
|
||||
describe GitHubInlineMacro do
|
||||
it 'should create a GitHub link with the caption being the target' do
|
||||
input = <<~INPUT
|
||||
github:foo-dogsquared/foobarbazxyz[]
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe GitHubRawIncludeProcessor do
|
||||
describe GitHubIncludeProcessor do
|
||||
it 'should include the raw content of a GitHub file successfully' do
|
||||
input = <<~INPUT
|
||||
[literal]
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe GitLabLinkInlineMacro do
|
||||
describe GitLabInlineMacro do
|
||||
it 'should link to the GitLab page for GitLab project' do
|
||||
input = 'gitlab:gitlab-org/gitlab[]'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe GitLabRawIncludeProcessor, if: ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN'] do
|
||||
describe GitLabIncludeProcessor, if: ENV['GITLAB_API_PERSONAL_ACCESS_TOKEN'] do
|
||||
it 'should include the GitLab CI configuration from freedesktop-sdk/freedesktop-sdk from the default instance' do
|
||||
commit = 'bcb3e0de957519e87a4c7b8c0e40af9876e531e7'
|
||||
input = <<~INPUT
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe MusicBrainzLinkInlineMacro do
|
||||
describe MusicBrainzInlineMacro do
|
||||
it 'should create a MusicBrainz release object with the right captions' do
|
||||
input = 'musicbrainz:9adcff14-7dba-4ccf-a6a6-298bcde3dd46[]'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe RepologyLinkInlineMacro do
|
||||
describe RepologyInlineMacro do
|
||||
it 'should link to the Repology page for beets' do
|
||||
input = 'repology:beets[]'
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'asciidoctor'
|
||||
require 'asciidoctor/foodogsquared-extensions'
|
||||
require 'asciidoctor-foodogsquared-extensions'
|
||||
|
||||
include Asciidoctor::Foodogsquared::Extensions
|
||||
RSpec.configure do
|
||||
def fixtures_dir
|
||||
File.join __dir__, 'fixtures'
|
||||
|
Loading…
Reference in New Issue
Block a user