jekyll_aspec 1.0.0.pre.alpha → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c77631b5967f60ae7f3f0ab335f0445b4c09e174
4
- data.tar.gz: 8eba5d605e5d4d1d2425cccb6495114f81669497
3
+ metadata.gz: a7660532ed66dd03c81835079b3f1f2c57a495a7
4
+ data.tar.gz: 9fd9c8f89aad34a1498ea52cd3f04b021db3a564
5
5
  SHA512:
6
- metadata.gz: cfc99e19a9cb848003dc84d99e2f5c5cc61f2702bcfdfd915f89db0bc0375e1737abe9bb9a04c6ecb1d711400ca93d4200c7779e2d5146e20e5fdf7027046ce0
7
- data.tar.gz: 48268e49f8269b28e5a449e17aa09f009145488a979cc5be4782ba565cfd290ecb020d48b3add72a25c32499a943293f2da8f40308128bc11f9d0e14d2740319
6
+ metadata.gz: a13f2ee35c2a0082ecb6c11d4ddce5c76c6145ccc16113912fcad43bb4ca751d7560d4ed03b8465489be8a22f21d7533d384ecc3bdcc6cea59847e26bf248ef0
7
+ data.tar.gz: 141f495acb546d4ef8a8e0037b6e9d099fc4803d11455af2149e0ab3ec59b8fae1a9d9b1e14a68c42b57563602f1c3cf122693aafe05b83b74e2a0ec6ae454e0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jekyll_aspec (1.0.0.pre.alpha)
4
+ jekyll_aspec (1.0.0)
5
5
  asciidoctor (>= 1.5.0)
6
6
 
7
7
  GEM
data/README.adoc ADDED
@@ -0,0 +1,54 @@
1
+ = Jekyll Aspec
2
+
3
+ image:https://travis-ci.org/bsmith-n4/jekyll_aspec.svg?branch=master["Build Status", link="https://travis-ci.org/bsmith-n4/jekyll_aspec"]
4
+
5
+ A selection of Asciidoctor extensions designed to used to write some AsciiSpec with Jekyll.
6
+
7
+ These extensions add custom blocks for Requirements and attempts to smartly handle inter-document auto-linking functionality.
8
+
9
+ == Motivation
10
+
11
+ Jekyll is a very flexible and speedy tool for generating static HTML pages.
12
+ The `jekyl-asciidoc` gem adds Asciidoctor functionality but it lacks a few features due to the way it handles multiple source files. As each `.adoc` file is consumed individually, we lose the ability to automatically format inter-document cross-references. This plugin is a group of extensions that performs some directory walking, stores the location of titles and anchors so that cross references in a Jekyll project are resolved automatically.
13
+
14
+ Additional features are
15
+
16
+ * Custom block for ``TODO``s
17
+ * Requirements Block with versioning
18
+ * Requirements block macro that creates a Table of Contents of declared Requirements
19
+ * Inline Callout macro to arbitrarily add callouts
20
+ * Inline Task macro to link to Jira tickets or Github Issues
21
+ * Inline Repo Macro to link to specific files or lines on GitHub
22
+ * A HTML postprocessor to correct some minor fixes and invalid tags created by Asciidoctor
23
+
24
+ When these custom extensions are combined with other recommended gems such as `asciidoctor-bibtex` and `asciidoctor-latex`, you can achieve quite high quality, speedy HTML documentation for technical projects with the benefits of a Jekyll build. It's recommended to use the `html-proofer` gem which will validate all links created with these extensions.
25
+
26
+ == Installation
27
+
28
+ Add `jekyll_aspec` to your Jekyll Gemfile:
29
+
30
+ ```ruby
31
+ group :jekyll_plugins do
32
+ gem 'jekyll-asciidoc'
33
+ gem 'jekyll_aspec'
34
+ end
35
+ ```
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install jekyll_aspec
40
+
41
+ == Docs
42
+
43
+ Please refer to the `docs` directory for some basic documentation on extended Asciidoctor features:
44
+
45
+ *<<docs/requirement-block#,[req] - Requirement Block>>*: Add requirements with custom formatting. +
46
+ *<<docs/todo-block#,[TODO] - Todo Block>>*: Add a custom TODO admonition block. +
47
+
48
+ == Contributing
49
+
50
+ This gem is under heavy initial development and there are still many kinks to work out. The areas to be improved upon include performance enhancements, proper handling of file IO / directory walking and, of course, documentation. Bug reports and pull requests are welcome on GitHub at https://github.com/bsmith-n4/jekyll_aspec.
51
+
52
+ == License
53
+
54
+ The gem is available as open source under the terms of the https://opensource.org/licenses/MIT[MIT License].
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'test/unit'
3
3
 
4
4
  task :default => :test
5
5
 
6
6
  task :test do
7
- ruby "test/suite.rb"
7
+ ruby 'test/suite.rb'
8
8
  end
