gem_docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/gem_docs/badge.rb +102 -0
- data/lib/gem_docs/config.rb +48 -0
- data/lib/gem_docs/emacs.rb +37 -0
- data/lib/gem_docs/header.rb +57 -0
- data/lib/gem_docs/overview.rb +98 -0
- data/lib/gem_docs/repo.rb +161 -0
- data/lib/gem_docs/skeleton.rb +54 -0
- data/lib/gem_docs/tasks.rb +81 -0
- data/lib/gem_docs/version.rb +5 -0
- data/lib/gem_docs/yard.rb +42 -0
- data/lib/gem_docs.rb +264 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a07d76db2fbb75383d41735198a4cbf7b1d2b6515653df77e21e8c9575a40ade
|
|
4
|
+
data.tar.gz: 65e3554324e7c91e8466efba2d032c53d8549e64a7a460cce4ee4d7322fffa40
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0d9090c6ac05d0d80fb556019bb56c57fbcdd3e1b691e1b38d5b54da8bca5923c00dae9a6e18f616bad189b3c61c981f5d341294c0ccb46e1afde7274569452c
|
|
7
|
+
data.tar.gz: 86a7c664a77405127a46e03a2233cc20958613965ca6bd3beba2a0ca1ae342192f835a26d22d18ad6f045a6d9f79eb13934942fe0f4baab4a11d17adbc9400dd
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
module Badge
|
|
5
|
+
GITHUB_BADGE_RE = %r{actions/workflows.*badge.svg}
|
|
6
|
+
GITLAB_BADGE_RE = %r{badges/.*pipeline.svg}
|
|
7
|
+
|
|
8
|
+
Badge = Struct.new(:name, :marker, :org_block, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
def self.ensure!
|
|
11
|
+
repo = Repo.from_gemspec
|
|
12
|
+
return false if repo.workflow.to_s.match?(/\A\s*\z/)
|
|
13
|
+
|
|
14
|
+
badge = make_badge(repo)
|
|
15
|
+
ensure_badge!(badge, repo)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def make_badge(repo)
|
|
22
|
+
repo = Repo.from_gemspec
|
|
23
|
+
org_block =
|
|
24
|
+
GemDocs.config.badge
|
|
25
|
+
.gsub('%n', repo.name)
|
|
26
|
+
.gsub('%h', repo.host)
|
|
27
|
+
.gsub('%u', repo.user)
|
|
28
|
+
.gsub('%r', repo.root)
|
|
29
|
+
.gsub('%b', repo.branch)
|
|
30
|
+
.gsub('%w', repo.workflow)
|
|
31
|
+
Badge.new(
|
|
32
|
+
name: 'GitHub Actions',
|
|
33
|
+
marker: '#badge',
|
|
34
|
+
org_block: org_block,
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Write the badge block to the README unless it's already there. Replace
|
|
39
|
+
# the #badge marker if present, otherwise add after TITLE.
|
|
40
|
+
def ensure_badge!(badge, repo)
|
|
41
|
+
content = File.read(README_ORG)
|
|
42
|
+
updated =
|
|
43
|
+
if content.lines.find { |l| l.match?(/\A\s*#{Regexp.quote(badge.marker)}/) }
|
|
44
|
+
insert_at_marker(badge.marker, content, badge.org_block)
|
|
45
|
+
elsif (repo.host.include?('github') && content.match?(GITHUB_BADGE_RE)) ||
|
|
46
|
+
(repo.host.include?('gitlab') && content.match?(GITLAB_BADGE_RE))
|
|
47
|
+
# Do nothing and return nil to indicate badge present
|
|
48
|
+
return
|
|
49
|
+
else
|
|
50
|
+
insert_after_header(content, badge.org_block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
File.write(README_ORG, updated)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Insert the badge block after the org header lines, if any. If there are
|
|
57
|
+
# no header lines, insert at the beginning of the file.
|
|
58
|
+
def insert_after_header(content, block)
|
|
59
|
+
lines = content.lines
|
|
60
|
+
out_lines = +''
|
|
61
|
+
|
|
62
|
+
in_header = lines.any? { |l| l.match?(/\A\s*\#\+/) }
|
|
63
|
+
block_added = false
|
|
64
|
+
lines.each do |line|
|
|
65
|
+
out_lines <<
|
|
66
|
+
if in_header && line.match?(/\A\s*\#\+/)
|
|
67
|
+
line
|
|
68
|
+
elsif in_header && !line.match?(/\A\s*\#\+/)
|
|
69
|
+
in_header = false
|
|
70
|
+
block_added = true
|
|
71
|
+
if line.match?(/\A\s*\z/)
|
|
72
|
+
line + block + "\n\n"
|
|
73
|
+
else
|
|
74
|
+
"\n" + block + "\n\n" + line
|
|
75
|
+
end
|
|
76
|
+
elsif !in_header && !block_added
|
|
77
|
+
block_added = true
|
|
78
|
+
block + "\n\n" + line
|
|
79
|
+
else
|
|
80
|
+
line
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
out_lines
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def insert_at_marker(marker, content, block)
|
|
87
|
+
lines = content.lines
|
|
88
|
+
out_lines = +''
|
|
89
|
+
|
|
90
|
+
lines.each do |line|
|
|
91
|
+
out_lines <<
|
|
92
|
+
if line.match?(/^#{marker}/)
|
|
93
|
+
block
|
|
94
|
+
else
|
|
95
|
+
line
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
out_lines
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
class Config
|
|
5
|
+
attr_accessor :overview_headings
|
|
6
|
+
attr_accessor :headers
|
|
7
|
+
attr_accessor :repo_host
|
|
8
|
+
attr_accessor :repo_name
|
|
9
|
+
attr_accessor :repo_user
|
|
10
|
+
attr_accessor :repo_branch
|
|
11
|
+
attr_accessor :repo_workflow_dir
|
|
12
|
+
attr_accessor :repo_workflow_name
|
|
13
|
+
attr_accessor :badge
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
# Default: support org comment markers
|
|
17
|
+
@overview_headings = ["Introduction"]
|
|
18
|
+
@headers =
|
|
19
|
+
<<~HEADER
|
|
20
|
+
#+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
|
|
21
|
+
#+PROPERTY: header-args:ruby+ :wrap example :session %n_session :eval yes
|
|
22
|
+
#+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require '%n'"
|
|
23
|
+
#+PROPERTY: header-args:sh :exports code :eval no
|
|
24
|
+
#+PROPERTY: header-args:bash :exports code :eval no
|
|
25
|
+
HEADER
|
|
26
|
+
@repo_host = nil
|
|
27
|
+
@repo_name = nil
|
|
28
|
+
@repo_user = nil
|
|
29
|
+
@repo_branch = nil
|
|
30
|
+
@repo_workflow_dir = ".github/workflows"
|
|
31
|
+
@repo_workflow_name = nil
|
|
32
|
+
@badge =
|
|
33
|
+
<<~BADGE
|
|
34
|
+
#+BEGIN_EXPORT markdown
|
|
35
|
+
[](https://github.com/%u/%n/actions/workflows/%w)
|
|
36
|
+
#+END_EXPORT
|
|
37
|
+
BADGE
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.configure
|
|
42
|
+
yield(config)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.config
|
|
46
|
+
@config ||= Config.new
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
module Emacs
|
|
5
|
+
def self.tangle
|
|
6
|
+
# ensure_saved
|
|
7
|
+
expr = <<~ELISP
|
|
8
|
+
(save-window-excursion
|
|
9
|
+
(with-current-buffer (find-file-noselect "#{README_ORG}")
|
|
10
|
+
(save-buffer)
|
|
11
|
+
(require 'ob-ruby)
|
|
12
|
+
(org-babel-execute-buffer)
|
|
13
|
+
(save-buffer)
|
|
14
|
+
"OK"))
|
|
15
|
+
ELISP
|
|
16
|
+
|
|
17
|
+
if system("emacsclient", "--quiet", "--eval", expr)
|
|
18
|
+
FileUtils.touch(STAMP)
|
|
19
|
+
else
|
|
20
|
+
abort "Babel execution failed"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.export
|
|
25
|
+
# ensure_saved
|
|
26
|
+
expr = <<~ELISP
|
|
27
|
+
(save-window-excursion
|
|
28
|
+
(with-current-buffer (find-file-noselect "#{README_ORG}")
|
|
29
|
+
(save-buffer)
|
|
30
|
+
(require 'ox-gfm)
|
|
31
|
+
(org-gfm-export-to-markdown)))
|
|
32
|
+
ELISP
|
|
33
|
+
|
|
34
|
+
system("emacsclient", "--quiet", "--eval", expr)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
module Header
|
|
5
|
+
PROPERTY_RE = /^#\+PROPERTY:\s+header-args:ruby/
|
|
6
|
+
|
|
7
|
+
# @return String The overview from README per config
|
|
8
|
+
def self.write_header?
|
|
9
|
+
return false if present?
|
|
10
|
+
|
|
11
|
+
prelim, body = extract_prelim_body
|
|
12
|
+
new_org = prelim.join.strip + org_headers.strip + "\n\n" + body.join
|
|
13
|
+
File.write(README_ORG, new_org) > 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.present?
|
|
17
|
+
prelim = extract_prelim_body.first
|
|
18
|
+
prelim.any? { |h| h.match?(PROPERTY_RE) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Returns the preliminary comment, blank, and header lines from README.org
|
|
25
|
+
def extract_prelim_body
|
|
26
|
+
prelim = []
|
|
27
|
+
body = []
|
|
28
|
+
in_prelim = true
|
|
29
|
+
File.read(README_ORG).lines.each do |line|
|
|
30
|
+
if in_prelim && line.match?(/^\s*[^#\n]+\s*$/)
|
|
31
|
+
in_prelim = false
|
|
32
|
+
body << line
|
|
33
|
+
elsif in_prelim && (line.match(/^#/) || line.match(/^\s*$/))
|
|
34
|
+
prelim << line
|
|
35
|
+
# elsif in_prelim
|
|
36
|
+
# in_prelim = false
|
|
37
|
+
# body << line
|
|
38
|
+
else
|
|
39
|
+
body << line
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
[prelim, body]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def org_headers
|
|
46
|
+
repo = Repo.from_gemspec
|
|
47
|
+
GemDocs.config.headers
|
|
48
|
+
.gsub('%n', repo.name)
|
|
49
|
+
.gsub('%h', repo.host)
|
|
50
|
+
.gsub('%u', repo.user)
|
|
51
|
+
.gsub('%r', repo.root)
|
|
52
|
+
.gsub('%b', repo.branch)
|
|
53
|
+
.gsub('%w', repo.workflow)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'strscan'
|
|
4
|
+
require 'debug'
|
|
5
|
+
|
|
6
|
+
module GemDocs
|
|
7
|
+
module Overview
|
|
8
|
+
BANNER = 'Gem Overview (extracted from README.org by gem_docs)'
|
|
9
|
+
|
|
10
|
+
# @return String The overview from README per config
|
|
11
|
+
def self.write_overview?
|
|
12
|
+
repo = Repo.from_gemspec
|
|
13
|
+
target = File.join("lib", "#{repo.name}.rb")
|
|
14
|
+
|
|
15
|
+
return false unless File.exist?(target)
|
|
16
|
+
|
|
17
|
+
overview = extract
|
|
18
|
+
return false unless overview
|
|
19
|
+
|
|
20
|
+
old_lib = File.read(target)
|
|
21
|
+
old_match = old_lib.match(overview_re)
|
|
22
|
+
old_comment = old_match.to_s
|
|
23
|
+
|
|
24
|
+
new_comment = <<~RUBY.chomp
|
|
25
|
+
# #{BANNER}
|
|
26
|
+
#
|
|
27
|
+
#{overview.lines.map { |l| l.match?(/\A\s*\z/) ? '#' : "# #{l.rstrip}" }.join("\n")}
|
|
28
|
+
RUBY
|
|
29
|
+
return false if old_comment == new_comment
|
|
30
|
+
|
|
31
|
+
new_lib =
|
|
32
|
+
if old_match
|
|
33
|
+
# There was an overview in the old_lib_content, so replace it with
|
|
34
|
+
# the new_lib_comment.
|
|
35
|
+
old_lib.sub(old_comment, new_comment)
|
|
36
|
+
else
|
|
37
|
+
scanner = StringScanner.new(old_lib)
|
|
38
|
+
scanner.scan_until(/^module #{repo.module_name}/)
|
|
39
|
+
scanner.pre_match + "\n" + new_comment + "\n" + scanner.matched + scanner.rest
|
|
40
|
+
end
|
|
41
|
+
File.write(target, new_lib) > 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Return a Regexp that capture any GemDocs-generated overview comment in
|
|
45
|
+
# the main module library file. The Regex requires the comment to come
|
|
46
|
+
# immediately before the `module <GemName>` line of the libary file, or it
|
|
47
|
+
# will not match.
|
|
48
|
+
def self.overview_re
|
|
49
|
+
heads = GemDocs.config.overview_headings
|
|
50
|
+
re_str = "#\\s*" + Regexp.quote(BANNER) + ".*"
|
|
51
|
+
heads.each do |h|
|
|
52
|
+
re_str << "\\*\\s+#{Regexp.escape(h)}.*"
|
|
53
|
+
end
|
|
54
|
+
repo = Repo.from_gemspec
|
|
55
|
+
re_str += "(?=\\n\\s*module\\s+#{repo.module_name})"
|
|
56
|
+
Regexp.new(re_str, Regexp::MULTILINE | Regexp::IGNORECASE)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.present?
|
|
60
|
+
repo = Repo.from_gemspec
|
|
61
|
+
target = File.join("lib", "#{repo.name}.rb")
|
|
62
|
+
|
|
63
|
+
return false unless File.exist?(target)
|
|
64
|
+
|
|
65
|
+
File.read(target).match?(overview_re)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class << self
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Extract the Overview from the concatenation of all the README.org
|
|
72
|
+
# top-level sections given in GemDocs.config.overview_headings.
|
|
73
|
+
def extract
|
|
74
|
+
text = File.read(README_ORG)
|
|
75
|
+
heads = GemDocs.config.overview_headings
|
|
76
|
+
return if heads.nil? || heads.empty?
|
|
77
|
+
|
|
78
|
+
result = +''
|
|
79
|
+
scanner = StringScanner.new(text)
|
|
80
|
+
heads.each do |h|
|
|
81
|
+
if scanner.scan_until(/\n(?<head>\s*\*\s+#{Regexp.escape(h)})[^\n]*\n/)
|
|
82
|
+
this_head = scanner.named_captures['head'] + "\n\n"
|
|
83
|
+
body_start = scanner.pos
|
|
84
|
+
body_end =
|
|
85
|
+
if scanner.scan_until(/\n^\s*\*[^\*\n]+/)
|
|
86
|
+
scanner.pos - scanner.matched.size
|
|
87
|
+
else
|
|
88
|
+
scanner.string.size - 1
|
|
89
|
+
end
|
|
90
|
+
scanner.pos = body_end
|
|
91
|
+
result << this_head + scanner.string[body_start..body_end]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
result
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
class Repo
|
|
5
|
+
attr_accessor :root, :host, :user, :name, :module_name
|
|
6
|
+
attr_accessor :branch, :workflow_dir, :workflow_name
|
|
7
|
+
|
|
8
|
+
def initialize(root: nil, host: nil,
|
|
9
|
+
user: nil,
|
|
10
|
+
name: nil,
|
|
11
|
+
module_name: nil,
|
|
12
|
+
branch: 'master',
|
|
13
|
+
workflow_dir: nil,
|
|
14
|
+
workflow_name: nil)
|
|
15
|
+
@root = root
|
|
16
|
+
@host = host
|
|
17
|
+
@user = user
|
|
18
|
+
@module_name = module_name
|
|
19
|
+
@name = name
|
|
20
|
+
@branch = branch
|
|
21
|
+
@workflow_dir = workflow_dir
|
|
22
|
+
@workflow_name = workflow_name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def workflow
|
|
26
|
+
workflow_name.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
def from_gemspec
|
|
31
|
+
spec = load_gemspec(gemspec_path)
|
|
32
|
+
|
|
33
|
+
url =
|
|
34
|
+
spec.metadata["source_code_uri"] ||
|
|
35
|
+
spec.metadata["homepage_uri"] ||
|
|
36
|
+
spec.homepage
|
|
37
|
+
|
|
38
|
+
abort "No repository URL found in gemspec metadata" unless url
|
|
39
|
+
|
|
40
|
+
# root = File.dirname(File.expand_path(path))
|
|
41
|
+
root = GemDocs.project_root
|
|
42
|
+
meta = parse_url(url)
|
|
43
|
+
name = GemDocs.config.repo_name ||
|
|
44
|
+
spec.name ||
|
|
45
|
+
meta[:name] ||
|
|
46
|
+
File.basename(Dir['*.gemspec'].first) ||
|
|
47
|
+
File.basename(root)
|
|
48
|
+
host = GemDocs.config.repo_host ||
|
|
49
|
+
meta[:host]
|
|
50
|
+
user = GemDocs.config.repo_user ||
|
|
51
|
+
meta[:user]
|
|
52
|
+
mname = to_module(name)
|
|
53
|
+
branch = GemDocs.config.repo_branch ||
|
|
54
|
+
repo_default_branch(root:)
|
|
55
|
+
wdir, wname = workflow_dir_name
|
|
56
|
+
new(
|
|
57
|
+
root: root,
|
|
58
|
+
host: host,
|
|
59
|
+
user: user,
|
|
60
|
+
name: name,
|
|
61
|
+
module_name: mname,
|
|
62
|
+
branch: branch,
|
|
63
|
+
workflow_dir: wdir,
|
|
64
|
+
workflow_name: wname,
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def workflow_dir_name
|
|
69
|
+
if GemDocs.config.repo_workflow_name && GemDocs.config.repo_workflow_dir
|
|
70
|
+
workflow_file = File.join(
|
|
71
|
+
GemDocs.project_root,
|
|
72
|
+
GemDocs.config.repo_workflow_dir,
|
|
73
|
+
GemDocs.config.repo_workflow_name,
|
|
74
|
+
)
|
|
75
|
+
return [File.dirname(workflow_file), File.dirname(workflow_file)] if File.readable?(workflow_file)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
dir = File.join(GemDocs.project_root, ".github/workflows")
|
|
79
|
+
return unless Dir.exist?(dir)
|
|
80
|
+
|
|
81
|
+
workflows =
|
|
82
|
+
Dir.children(dir)
|
|
83
|
+
.select { |f| f.match?(/\A.+\.ya?ml\z/) }
|
|
84
|
+
.sort
|
|
85
|
+
return if workflows.empty?
|
|
86
|
+
|
|
87
|
+
fname = workflows.find { |f| f =~ /\A[A-Za-z][^\.]*\.ya?ml\z/i } || workflows.first
|
|
88
|
+
# File.join(dir, fname)
|
|
89
|
+
[dir, fname]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def to_module(name)
|
|
93
|
+
name.split(/[-_]/).map(&:capitalize).join('')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def repo_default_branch(root:)
|
|
99
|
+
return "master" unless git_repo?(root:)
|
|
100
|
+
|
|
101
|
+
default_branch_from_origin ||
|
|
102
|
+
fallback_branch ||
|
|
103
|
+
"master"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def fallback_branch
|
|
107
|
+
return unless git_available?
|
|
108
|
+
|
|
109
|
+
branches = %x[git branch --list 2>/dev/null]
|
|
110
|
+
.lines
|
|
111
|
+
.map { |l| l.sub("*", "").strip }
|
|
112
|
+
|
|
113
|
+
return "master" if branches.include?("master")
|
|
114
|
+
return "main" if branches.include?("main")
|
|
115
|
+
|
|
116
|
+
nil
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def default_branch_from_origin
|
|
120
|
+
return unless git_available?
|
|
121
|
+
|
|
122
|
+
ref = %x[git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null].strip
|
|
123
|
+
return if ref.empty?
|
|
124
|
+
|
|
125
|
+
ref.split("/").last
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def git_available?
|
|
129
|
+
system("git", "--version", out: File::NULL, err: File::NULL)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def git_repo?(root: nil)
|
|
133
|
+
File.directory?(File.join(root, ".git"))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Return {host: <git_host>, user: <user_name>, name: <repo_name> } by parsing the given url
|
|
137
|
+
def parse_url(url)
|
|
138
|
+
host_bases = ['github', 'gitlab']
|
|
139
|
+
md = url.match(%r{(?<host>(?:#{host_bases.join('|')})\.com)/(?<user>[^/]+)/(?<repo_name>[^/]+)(?:\.git)?/?})
|
|
140
|
+
abort "Unsupported repository URL: #{url}" unless md
|
|
141
|
+
|
|
142
|
+
{ host: md[:host], user: md[:user], name: md[:repo_name] }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def gemspec_path
|
|
146
|
+
candidates = nil
|
|
147
|
+
Dir.chdir(GemDocs.project_root) do
|
|
148
|
+
candidates = Dir["*.gemspec"]
|
|
149
|
+
abort "No gemspec found" if candidates.empty?
|
|
150
|
+
abort "Multiple gemspecs found: #{candidates.join(', ')}" if candidates.size > 1
|
|
151
|
+
end
|
|
152
|
+
candidates.first
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def load_gemspec(path)
|
|
156
|
+
Gem::Specification.load(path) ||
|
|
157
|
+
abort("Failed to load gemspec: #{path}")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
module Skeleton
|
|
5
|
+
PROPERTY_RE = /^#\+PROPERTY:\s+header-args:ruby/
|
|
6
|
+
|
|
7
|
+
# @return String The overview from README per config
|
|
8
|
+
def self.make_readme?
|
|
9
|
+
return false if present?
|
|
10
|
+
|
|
11
|
+
repo = Repo.from_gemspec
|
|
12
|
+
content = <<~SKEL
|
|
13
|
+
#+TITLE: #{repo.module_name} Guide
|
|
14
|
+
|
|
15
|
+
* Introduction
|
|
16
|
+
|
|
17
|
+
* Installation
|
|
18
|
+
|
|
19
|
+
#+begin_src sh :eval no
|
|
20
|
+
bundle add #{repo.name}
|
|
21
|
+
#+end_src
|
|
22
|
+
|
|
23
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
24
|
+
|
|
25
|
+
#+begin_src sh :eval no
|
|
26
|
+
gem install #{repo.name}
|
|
27
|
+
#+end_src
|
|
28
|
+
|
|
29
|
+
* Usage
|
|
30
|
+
|
|
31
|
+
* Development
|
|
32
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
|
33
|
+
run `rake spec` to run the tests. You can also run `bin/console` for an
|
|
34
|
+
interactive prompt that will allow you to experiment.
|
|
35
|
+
|
|
36
|
+
To install this gem onto your local machine, run `bundle exec rake
|
|
37
|
+
install`.
|
|
38
|
+
|
|
39
|
+
* Contributing
|
|
40
|
+
Bug reports and pull requests are welcome on GitHub at
|
|
41
|
+
https://github.com/#{repo.user}/#{repo.name}.
|
|
42
|
+
|
|
43
|
+
* License
|
|
44
|
+
The gem is available as open source under the terms of the [MIT
|
|
45
|
+
License](https://opensource.org/licenses/MIT).
|
|
46
|
+
SKEL
|
|
47
|
+
File.write(README_ORG, content) > 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.present?
|
|
51
|
+
File.exist?(README_ORG)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
extend Rake::DSL
|
|
5
|
+
|
|
6
|
+
STAMP = ".tangle-stamp"
|
|
7
|
+
|
|
8
|
+
def self.install
|
|
9
|
+
extend Rake::DSL
|
|
10
|
+
|
|
11
|
+
# README.org → README.md when README.org is newer
|
|
12
|
+
file README_MD => README_ORG do
|
|
13
|
+
print "Exporting \"#{README_ORG}\" → "
|
|
14
|
+
GemDocs::Emacs.export
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Evaluate code blocks only when README.org changes
|
|
18
|
+
file STAMP => README_ORG do
|
|
19
|
+
print "Executing code blocks in #{README_ORG} ... "
|
|
20
|
+
GemDocs::Emacs.tangle
|
|
21
|
+
FileUtils.touch(STAMP)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
namespace :docs do
|
|
25
|
+
desc "Evaluate code blocks in README.org"
|
|
26
|
+
task :tangle => [:skeleton, STAMP]
|
|
27
|
+
|
|
28
|
+
desc "Export README.org → README.md"
|
|
29
|
+
task :export => [:badge, README_MD]
|
|
30
|
+
|
|
31
|
+
desc "Extract overview from README.org and embed in lib/<gem>.rb for ri/yard"
|
|
32
|
+
task :overview => [:skeleton, README_ORG] do
|
|
33
|
+
print "Embedding overview extracted from #{GemDocs::README_ORG} into main gem file ... "
|
|
34
|
+
if GemDocs::Overview.write_overview?
|
|
35
|
+
puts "added"
|
|
36
|
+
else
|
|
37
|
+
puts "already present"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "Create skeleton README.org if one does not exist"
|
|
42
|
+
task :skeleton do
|
|
43
|
+
if GemDocs::Skeleton.make_readme?
|
|
44
|
+
puts "README.org added"
|
|
45
|
+
else
|
|
46
|
+
puts "README.org already present"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc "Insert #+PROPERTY headers at top of README.org for code blocks"
|
|
51
|
+
task :header => :skeleton do
|
|
52
|
+
print "Inserting headers ... "
|
|
53
|
+
if GemDocs::Header.write_header?
|
|
54
|
+
puts "added"
|
|
55
|
+
else
|
|
56
|
+
puts "already present"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc "Generate YARD HTML documentation"
|
|
61
|
+
task :yard => [:overview] do
|
|
62
|
+
puts "Generating YARD documentation ... "
|
|
63
|
+
GemDocs::Yard.generate
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
desc "Ensure GitHub Actions badge exists in README.org"
|
|
67
|
+
task :badge => :skeleton do
|
|
68
|
+
print "Ensuring badges are in README.org ... "
|
|
69
|
+
|
|
70
|
+
if GemDocs::Badge.ensure!
|
|
71
|
+
puts "added"
|
|
72
|
+
else
|
|
73
|
+
puts "already present"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
desc "Run all documentation tasks (examples, readme, overview, yard, ri)"
|
|
78
|
+
task :all => [:skeleton, :header, :tangle, :export, :overview, :yard]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GemDocs
|
|
4
|
+
module Yard
|
|
5
|
+
# Generate HTML documentation via YARD
|
|
6
|
+
def self.generate(supress_out: false)
|
|
7
|
+
write_yardopts
|
|
8
|
+
Dir.chdir(GemDocs.project_root) do
|
|
9
|
+
redirect = supress_out ? '>/dev/null 2>&1' : ''
|
|
10
|
+
unless system("yard doc --no-private #{redirect}")
|
|
11
|
+
abort "Failed to generate YARD documentation"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# Path to the .yardopts file in the project root
|
|
20
|
+
def yardopts_path
|
|
21
|
+
File.expand_path(".yardopts", GemDocs.project_root)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Contents of .yardopts
|
|
25
|
+
def yardopts_contents
|
|
26
|
+
<<~YOPTS
|
|
27
|
+
--markup markdown
|
|
28
|
+
--output-dir doc
|
|
29
|
+
--readme README.md
|
|
30
|
+
lib/**/*.rb
|
|
31
|
+
YOPTS
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Write .yardopts only if needed
|
|
35
|
+
def write_yardopts
|
|
36
|
+
if !File.exist?(yardopts_path) || File.read(yardopts_path) != yardopts_contents
|
|
37
|
+
File.write(yardopts_path, yardopts_contents)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/gem_docs.rb
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# -*- mode: ruby -*-
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Gem Overview (auto-generated by gem_docs)
|
|
5
|
+
#
|
|
6
|
+
# * Usage
|
|
7
|
+
|
|
8
|
+
require "rake"
|
|
9
|
+
require "rake/dsl_definition"
|
|
10
|
+
require "fileutils"
|
|
11
|
+
|
|
12
|
+
# Gem Overview (extracted from README.org by gem_docs)
|
|
13
|
+
#
|
|
14
|
+
#
|
|
15
|
+
#
|
|
16
|
+
# * Overview
|
|
17
|
+
#
|
|
18
|
+
# One of the more onerous tasks when writing a ~gem~ or other ~github~ project
|
|
19
|
+
# is maintaining the documentation, and keeping it consistent and up-to-date.
|
|
20
|
+
# One of the better options for writing the ~README~ file that gets displayed by
|
|
21
|
+
# ~github~ as the main documentation is to write the file in ~org-mode~,
|
|
22
|
+
# ~README.org~ the export it to markdown ~README.md~ for display by ~github~.
|
|
23
|
+
#
|
|
24
|
+
# Doing so gives you access to ~org-mode~'s code blocks for writing example code
|
|
25
|
+
# to demonstrate the ~gem~ or other documents being presented. If you do so,
|
|
26
|
+
# ~github~ will render your ~README.org~ file in ~HTML~ and give you a credible
|
|
27
|
+
# result. However, ~github~ cannot handle some ~org-mode~ features, and in
|
|
28
|
+
# particular, it will not render ~#+RESULTS~ blocks showing the results of code
|
|
29
|
+
# block execution unless you wrap the results in something like a
|
|
30
|
+
# ~#+begin_example~ block and manually delete the ~#+RESULTS~ markers.
|
|
31
|
+
# Exporting to markdown eliminates that hassle.
|
|
32
|
+
#
|
|
33
|
+
# This gem contains ~rake~ tasks to facilitate the production of documentation
|
|
34
|
+
# in other gems.
|
|
35
|
+
#
|
|
36
|
+
# It provides tasks for:
|
|
37
|
+
#
|
|
38
|
+
# - ensuring that proper code block setup is included in ~README.org~ to
|
|
39
|
+
# facilitate running ~ruby~ code blocks in a session using the current version
|
|
40
|
+
# of the library,
|
|
41
|
+
# - running the code block examples in a ~README.org~ by invoking ~emacsclient~,
|
|
42
|
+
# - exporting ~README.org~ to Git-flavored markdown in ~README.md~
|
|
43
|
+
# - ensuring a workflow or ci badge is present in the ~README.md~
|
|
44
|
+
# - generating yard documents for your repo, and
|
|
45
|
+
# - copying the introductory contents of the README as a leading comment in your
|
|
46
|
+
# main gem library file so it gets picked up as an overview for ~ri~ and ~yri~
|
|
47
|
+
#
|
|
48
|
+
# * Usage
|
|
49
|
+
#
|
|
50
|
+
# ** Create a skeleton README.org file
|
|
51
|
+
# This is a simple task that creates a bare-bones ~README.org~ file to get
|
|
52
|
+
# started with. It does create the file with the name of the gem and other repo
|
|
53
|
+
# detains filled in. If there is already a README.org file, it does nothing.
|
|
54
|
+
#
|
|
55
|
+
# #+begin_src ruby :eval no
|
|
56
|
+
# rake docs:skeleton
|
|
57
|
+
# #+end_src
|
|
58
|
+
#
|
|
59
|
+
# ** Add proper ~#+PROPERTY~ headers in ~README.org~: ~rake docs:headers~
|
|
60
|
+
# **
|
|
61
|
+
# Getting emacs code blocks to render well in your ~README.org~ takes proper
|
|
62
|
+
# configuration of the code block headers in Emacs.
|
|
63
|
+
#
|
|
64
|
+
# #+begin_src ruby :eval no
|
|
65
|
+
# rake docs:headers
|
|
66
|
+
# #+end_src
|
|
67
|
+
#
|
|
68
|
+
# By default, the ~gem_docs~ ~rake docs:headers~ task will add the following
|
|
69
|
+
# headers to the top of your ~README.org~ file. It does nothing if any ruby
|
|
70
|
+
# header args are already present, so remove them if you want these installed.
|
|
71
|
+
#
|
|
72
|
+
# #+begin_example
|
|
73
|
+
# #+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
|
|
74
|
+
# #+PROPERTY: header-args:ruby+ :wrap example :session gem_docs_session
|
|
75
|
+
# #+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require '%n'"
|
|
76
|
+
# #+PROPERTY: header-args:sh :exports code :eval no
|
|
77
|
+
# #+PROPERTY: header-args:bash :exports code :eval no
|
|
78
|
+
# #+end_example
|
|
79
|
+
#
|
|
80
|
+
# Here's what the ruby headers buy you:
|
|
81
|
+
# - ~:results value~ :: the value of the last expression in the block is rendered
|
|
82
|
+
# as the results of code execution. If you want the output instead for a
|
|
83
|
+
# particular block, just add the block argument ~:results output~ to the code
|
|
84
|
+
# block.
|
|
85
|
+
# - ~:colnames no~ :: prevents org from processing the column headers in tables
|
|
86
|
+
# it renders. It is better for you to control column headers, and this
|
|
87
|
+
# setting allows this.
|
|
88
|
+
# - ~:hlines yes~ :: prevents org from stripping hlines from tables, which also
|
|
89
|
+
# allows you to control the insertion of hlines in tables.
|
|
90
|
+
# - ~:exports both~ :: causes both your code and the results of evaluation to be
|
|
91
|
+
# exported to the ~README.md~. Your example blocks should demonstrate the use
|
|
92
|
+
# of the gem, and the reader will want to see both the code and its results.
|
|
93
|
+
# - ~:dir "./"~ :: causes each code block to execute with your gem's root
|
|
94
|
+
# directory as its current directory.
|
|
95
|
+
# - ~:wrap example~ :: this wraps the result of code block evaluation in
|
|
96
|
+
# ~#+begin_example~ / ~#+end_example~ so that the results are displayed
|
|
97
|
+
# literally.
|
|
98
|
+
# - ~:session gem_docs_session~ :: causes the code blocks to execute in a
|
|
99
|
+
# continuous session, so that variables set up in one code block are
|
|
100
|
+
# accessible in later code blocks. Without this, you would have to build the
|
|
101
|
+
# code environment anew with each code block which obscures the readability of
|
|
102
|
+
# your ~README~. The session name is set to '<gem_name>_session'
|
|
103
|
+
# automatically, where <gem_name> is the name of your gem.
|
|
104
|
+
# - ~:prologue "$:.unshift('./lib') unless $:.first == './lib'; require 'gem_name'"~
|
|
105
|
+
# :: this prologue gets executed before each code block execution and ensures
|
|
106
|
+
# that the version of the gem library is you current development version;
|
|
107
|
+
# otherwise, your code blocks could be running a version from a prior
|
|
108
|
+
# installation of the gem. The 'gem_name' in the require is set to the name
|
|
109
|
+
# of your gem automatically.
|
|
110
|
+
#
|
|
111
|
+
# The ~docs:headers~ task also turns off evaluation of shell code blocks since
|
|
112
|
+
# these will often be such things as demonstrating the shell commands to install
|
|
113
|
+
# the gem, etc. Of course, you can override this for particular code blocks.
|
|
114
|
+
#
|
|
115
|
+
# Those headers are in fact what I am using in this ~README~, and here is how
|
|
116
|
+
# they work:
|
|
117
|
+
#
|
|
118
|
+
# #+begin_src ruby
|
|
119
|
+
# result = []
|
|
120
|
+
# result << ['N', 'exp(N)']
|
|
121
|
+
# result << nil
|
|
122
|
+
# 0.upto(10) do |n|
|
|
123
|
+
# result << [n/3.0, Math.exp(n/3.0)]
|
|
124
|
+
# end
|
|
125
|
+
# result
|
|
126
|
+
# #+end_src
|
|
127
|
+
#
|
|
128
|
+
# #+RESULTS:
|
|
129
|
+
# #+begin_example
|
|
130
|
+
# | N | exp(N) |
|
|
131
|
+
# |--------------------+--------------------|
|
|
132
|
+
# | 0.0 | 1.0 |
|
|
133
|
+
# | 0.3333333333333333 | 1.3956124250860895 |
|
|
134
|
+
# | 0.6666666666666666 | 1.9477340410546757 |
|
|
135
|
+
# | 1.0 | 2.718281828459045 |
|
|
136
|
+
# | 1.3333333333333333 | 3.7936678946831774 |
|
|
137
|
+
# | 1.6666666666666667 | 5.29449005047003 |
|
|
138
|
+
# | 2.0 | 7.38905609893065 |
|
|
139
|
+
# | 2.3333333333333335 | 10.312258501325767 |
|
|
140
|
+
# | 2.6666666666666665 | 14.391916095149892 |
|
|
141
|
+
# | 3.0 | 20.085536923187668 |
|
|
142
|
+
# | 3.3333333333333335 | 28.03162489452614 |
|
|
143
|
+
# #+end_example
|
|
144
|
+
#
|
|
145
|
+
# I built the table in the output by returning an array of arrays, which
|
|
146
|
+
# org-mode renders as a table in the output. Notice that I added an hline to
|
|
147
|
+
# the output by simply adding ~nil~ to the outer array where I wanted the hline
|
|
148
|
+
# to occur.
|
|
149
|
+
#
|
|
150
|
+
# Apart from all the convenient markup that ~org-mode~ allows, the ability to
|
|
151
|
+
# easily demonstrate your gem's code in this way is the real killer feature of
|
|
152
|
+
# writing your ~README~ in ~org-mode~ then exporting to markdown.
|
|
153
|
+
#
|
|
154
|
+
# ** Run the Code Blocks in README.org: ~rake docs:tangle~
|
|
155
|
+
# You can invoke ~emacsclient~ to run all the example code blocks in your
|
|
156
|
+
# ~README.org~ that are set for evaluation:
|
|
157
|
+
#
|
|
158
|
+
# Note that the ~tangle~ task relies on ~emacsclient~ to evaluate the code blocks in
|
|
159
|
+
# ~README.org~, so your Emacs ~init~ files should start [[info:emacs#Emacs Server][the Emacs server]] in
|
|
160
|
+
# order to work properly.
|
|
161
|
+
#
|
|
162
|
+
# I use the following snippet in my Emacs init file:
|
|
163
|
+
#
|
|
164
|
+
# #+begin_src emacs-lisp :eval no
|
|
165
|
+
# (require 'server)
|
|
166
|
+
# (unless (server-running-p)
|
|
167
|
+
# (message "Starting Emacs server")
|
|
168
|
+
# (server-start))
|
|
169
|
+
# #+end_src
|
|
170
|
+
#
|
|
171
|
+
# Then, you can evaluate all the code blocks in your ~README.org~ like this:
|
|
172
|
+
#
|
|
173
|
+
# #+begin_src ruby :eval no
|
|
174
|
+
# rake docs:tangle
|
|
175
|
+
# #+end_src
|
|
176
|
+
#
|
|
177
|
+
# ** Ensure that a Badge is Present in ~README.md~: ~rake docs:badge~
|
|
178
|
+
# It is reassuring to consumers of your gem that your gem passes its workflow
|
|
179
|
+
# tests on github. This task checks to see if a "badge" indicating success or
|
|
180
|
+
# failure is present and, if not, inserts one at the top of the ~README.org~
|
|
181
|
+
# such that it will get exported to ~README.md~ when =rake docs:export= is run.
|
|
182
|
+
#
|
|
183
|
+
# If you want to place the badge somewhere else in you ~README.org~, place the
|
|
184
|
+
# special comment ~#badge~ where you want the badge located and the task will
|
|
185
|
+
# place it there.
|
|
186
|
+
#
|
|
187
|
+
# If there is already a badge present, the task will not modify the ~README.org~
|
|
188
|
+
# file.
|
|
189
|
+
#
|
|
190
|
+
# ** Export ~README.org~ to ~README.md~: ~rake docs:export~
|
|
191
|
+
# You can write the ~README~ in Emacs org-mode, using all its features
|
|
192
|
+
# including the execution of code blocks, and then export to git-flavored
|
|
193
|
+
# markdown.
|
|
194
|
+
#
|
|
195
|
+
# If your repo contains both ~README.org~ and ~README.md~, github (and gitlab)
|
|
196
|
+
# will render the markdown version.
|
|
197
|
+
#
|
|
198
|
+
# Github renders markdown better than it renders org files, so this helps with
|
|
199
|
+
# the readability of the ~README~ on github. For example, if you write the
|
|
200
|
+
# ~README~ in org mode without exporting to markdown, ~github~ will not render
|
|
201
|
+
# the ~#+RESULTS~ blocks unless you manually delete the ~#+RESULTS~ tag from the
|
|
202
|
+
# output. This is tedious and error-prone, so it is best that you write the
|
|
203
|
+
# ~README~ in ~org-mode~ and export to ~markdown~. That's what this task
|
|
204
|
+
# enables.
|
|
205
|
+
#
|
|
206
|
+
# Also note that when ~github~ renders your ~README.md~, it automatically adds a
|
|
207
|
+
# table of contents, so putting one in the ~README.org~ file is redundant. If
|
|
208
|
+
# you want to have one for your own purposes, just set the ~:noexport~ tag on it
|
|
209
|
+
# so it doesn't get put into the ~README.md~
|
|
210
|
+
#
|
|
211
|
+
# #+begin_src ruby :eval no
|
|
212
|
+
# rake docs:export
|
|
213
|
+
# #+end_src
|
|
214
|
+
#
|
|
215
|
+
# ** Generate Yard Documents: ~rake docs:yard~
|
|
216
|
+
# This task generates a suitable ~.yardopts~ file if none exists and then
|
|
217
|
+
# generates ~yard~ documents into the gem's ~doc~ directory. It also makes sure
|
|
218
|
+
# that ~yard~ knows about your ~README.md~ file so user's of your gem will be
|
|
219
|
+
# able to get an overview of how to use your gem.
|
|
220
|
+
#
|
|
221
|
+
# #+begin_src ruby :eval no
|
|
222
|
+
# rake docs:yard
|
|
223
|
+
# #+end_src
|
|
224
|
+
#
|
|
225
|
+
# ** Generate an Overview Comment for the Main gem File: ~rake docs:overview~
|
|
226
|
+
# Gem's typically gather into a central library file all the require's and other
|
|
227
|
+
# setup needed for the gem and the file is given the same name as the gem. For
|
|
228
|
+
# example, this gem uses the file =lib/gem_docs.rb= for this purpose. Since
|
|
229
|
+
# this =lib= directory is placed in the user's =LOADPATH=, a =require
|
|
230
|
+
# 'gem_docs'= or =require '<gemname>'= effectively initializes the gem.
|
|
231
|
+
#
|
|
232
|
+
# By convention the comment immediately above the 'module' definition in your
|
|
233
|
+
# main library file is used by =yard= and =ri= as the overview for the gem.
|
|
234
|
+
#
|
|
235
|
+
# #+begin_src ruby :eval no
|
|
236
|
+
# rake docs:overview
|
|
237
|
+
# #+end_src
|
|
238
|
+
#
|
|
239
|
+
# This extracts the "Introduction" section from ~README.org~ and makes it the
|
|
240
|
+
# overview comment in the gem's main library file. If it already exists, it
|
|
241
|
+
# replaces it with any newer version of the "Introduction" section, otherwise,
|
|
242
|
+
# it does not change the file.
|
|
243
|
+
module GemDocs
|
|
244
|
+
require_relative "gem_docs/version"
|
|
245
|
+
require_relative "gem_docs/config"
|
|
246
|
+
require_relative "gem_docs/repo"
|
|
247
|
+
require_relative "gem_docs/emacs"
|
|
248
|
+
require_relative "gem_docs/overview"
|
|
249
|
+
require_relative "gem_docs/yard"
|
|
250
|
+
require_relative "gem_docs/badge"
|
|
251
|
+
require_relative "gem_docs/header"
|
|
252
|
+
require_relative "gem_docs/skeleton"
|
|
253
|
+
require_relative "gem_docs/tasks"
|
|
254
|
+
|
|
255
|
+
README_ORG = "README.org"
|
|
256
|
+
README_MD = "README.md"
|
|
257
|
+
|
|
258
|
+
# Auto-detect project root (handles being run from subdirs)
|
|
259
|
+
def self.project_root
|
|
260
|
+
here = Dir.pwd
|
|
261
|
+
here = File.dirname(here) until !Dir['*.gemspec', 'Gemfile'].empty? || here == "/"
|
|
262
|
+
here
|
|
263
|
+
end
|
|
264
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gem_docs
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Daniel E. Doherty
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2025-12-23 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: yard
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.9'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.9'
|
|
40
|
+
description: Shared tasks for README.org execution, GFM export, YARD integration (future),
|
|
41
|
+
etc.
|
|
42
|
+
email:
|
|
43
|
+
- ded@ddoherty.net
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- lib/gem_docs.rb
|
|
49
|
+
- lib/gem_docs/badge.rb
|
|
50
|
+
- lib/gem_docs/config.rb
|
|
51
|
+
- lib/gem_docs/emacs.rb
|
|
52
|
+
- lib/gem_docs/header.rb
|
|
53
|
+
- lib/gem_docs/overview.rb
|
|
54
|
+
- lib/gem_docs/repo.rb
|
|
55
|
+
- lib/gem_docs/skeleton.rb
|
|
56
|
+
- lib/gem_docs/tasks.rb
|
|
57
|
+
- lib/gem_docs/version.rb
|
|
58
|
+
- lib/gem_docs/yard.rb
|
|
59
|
+
homepage: https://github.com/ddoherty03/gem_docs
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata:
|
|
63
|
+
homepage_uri: https://github.com/ddoherty03/gem_docs
|
|
64
|
+
source_code_uri: https://github.com/ddoherty03/gem_docs
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 3.2.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.6.3
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Documentation automation for Ruby gems
|
|
82
|
+
test_files: []
|