@@ -0,0 +1,34 @@
1
+ = Inline Task Macro
2
+ :toc:
3
+
4
+ Usage::
5
+ [source,asciidoc]
6
+ task:target[title]
7
+
8
+ The `inline task macro` creates hyperlinks to Jira task management and GitHub issue-tracking systems.
9
+ Optionally, it will render the links differently to reflect the status of the tasks if a task info file is provided.
10
+
11
+ Note that in case two colons are given instead of only one after `task`, the task link will be moved to the sidebar.
12
+
13
+ Attributes::
14
+ * *target:* The project prefix followed by a hyphen and the task number or ID (e.g. `23`).
15
+ * *title:* (optional) The text that will be displayed as an anchor in the generated hyperlink.
16
+
17
+ == Configuration
18
+
19
+ The target pattern needs to be specified in a document attribute:
20
+
21
+ `:task-pattern: http://www.myorg.github.com/myrepo/issues/`
22
+
23
+ For a Jekyll build, this can be added in the Jekyll `pass:[_config.yml]` to be passed to all documents:
24
+
25
+ ```yaml
26
+ asciidoctor:
27
+ attributes:
28
+ task-pattern: http://www.myorg.github.com/myrepo/issues/
29
+ ```
30
+
31
+ == Examples
32
+
33
+ A bug has already ``+++task:35[]+++`` been filed...
34
+
@@ -0,0 +1,56 @@
1
+ = Requirements Block
2
+
3
+ Usage::
4
+
5
+ .title
6
+ [req,id=RSL-3,version=1]
7
+ --
8
+ Contents of the requirement
9
+ --
10
+
11
+
12
+ Attributes::
13
+ * *title* (required): An anchor is derived from the requirement title and embedded at the beginning of the rendered output.
14
+ * *ID:* (required) The ID in the form *<Prefix>-<Number>*, used to generate an anchor
15
+ * *version*: (required) value is a non-negative integer.
16
+
17
+ NOTE: Omitting any of the above attributes will print an error to the console and insert a warning text in the generated document.
18
+ * *delimiter*: Lines containing only two hyphens `--` delimit the block. This is required if the block contains empty lines or nested formatting.
19
+
20
+ == ID Pattern
21
+
22
+ The purpose of the `<Prefix>-<Number>` ID is to ensure that Requirements are both unique and easily referenceable.
23
+ Currently, the ID may be any string, but should conform to the following conventions:
24
+
25
+ *<Prefix>*: :: `R` (requirement) followed by the project prefix (i.e. `SL` for `stdlib`)
26
+ *<Number>*: :: The requirement number, currently not validated.
27
+ A validation stage for requirement IDs (detecting duplicates, for instance) is planned.
28
+
29
+ Example::
30
+
31
+ The following example demonstrates how to document Requirement pass:[#]3 for stdlib Version 1;
32
+
33
+ .This is the title
34
+ [req,id=RSL-3,version=1]
35
+ --
36
+ My Super Requirement
37
+ --
38
+
39
+
40
+ *Req. RSL-3: <<This_is_the_title,This is the title>> (ver. 1)* +
41
+ My Super Requirement
42
+
43
+
44
+ == Xrefs
45
+
46
+ Cross-referencing requirements is done using the syntax `\<<Req-ID,Optional Link Text>>`, e.g. for the following requirement:
47
+
48
+ [req,id=ROPR-14603,version=1]
49
+ --
50
+ ...
51
+ --
52
+
53
+
54
+ can be cross-referenced using the following syntax
55
+
56
+ See <<Req-ROPR-14603>>, or see also <<Req-ROPR-14603,confirm the booking>>.
@@ -0,0 +1,48 @@
1
+ = TODO Block
2
+
3
+ == Usage
4
+
5
+ [source,asciidoc]
6
+ ----
7
+ // Simple use
8
+
9
+ [TODO]
10
+ Don't forget
11
+ ----
12
+
13
+ === Delimiters
14
+
15
+ Delimiters are required if the block contains empty lines or nested blocks. +
16
+ The following delimiters may be used:
17
+
18
+ [source,subs=macros]
19
+ ----
20
+ ====
21
+ --
22
+ pass:[++++]
23
+ ****
24
+ pass:[----]
25
+ ----
26
+
27
+ Examples::
28
+ [source,asciidoc]
29
+ ----
30
+ .Block Title (optional)
31
+ [TODO]
32
+ --
33
+ Don't Forget!
34
+
35
+ . Resolve an issue
36
+ .. Don't break anything
37
+ --
38
+
39
+ // or
40
+
41
+ [TODO]
42
+ ++++
43
+ Don't divide by zero.
44
+
45
+ * Add 1 to infinity.
46
+ ++++
47
+
48
+ ----
data/jekyll_aspec.gemspec CHANGED
@@ -10,9 +10,16 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["brian.smith@numberfour.eu"]
11
11
 
12
12
  spec.summary = %q{Asciidoctor extensions for use as a Jekyll plugin}
13
+ spec.description = %q{This plugin is a group of Asciidoctor extensions that perform directory walking,
14
+ resolving the location of titles and anchors in all adoc files so that inter-document
15
+ cross-references in a Jekyll project are resolved automatically. Also included are some
16
+ custom macros and blocks that are useful for techinical writing.}
13
17
  spec.homepage = "https://github.com/bsmith-n4/jekyll_aspec"
14
18
  spec.license = "MIT"
15
19
 
20
+ # This gem will work with 2.0 or greater.
21
+ spec.required_ruby_version = '>= 2.0'
22
+
16
23
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
24
  f.match(%r{^(test|spec|features)/})
18
25
  end
@@ -1,117 +1,23 @@
1
1
  require 'asciidoctor/extensions'
2
2
  require 'pathname'
3
+ require_relative 'utils/xref_helper'
3
4
 
4
5
  include ::Asciidoctor
5
6
 
6
- # Find all Adoc Files
7
- adoc_files = Dir.glob('**/*.adoc')
7
+ mismatches = Xrefs.list_xrefs
8
8
  invoc = Dir.pwd
9
9
 
10
- # Make some arrays available
11
- titles = []
12
- anchors = []
13
- xrefs = []
14
- mismatches = []
15
-
16
- replacement = ''
17
-
18
- def trim(s)
19
- s.gsub!(/_docs\//, '')
20
- s.gsub!(/(\.adoc|\.md|\.html)/, '')
21
- end
22
-
23
- def targetify(t)
24
- # make all chars lowercase and substitute spaces with hyphens
25
- t.downcase.gsub(/\s/, '-')
26
- end
27
-
28
- adoc_files.each do |file_name|
29
- lc = 0
30
-
31
- File.read(file_name).each_line do |li|
32
- lc += 1
33
-
34
- # Match all <<xrefs>> exluding Requirements
35
- if li[/\<\<(?!Req)(.+?)\>\>/]
36
-
37
- text = ''
38
- target = ''
39
- path = trim(file_name)
40
- xref = li.chop.match(/\<\<(?!Req)(\S.+?)\>\>/i).captures[0].to_s
41
-
42
- if xref[/,/]
43
- target = xref.downcase.gsub(/,.+/, '').gsub(/\s/, '-')
44
- text = xref.gsub(/.+,/, '').lstrip!
45
- xref = xref.sub(/,.+/, '')
46
- path = file_name
47
- else
48
- target = xref.downcase.gsub(/\s/, '-')
49
- text = xref
50
- end
51
-
52
- item = [xref, path, file_name, text, target]
53
- xrefs.push item
54
-
55
- # Match .Titles and = Section Titles
56
- elsif li[/(^(\.\S\w+)|^(\=+\s+?\S+.+))/]
57
-
58
- # Add check if none found (captures nil)
59
- title = li.chop.match(/(?!=+\s)(\S+.+?)$/i).captures[0]
60
- title.sub!(/\.(?=\w+?)/, '') if title[/\.(?=\w+?)/]
61
- path = trim(file_name)
62
- item = [title, path, file_name]
63
- titles.push item
64
-
65
- # Match [[anchors]]
66
- elsif li[/\[\[.+?\]\]/]
67
-
68
- # Add check if none found (captures nil)
69
- anchor = li.chop.match(/(?<=\[\[).+?(?=\]\])/).to_s
70
-
71
- if anchor[/,/]
72
- anchor = anchor.match(/(?<=\[\[)(?:|[\w+?_:][\w+?:.-]*)(?=,.+?\]\])/).to_s
73
- text = anchor.sub(/.+?,/, '')
74
- text = text.sub(/\]\]$/, '')
75
- end
76
-
77
- path = trim(file_name)
78
- item = [anchor, path, file_name, text]
79
- titles.push item
80
-
81
- end
82
- end
83
- end
84
-
85
- # Run through each xref and check for matching titles
86
- xrefs.each do |xref, xpath, xfile, xtext, xtarget|
87
- # check xrefs against titles
88
- titles.each do |ttext, tpath, tfile, _tdisp|
89
- # puts "checking #{ttext} against #{xref}"
90
- next unless ttext == xref
91
- tpath = 'index' if tpath.to_s.empty?
92
- # If the paths are not the same (xref and title not the same document) do the following
93
- next unless tpath != xpath
94
-
95
- # puts "Title \"#{ttext}\" in #{tfile} - mismatched xref \"#{xref}\" to different doc - #{xpath}"
96
- xtform = targetify(xtarget)
97
- detail = [xref, xtarget, xtext, xpath, xfile, ttext, tpath, tfile, xtform]
98
- mismatches.push detail
99
- end
100
- end
101
-
102
10
  Extensions.register do
103
11
  preprocessor do
104
12
  process do |document, reader|
105
13
  fixes = []
106
14
  i = 0
107
15
 
108
- # Block is loaded once per document!!!
109
- # for each malformed xref
16
+ # TODO - remove unused elements (prepended with _) from the helper method in utils/xref_helper
110
17
  mismatches.each do |_xref, _xtarget, xtext, _xpath, xfile, _ttext, _tpath, tfile, xtform|
111
- # FIXME: This directory is empty in POSTS - breaks conversion
112
18
  docfile = document.attributes['docfile'].sub(/^#{invoc}\//, '')
113
- trim(docfile)
114
-
19
+ Xrefs.trim(docfile)
20
+
115
21
  next unless docfile.to_s == xfile
116
22
 
117
23
  # calculate the relative path between source and target
@@ -129,8 +35,6 @@ Extensions.register do
129
35
  if li[/\<\<(?!Req)(.+?)\>\>/]
130
36
 
131
37
  mismatches.each do |xref, xtarget, xtext, _xpath, _xfile, _ttext, _tpath, _tfile, _relpath|
132
- # check if the line contains the original xref
133
-
134
38
  next unless li[/\<\<#{xref}(,.+)?\>\>/]
135
39
  fixes.each do |x|
136
40
  if x[/#{xtarget}/]
@@ -140,7 +44,6 @@ Extensions.register do
140
44
  end
141
45
  i += 1
142
46
  end
143
-
144
47
  else
145
48
  replacement = ''
146
49
  end
@@ -2,6 +2,7 @@ require 'asciidoctor/extensions'
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
+ # Strip bogus tags generated by Asciidoctor
5
6
  Extensions.register do
6
7
  postprocessor do
7
8
  process do |document, output|
@@ -2,6 +2,8 @@ require 'asciidoctor/extensions'
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
+ # @example Basic Usage
6
+ # See call:1[] for details
5
7
  Asciidoctor::Extensions.register do
6
8
  inline_macro do
7
9
  named :call
@@ -9,4 +11,4 @@ Asciidoctor::Extensions.register do
9
11
  Asciidoctor::Inline.new(parent, :callout, target.to_i).convert
10
12
  end
11
13
  end
12
- end
14
+ end
@@ -4,18 +4,24 @@ require_relative 'utils/block'
4
4
 
5
5
  include ::Asciidoctor
6
6
 
7
+ # @example Basic Usage
8
+ # See cwiki:topic[] for details
9
+ # @example Block Use
10
+ # Already documented. cwiki::topic[]
7
11
  Extensions.register do
8
12
  inline_macro do
9
13
  named :cwiki
10
14
 
11
15
  process do |parent, target, attrs|
12
- pattern =
13
- (parent.document.attr 'cwiki-pattern') ||
14
- 'https://confluence.numberfour.eu/display/%s'
16
+ pattern = parent.document.attr 'cwiki-pattern'
17
+ if pattern.nil?
18
+ warn "asciidoctor: WARNING: Attribue 'cwiki-pattern' for inline repo macro not defined"
19
+ pattern = "unknown"
20
+ end
15
21
  url = pattern % target
16
22
 
17
23
  label = Labels.getstatus(attrs)
18
- html = Context.form(attrs, target, url, label)
24
+ html = Context.format(attrs, target, url, label)
19
25
  (create_pass_block parent, html, attrs).render
20
26
  end
21
27
  end
@@ -2,29 +2,34 @@ require 'asciidoctor/extensions' unless RUBY_ENGINE == 'opal'
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
+ url = ''
6
+ file = ''
7
+ line = ''
8
+ formattedurl = ''
9
+ text = ''
10
+
5
11
  # Link to a file on GitHub.
6
12
  #
7
13
  # repo:<repository>:<file>:<line>[]
8
14
  #
9
- # Examples:
15
+ # The target should be set using document attributes prefixed by 'repo_'.
10
16
  #
17
+ # @example Attribute configuration
18
+ # :repo_dockerfiles: www.github.com/exampleuser/dockerfiles/issues
19
+ # @example Simple Use
11
20
  # repo:dockerfiles:ansible/Dockerfile_template[]
21
+ # @example Link to repo and line number
12
22
  # repo:dockerfiles:ansible/Dockerfile_template:2[]
23
+ # @example Link to repo, branch and line number
13
24
  # repo:dockerfiles:ansible/Dockerfile_template:5[branch="AS_v0.0.10"]
25
+ # @example Link to repo and line number (alternate use)
14
26
  # repo:dockerfiles:ansible/Dockerfile_template[line="5",branch="AS_v0.0.10"]
15
- #
16
-
17
- url = ''
18
- file = ''
19
- line = ''
20
- formattedurl = ''
21
- text = ''
22
-
23
27
  Extensions.register do
24
28
  inline_macro do
25
29
  named :repo
26
30
 
27
31
  process do |parent, target, attrs|
32
+ # @todo fix handling of use within cells. This is done using the context.
28
33
  if parent.context.to_s == 'cell'
29
34
  warn %([Hell in a cell] cell with repo link must have 'asciidoc format')
30
35
  end
@@ -66,6 +71,11 @@ Extensions.register do
66
71
  end
67
72
  end
68
73
 
74
+ if formattedurl.nil?
75
+ warn "asciidoctor: WARNING: Attribue 'repo_...' for inline repo macro not defined"
76
+ pattern = "unknown"
77
+ end
78
+
69
79
  html = %(<a href=\"#{formattedurl}\" style=\"padding-right:2px;\">
70
80
  <span class=\"label label-#{label}\" style=\"font-weight: 400;
71
81
  font-size:smaller;\">
@@ -4,18 +4,24 @@ require_relative 'utils/block'
4
4
 
5
5
  include ::Asciidoctor
6
6
 
7
+ # @example Basic Usage
8
+ # See task:101[] for details
9
+ # @example Block Use
10
+ # Already completed. task::101[]
7
11
  Extensions.register do
8
12
  inline_macro do
9
13
  named :task
10
14
 
11
15
  process do |parent, target, attrs|
12
- pattern =
13
- (parent.document.attr 'task-pattern') ||
14
- 'https://jira.numberfour.eu/browse/%s'
16
+ pattern = parent.document.attr 'task-pattern'
17
+ if pattern.nil?
18
+ warn "asciidoctor: WARNING: Attribue 'task-pattern' for inline task macro not defined"
19
+ pattern = "unknown"
20
+ end
15
21
  url = pattern % target
16
- # Some utility functions used by similar inline macros
22
+
17
23
  label = Labels.getstatus(attrs)
18
- html = Context.form(attrs, target, url, label)
24
+ html = Context.format(attrs, target, url, label)
19
25
  (create_pass_block parent, html, attrs).render
20
26
  end
21
27
  end
@@ -1,6 +1,8 @@
1
+ # Used by Jekyll during conversion
1
2
  module Jekyll
2
3
  # A Liquid Template Filter for using regular expressions
3
4
  module RegexFilter
5
+ # Simple replacement
4
6
  def replace_regex(input, regex_string, replace_string)
5
7
  regex = Regexp.new regex_string
6
8
  input.gsub regex, replace_string
@@ -1,8 +1,9 @@
1
1
  require 'asciidoctor/extensions'
2
2
 
3
3
  include ::Asciidoctor
4
+
4
5
  # Preprocessor that strips the << tags
5
- # FIXME: may break conversion if line ends with >>
6
+ # @todo may break conversion if line ends with >>
6
7
 
7
8
  req = '<<req-'
8
9
  brackets = /<<|>>/
@@ -2,26 +2,30 @@ require 'asciidoctor/extensions' unless RUBY_ENGINE == 'opal'
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
+ # @todo check if all code before the macro can be moved into util directory as helper methods
6
+
7
+ # @todo Don't do this
5
8
  adoc_files = Dir.glob('**/*.adoc')
9
+ # @todo Retrieve these via document attributes, should not be hardcoded
10
+ docsdir = '_docs'
6
11
  exts = '(\.adoc|\.md|\.html)'
7
12
 
8
13
  rpath = nil
9
14
  rtext = nil
15
+ orphan = false
16
+
10
17
  reqs = []
11
18
  inc_reqs = []
12
19
  com_reqs = []
13
20
  incs = []
14
21
  xrefs = []
22
+
15
23
  xref_base = ''
16
24
 
17
- blockrx = %r(^\/{4,}$)
25
+ blockrx = %r{^\/{4,}$}
18
26
  linerx = %r{^//(?=[^/]|$)}
19
27
 
20
- orphan = false
21
-
22
- # Retrieve this via document attribute
23
- docsdir = '_docs'
24
-
28
+ # @todo called helper method here
25
29
  def trim(s)
26
30
  s.gsub!(/_docs\//, '')
27
31
  s.gsub!(/(\.adoc|\.md|\.html)/, '')
@@ -90,11 +94,16 @@ end
90
94
  # Sort (in-place) by numberic ID
91
95
  reqs.sort_by!(&:first)
92
96
 
97
+ # @todo convert to formal
93
98
  Extensions.register do
94
99
  inline_macro do
95
100
  named :requirement_autoxref
96
101
 
97
102
  # Regex-based, will match "See Req-ROPR-123 for..."
103
+ # Will also match <<Req-ROPR-123>>
104
+ # @todo this is a heavy-handed approach to matching all
105
+ # xrefs. Find a better way to autolink xrefs that doesn't involved the
106
+ # use of the req-preprocessor
98
107
  match /(Req-\w+-?\d+)/
99
108
 
100
109
  # match id with Req-\w+-?(\d+)
@@ -2,50 +2,55 @@ require "asciidoctor/extensions" unless RUBY_ENGINE == "opal"
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
- Extensions.register do
6
- block do
7
- named :req
8
- on_contexts :open, :paragraph, :example, :listing, :sidebar, :pass
9
- name_positional_attributes "number", "version"
10
-
11
- process do |parent, reader, attrs|
12
- # Add pass characters here to prevent html character replacements for < > tags
13
- pass = "+++"
14
- attrs["name"] = "requirement"
15
- attrs["caption"] = "Requirement: "
16
- id = attrs["id"]
17
- nl = ""
18
-
19
- begin
20
- # downcase the title and replace spaces with underscores.
21
- # Also replacing special HTML entities:
22
- # &quot; = "
23
- # &amp; = &
24
- downcased_title = attrs["title"].downcase.tr(" ", "_").gsub('"', "&quot;")
25
- san_title = attrs["title"].gsub(/&/, "&amp;")
26
- rescue Exception => msg
27
- puts msg
28
- # If no title exists on the Req block, throw an exception
29
- puts "[ERROR] Requirement block title missing"
30
- end
31
-
32
- alt = %(
33
- <div class=\"panel panel-primary\">
34
- <div class=\"panel-heading\">
35
- <h3 class=\"panel-title\">
36
- <a class=\"anchor\" href=\"##{id}\"></a>
37
- <a class=\"link\" href=\"##{id}\"><emphasis role=\"strong\">Requirement: #{id}:</emphasis> #{san_title} </a> (ver. #{attrs['version']})
38
- </h3>
39
- </div>
40
- <div class=\"panel-body\">)
41
-
42
- close = "</div></div>"
43
-
44
- # concatenate all generated lines and prepend before the original content
45
- concat_lines = reader.lines.unshift(pass, alt, pass, nl)
46
- concat_lines.push(nl, pass, close, pass)
47
-
48
- create_block parent, :admonition, concat_lines, attrs, content_model: :compound
5
+ # @example Delimited Requirement Block with Title
6
+ # .My Requirement
7
+ # [req,id=RA-1,version=1]
8
+ # --
9
+ # Contents of the requirement
10
+ # --
11
+ class RequirementBlock < Extensions::BlockProcessor
12
+ use_dsl
13
+ named :req
14
+ on_contexts :open, :paragraph, :example, :listing, :sidebar, :pass
15
+ name_positional_attributes "number", "version"
16
+
17
+ # Read the parent attributes and create a Requirement Admonition block
18
+ def process parent, reader, attrs
19
+ # Add pass characters here to prevent html character replacements for < > tags
20
+ pass = "+++"
21
+ attrs["name"] = "requirement"
22
+ attrs["caption"] = "Requirement: "
23
+ id = attrs["id"]
24
+ nl = ""
25
+
26
+ begin
27
+ # downcase the title and replace spaces with underscores.
28
+ # @todo use utility methods here?
29
+ downcased_title = attrs["title"].downcase.tr(" ", "_").gsub('"', "&quot;")
30
+ san_title = attrs["title"].gsub(/&/, "&amp;")
31
+ rescue Exception => msg
32
+ # puts msg
33
+ # If no title exists on the Req block, throw an exception
34
+ warn %(asciidoctor: WARNING: Requirement block title missing)
49
35
  end
36
+
37
+ alt = %(
38
+ <div class=\"panel panel-primary\">
39
+ <div class=\"panel-heading\">
40
+ <h3 class=\"panel-title\">
41
+ <a class=\"anchor\" href=\"##{id}\"></a>
42
+ <a class=\"link\" href=\"##{id}\"><emphasis role=\"strong\">Requirement: #{id}:</emphasis> #{san_title} </a> (ver. #{attrs['version']})
43
+ </h3>
44
+ </div>
45
+ <div class=\"panel-body\">)
46
+
47
+ close = "</div></div>"
48
+
49
+ # concatenate all generated lines and prepend before the original content
50
+ concat_lines = reader.lines.unshift(pass, alt, pass, nl)
51
+ concat_lines.push(nl, pass, close, pass)
52
+
53
+ # @todo use a regular pass block in this instance
54
+ create_block parent, :admonition, concat_lines, attrs, content_model: :compound
50
55
  end
51
56
  end
@@ -0,0 +1,28 @@
1
+ require 'asciidoctor'
2
+ require 'asciidoctor/extensions'
3
+ require_relative 'utils/req_macro_walker'
4
+
5
+ include ::Asciidoctor
6
+
7
+ # @example Requirement Block Macro Use
8
+ # requirements::[]
9
+ class RequirementsBlockMacro < Extensions::BlockMacroProcessor
10
+ use_dsl
11
+ named :requirements
12
+
13
+ # Read the parent attributes and create a list of requirements in an appendix style
14
+ def process(parent, target, attrs)
15
+ rows = Reqs.list_reqs
16
+ content = %(<h2 id="requirements"><a class="anchor" href="#requirements"></a><a class="link" href="#requirements">Requirements</a></h2>
17
+ <div class="panel panel-default"> <div class="panel-heading"><h4>Requirements</h4></div>
18
+ <table class="table"> <thead> <tr>
19
+ <th>#</th> <th>ID</th><th>Version</th> <th>Title</th> <th>Document</th>
20
+ </tr> </thead>
21
+ <tbody>
22
+ #{rows.join}
23
+ </tbody>
24
+ </table> </div>)
25
+
26
+ create_pass_block parent, content, {}
27
+ end
28
+ end
@@ -2,11 +2,18 @@ require "asciidoctor/extensions" unless RUBY_ENGINE == "opal"
2
2
 
3
3
  include ::Asciidoctor
4
4
 
5
+ # @example A delimited TODO block
6
+ # [TODO]
7
+ # --
8
+ # Don't Forget!
9
+ # --
5
10
  class TodoBlock < Extensions::BlockProcessor
6
11
  use_dsl
7
12
  named :TODO
8
13
  on_contexts :open, :paragraph, :example, :listing, :sidebar, :pass
9
14
 
15
+ # Read the parent attributes and create a TODO
16
+ # admonition block
10
17
  def process parent, reader, attrs
11
18
  attrs['name'] = 'todo'
12
19
  attrs['caption'] = 'Todo'
@@ -1,5 +1,13 @@
1
+ # Helper methods handling whether to output inline content or a block.
2
+ # Will read the attributes of the current macro and output a HTML string that is either
3
+ # inline or a block (float-right).
1
4
  module Context
2
- def self.form(attributes, target, url, label)
5
+ # @param attributes [Array] attributes passed by the inline macro
6
+ # @param target [String] the target text
7
+ # @param url [String] the target url
8
+ # @param label [String] an optional status label, used to display if a task/issue is open or closed
9
+ # @return [String] the raw HTML to be included in the target document
10
+ def self.format(attributes, target, url, label)
3
11
  block = false
4
12
  block = true if attributes.key? "block"
5
13
 
@@ -1,4 +1,9 @@
1
+ # A simple helper method handles the status of the target text.
2
+ # This is used to display whether a GitHub issue or a Jira ticket
3
+ # is open or closed etc.
1
4
  module Labels
5
+ # @param attrs [Array] attributes passed by the inline macro
6
+ # @return [String] the status and/or label to be displayed
2
7
  def self.getstatus(attrs)
3
8
  status = attrs["status"]
4
9
  if status == ("done" || "closed")
@@ -0,0 +1,110 @@
1
+ # Special handling for linking to Requirements.
2
+ # These are mainly used by the Requirement Appendix (requirement_block_macro)
3
+ #
4
+ module Reqs
5
+ # Recursively globs all files with the .adoc extension and matches cross-references
6
+ # to Requirements. The special handling here is that we detect if the target
7
+ # requirement is commented, in a source block or included.
8
+ #
9
+ # @return [Array] An array of the IDs and paths to requirements in generated HTML files
10
+ def self.list_reqs
11
+ # @todo This should be configurable, or at least not hardcoded
12
+ exts = "(\.adoc|\.md|\.html)"
13
+ docsdir = '_docs'
14
+
15
+ title = nil
16
+ chapter = nil
17
+ doctitle = nil
18
+
19
+ reqs = []
20
+ rows = []
21
+ # For commented requirements
22
+ coms = []
23
+ # For includes
24
+ inc_reqs = []
25
+ incs = []
26
+
27
+ commentblockrx = '/^\/{4,}$/'
28
+ commentlinerx = '/^//(?=[^/]|$)/'
29
+
30
+ # @todo Already defined in Xref util?
31
+ def trim(s)
32
+ s.gsub!(/_docs\//, '')
33
+ s.gsub!(/(\.adoc|\.md|\.html)/, '')
34
+ end
35
+
36
+ # @todo Dont do this? Find a better way of handling all source adoc files.
37
+ adoc_files = Dir.glob('**/*.adoc')
38
+
39
+ adoc_files.each do |f|
40
+ inc = false
41
+ commented = false
42
+
43
+ File.read(f).each_line do |li|
44
+ incommentblock ^= true if li[commentblockrx]
45
+ commented = true if li[commentlinerx]
46
+ inc = true if li[/published: false/]
47
+
48
+ doctitle = /(?<=title:\s).+/.match(li) if li[/^title:\s+\w.+/]
49
+ chapter = /(?<=chapter:\s).+/.match(li) if li[/^chapter:\s+\w.+/]
50
+
51
+ if li[/^\[\s*req\s*,\s*id\s*=\s*\w+-?[0-9]+\s*,.*/]
52
+ title.sub!(/^\./, '')
53
+ req = [li.chop, f, title, chapter, doctitle]
54
+
55
+ if commented || incommentblock
56
+ coms.push(req)
57
+ elsif inc
58
+ inc_reqs.push(req)
59
+ else
60
+ reqs.push(req)
61
+ end
62
+
63
+ # Collect all includes
64
+ elsif li[/^include::.+.adoc\[\]/]
65
+
66
+ inc_file = li.chop.match(/(?<=^include::).+.adoc(?=\[\])/i).to_s
67
+ path = inc_file.sub(/^#{docsdir}\//, '')
68
+ path = path.sub(/#{exts}/, '')
69
+ parent = f
70
+ item = [inc_file, path, parent]
71
+ incs.push item
72
+
73
+ end
74
+ title = li
75
+ end
76
+ end
77
+
78
+ # Sort included reqs and correct the path to the parent (including doc)
79
+ # Push this back into 'normal' requirements array for regular processing
80
+ inc_reqs.each do |l, f, title, chapter, doctitle|
81
+ incs.each do |incfile, _incpath, parent|
82
+ if f == incfile
83
+ item = [l, parent, title, chapter, doctitle]
84
+ reqs.push item
85
+ end
86
+ end
87
+ end
88
+
89
+ # Remove dupes
90
+ reqs.uniq!
91
+
92
+ i = 0
93
+ reqs.each do |req, f, title, chapter, doctitle|
94
+ i += 1
95
+
96
+ id = /[^,]*\s*id\s*=\s*(\w+-?[0-9]+)\s*,.*/.match(req)[1]
97
+ version = /(?<=version=)\d+/.match(req)
98
+
99
+ f.gsub!(/^_docs\//, '')
100
+ f.gsub!(/.adoc$/, '')
101
+
102
+ link = "#{f}/index##{id}"
103
+ ref = "<a class=\"link\" href=\"#{link}\"><emphasis role=\"strong\">#{title}</emphasis> </a>"
104
+ breadcrumb = "<a href=\"#{f}\">#{chapter} / #{doctitle}</a>"
105
+ row = "<tr> <th scope=\"row\">#{i}</th> <td>#{id}</td><td>#{version}</td> <td>#{ref}</td> <td>#{breadcrumb}</td> </tr>"
106
+
107
+ rows.push(row)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,122 @@
1
+ # Helper methods for common xref processing.
2
+ #
3
+ module Xrefs
4
+ # Trims a path of a given source document to exclude the docs directory
5
+ # and file extension. This is used to calculate the target directory
6
+ # of generated HTML files given default permalink settings.
7
+ #
8
+ # @param path [String] the path of the source document relative to the project root
9
+ # @return [String] the formatted path
10
+ def self.trim(path)
11
+ trimmed = path.gsub(/_docs\//, '')
12
+ trimmed.gsub(/(\.adoc|\.md|\.html)/, '')
13
+ end
14
+
15
+ # Formats a string to be permalink-friendly. This simply
16
+ # downcases the string an substitutes spaces with hyphens. This is
17
+ # typically used for section titles and document titles to generate
18
+ # a cross-reference to an anchor.
19
+ #
20
+ # @param path [String] the path of the source document relative to the project root
21
+ # @return [String] the formatted path
22
+ def self.targetify(path)
23
+ path.downcase.gsub(/\s/, '-')
24
+ end
25
+
26
+ # Recursively globs all files with the .adoc extension and matches cross-references,
27
+ # section titles and anchors. Cross-references to Requirements are excluded and handled
28
+ # in their own processor as they are a special case (i.e., there is no built-in support).
29
+ #
30
+ # @return [String] the formatted path
31
+ def self.list_xrefs
32
+
33
+ # @todo Maybe don't do this. Find a better way to process
34
+ # all .adoc files before extensions are loaded.
35
+ adoc_files = Dir.glob('**/*.adoc')
36
+
37
+ # Make some arrays available
38
+ titles = []
39
+ anchors = []
40
+ xrefs = []
41
+ mismatches = []
42
+
43
+ replacement = ''
44
+
45
+ adoc_files.each do |file_name|
46
+ lc = 0
47
+
48
+ File.read(file_name).each_line do |li|
49
+ lc += 1
50
+
51
+ # @note Matches all <<xrefs>> except Requirements
52
+ if li[/\<\<(?!Req)(.+?)\>\>/]
53
+
54
+ text = ''
55
+ target = ''
56
+ path = trim(file_name)
57
+ xref = li.chop.match(/\<\<(?!Req)(\S.+?)\>\>/i).captures[0].to_s
58
+
59
+ # @note Checks if the xref has display text, i.e. '<<title-1,Lovely Display Text>>'
60
+ if xref[/,/]
61
+ # @todo Use helper methods.
62
+ target = xref.downcase.gsub(/,.+/, '').gsub(/\s/, '-')
63
+ text = xref.gsub(/.+,/, '').lstrip!
64
+ xref = xref.sub(/,.+/, '')
65
+ path = file_name
66
+ else
67
+ # @todo Use helper methods.
68
+ target = xref.downcase.gsub(/\s/, '-')
69
+ text = xref
70
+ end
71
+
72
+ item = [xref, path, file_name, text, target]
73
+ xrefs.push item
74
+
75
+ # Match .Titles and = Section Titles
76
+ elsif li[/(^(\.\S\w+)|^(\=+\s+?\S+.+))/]
77
+
78
+ # Add check if none found (captures nil)
79
+ title = li.chop.match(/(?!=+\s)(\S+.+?)$/i).captures[0]
80
+ title.sub(/\.(?=\w+?)/, '') if title[/\.(?=\w+?)/]
81
+ path = trim(file_name)
82
+ item = [title, path, file_name]
83
+ titles.push item
84
+
85
+ # Match [[anchors]]
86
+ elsif li[/\[\[.+?\]\]/]
87
+
88
+ # Add check if none found (captures nil)
89
+ anchor = li.chop.match(/(?<=\[\[).+?(?=\]\])/).to_s
90
+
91
+ if anchor[/,/]
92
+ anchor = anchor.match(/(?<=\[\[)(?:|[\w+?_:][\w+?:.-]*)(?=,.+?\]\])/).to_s
93
+ text = anchor.sub(/.+?,/, '')
94
+ text = text.sub(/\]\]$/, '')
95
+ end
96
+
97
+ path = trim(file_name)
98
+ item = [anchor, path, file_name, text]
99
+ # for the moment, just handle anchors similar to titles
100
+ titles.push item
101
+
102
+ end
103
+ end
104
+ end
105
+
106
+ # Run through each xref and check for matching titles
107
+ xrefs.each do |xref, xpath, xfile, xtext, xtarget|
108
+ # check xrefs against titles
109
+ titles.each do |ttext, tpath, tfile, _tdisp|
110
+
111
+ next unless ttext == xref
112
+ tpath = 'index' if tpath.to_s.empty?
113
+ # If the paths are not the same (xref and title not the same document) do the following
114
+ next unless tpath != xpath
115
+
116
+ xtform = targetify(xtarget)
117
+ detail = [xref, xtarget, xtext, xpath, xfile, ttext, tpath, tfile, xtform]
118
+ mismatches.push detail
119
+ end
120
+ end
121
+ end
122
+ end
@@ -1,3 +1,6 @@
1
+ # Use this to set global versioning for the RubyGem
1
2
  module JekyllAspec
2
- VERSION = "1.0.0-alpha"
3
+ # After updating the version, publishing can be done by running
4
+ # rake release in the project root
5
+ VERSION = "1.0.0"
3
6
  end
data/lib/jekyll_aspec.rb CHANGED
@@ -6,12 +6,14 @@ require_relative "extensions/inline_repo_macro"
6
6
  require_relative "extensions/inline_task_macro"
7
7
  require_relative "extensions/req_preprocessor"
8
8
  require_relative "extensions/req_refs"
9
- require_relative "extensions/requirement_appendix"
10
- require_relative "extensions/requirement_block"
9
+ require_relative "extensions/requirement_block_macro"
11
10
  require_relative "extensions/todo_block"
12
11
 
13
12
  require "jekyll_aspec/version"
14
13
 
14
+ # Load Asciidoctor extensions
15
15
  Extensions.register do
16
16
  block TodoBlock
17
+ block RequirementBlock
18
+ block_macro RequirementsBlockMacro
17
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll_aspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.alpha
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bsmith-n4
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-25 00:00:00.000000000 Z
11
+ date: 2017-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,7 +66,11 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.5.0
69
- description:
69
+ description: "This plugin is a group of Asciidoctor extensions that perform directory
70
+ walking, \n resolving the location of titles and anchors
71
+ in all adoc files so that inter-document\n cross-references
72
+ in a Jekyll project are resolved automatically. Also included are some \n custom
73
+ macros and blocks that are useful for techinical writing."
70
74
  email:
71
75
  - brian.smith@numberfour.eu
72
76
  executables: []
@@ -78,10 +82,13 @@ files:
78
82
  - Gemfile
79
83
  - Gemfile.lock
80
84
  - LICENSE.txt
81
- - README.md
85
+ - README.adoc
82
86
  - Rakefile
83
87
  - bin/console
84
88
  - bin/setup
89
+ - docs/inline-task.adoc
90
+ - docs/requirement-block.adoc
91
+ - docs/todo-block.adoc
85
92
  - jekyll_aspec.gemspec
86
93
  - lib/extensions/autoxrefs.rb
87
94
  - lib/extensions/html_postprocessor.rb
@@ -92,11 +99,13 @@ files:
92
99
  - lib/extensions/replace_regex.rb
93
100
  - lib/extensions/req_preprocessor.rb
94
101
  - lib/extensions/req_refs.rb
95
- - lib/extensions/requirement_appendix.rb
96
102
  - lib/extensions/requirement_block.rb
103
+ - lib/extensions/requirement_block_macro.rb
97
104
  - lib/extensions/todo_block.rb
98
105
  - lib/extensions/utils/block.rb
99
106
  - lib/extensions/utils/labels.rb
107
+ - lib/extensions/utils/req_macro_walker.rb
108
+ - lib/extensions/utils/xref_helper.rb
100
109
  - lib/jekyll_aspec.rb
101
110
  - lib/jekyll_aspec/version.rb
102
111
  homepage: https://github.com/bsmith-n4/jekyll_aspec
@@ -111,12 +120,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
120
  requirements:
112
121
  - - ">="
113
122
  - !ruby/object:Gem::Version
114
- version: '0'
123
+ version: '2.0'
115
124
  required_rubygems_version: !ruby/object:Gem::Requirement
116
125
  requirements:
117
- - - ">"
126
+ - - ">="
118
127
  - !ruby/object:Gem::Version
119
- version: 1.3.1
128
+ version: '0'
120
129
  requirements: []
121
130
  rubyforge_project:
122
131
  rubygems_version: 2.6.12
data/README.md DELETED
@@ -1,30 +0,0 @@
1
- # Jekyll Aspec
2
-
3
- [![Build Status](https://travis-ci.org/bsmith-n4/jekyll_aspec.svg?branch=master)](https://travis-ci.org/bsmith-n4/jekyll_aspec)
4
-
5
- A selection of Asciidoctor extensions designed to used with Jekyll.
6
-
7
- These extensions add custom blocks for Requirements, Definitions and inter-document auto-linking functionality for these blocks.
8
-
9
- ## Installation
10
-
11
- Add `jekyll_aspec` to your Jekyll Gemfile:
12
-
13
- ```ruby
14
- group :jekyll_plugins do
15
- gem 'jekyll-asciidoc'
16
- gem 'jekyll_aspec'
17
- end
18
- ```
19
-
20
- Or install it yourself as:
21
-
22
- $ gem install jekyll_aspec
23
-
24
- ## Contributing
25
-
26
- Bug reports and pull requests are welcome on GitHub at https://github.com/bsmith-n4/jekyll_aspec.
27
-
28
- ## License
29
-
30
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,115 +0,0 @@
1
- require 'asciidoctor'
2
- require 'asciidoctor/extensions'
3
-
4
- exts = "(\.adoc|\.md|\.html)"
5
- docsdir = '_docs'
6
-
7
- title = nil
8
- chapter = nil
9
- doctitle = nil
10
-
11
- reqs = []
12
- rows = []
13
- # For commented requirements
14
- coms = []
15
-
16
- # For includes
17
- inc_reqs = []
18
- incs = []
19
-
20
- CommentBlockRx = %r(^\/{4,}$)
21
- CommentLineRx = %r{^//(?=[^/]|$)}
22
-
23
- def trim(s)
24
- s.gsub!(/_docs\//, '')
25
- s.gsub!(/(\.adoc|\.md|\.html)/, '')
26
- end
27
-
28
- adoc_files = Dir.glob('**/*.adoc')
29
- adoc_files.each do |f|
30
- inc = false
31
- commented = false
32
-
33
- File.read(f).each_line do |li|
34
- incommentblock ^= true if li[CommentBlockRx]
35
- commented = true if li[CommentLineRx]
36
- inc = true if li[/published: false/]
37
-
38
- doctitle = /(?<=title:\s).+/.match(li) if li[/^title:\s+\w.+/]
39
- chapter = /(?<=chapter:\s).+/.match(li) if li[/^chapter:\s+\w.+/]
40
-
41
- if li[/\[\s*req\s*,\s*id\s*=\s*\w+-?[0-9]+\s*,.*/]
42
- title.sub!(/^\./, '')
43
- req = [li.chop, f, title, chapter, doctitle]
44
-
45
- if commented || incommentblock
46
- coms.push(req)
47
- elsif inc
48
- inc_reqs.push(req)
49
- else
50
- reqs.push(req)
51
- end
52
-
53
- # Collect all includes
54
- elsif li[/^include::.+.adoc\[\]/]
55
-
56
- inc_file = li.chop.match(/(?<=^include::).+.adoc(?=\[\])/i).to_s
57
- path = inc_file.sub(/^#{docsdir}\//, '')
58
- path = path.sub(/#{exts}/, '')
59
- parent = f
60
- item = [inc_file, path, parent]
61
- incs.push item
62
-
63
- end
64
- title = li
65
- end
66
- end
67
-
68
- # Sort included reqs and correct the path to the parent (including doc)
69
- # Push this back into 'normal' requirements array for regular processing
70
- inc_reqs.each do |l, f, title, chapter, doctitle|
71
- incs.each do |incfile, _incpath, parent|
72
- if f == incfile
73
- item = [l, parent, title, chapter, doctitle]
74
- reqs.push item
75
- end
76
- end
77
- end
78
-
79
- reqs.uniq!
80
-
81
- i = 0
82
- reqs.each do |req, f, title, chapter, doctitle|
83
- i += 1
84
-
85
- id = /[^,]*\s*id\s*=\s*(\w+-?[0-9]+)\s*,.*/.match(req)[1]
86
- version = /(?<=version=)\d+/.match(req)
87
-
88
- f.gsub!(/^_docs\//, '')
89
- f.gsub!(/.adoc$/, '')
90
-
91
- link = "#{f}/index##{id}"
92
- ref = "<a class=\"link\" href=\"#{link}\"><emphasis role=\"strong\">#{title}</emphasis> </a>"
93
- breadcrumb = "<a href=\"#{f}\">#{chapter} / #{doctitle}</a>"
94
- row = "<tr> <th scope=\"row\">#{i}</th> <td>#{id}</td><td>#{version}</td> <td>#{ref}</td> <td>#{breadcrumb}</td> </tr>"
95
-
96
- rows.push(row)
97
- end
98
-
99
- Asciidoctor::Extensions.register do
100
- block_macro :requirements do
101
- process do |parent, _target, _attrs|
102
- content = %(<h2 id="requirements"><a class="anchor" href="#requirements"></a><a class="link" href="#requirements">Requirements</a></h2>
103
- <div class="panel panel-default"> <div class="panel-heading"><h4>Requirements</h4></div>
104
- <table class="table"> <thead> <tr>
105
- <th>#</th> <th>ID</th><th>Version</th> <th>Title</th> <th>Document</th>
106
- </tr> </thead>
107
- <tbody>
108
- #{rows.join}
109
- </tbody>
110
- </table> </div>)
111
-
112
- create_pass_block parent, content, {}
113
- end
114
- end
115
- end