markdown_ruby_documentation 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/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +134 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/github-markdown.css +656 -0
- data/lib/markdown_ruby_documentation.rb +29 -0
- data/lib/markdown_ruby_documentation/constants_presenter.rb +38 -0
- data/lib/markdown_ruby_documentation/default_erb_methods.rb +11 -0
- data/lib/markdown_ruby_documentation/generate.rb +107 -0
- data/lib/markdown_ruby_documentation/git_hub_link.rb +74 -0
- data/lib/markdown_ruby_documentation/git_hub_project.rb +29 -0
- data/lib/markdown_ruby_documentation/instance_to_class_methods.rb +44 -0
- data/lib/markdown_ruby_documentation/markdown_presenter.rb +36 -0
- data/lib/markdown_ruby_documentation/method.rb +101 -0
- data/lib/markdown_ruby_documentation/method/class_method.rb +13 -0
- data/lib/markdown_ruby_documentation/method/instance_method.rb +11 -0
- data/lib/markdown_ruby_documentation/method/null_method.rb +28 -0
- data/lib/markdown_ruby_documentation/method_linker.rb +41 -0
- data/lib/markdown_ruby_documentation/print_method_source.rb +19 -0
- data/lib/markdown_ruby_documentation/reject_blank_methods.rb +9 -0
- data/lib/markdown_ruby_documentation/summary.rb +32 -0
- data/lib/markdown_ruby_documentation/template_parser.rb +348 -0
- data/lib/markdown_ruby_documentation/version.rb +3 -0
- data/lib/markdown_ruby_documentation/write_markdown_to_disk.rb +29 -0
- data/markdown_ruby_documenation.gemspec +30 -0
- metadata +162 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require "markdown_ruby_documentation/version"
|
2
|
+
require "markdown_ruby_documentation/summary"
|
3
|
+
require "markdown_ruby_documentation/instance_to_class_methods"
|
4
|
+
require "markdown_ruby_documentation/print_method_source"
|
5
|
+
require "markdown_ruby_documentation/template_parser"
|
6
|
+
require "markdown_ruby_documentation/markdown_presenter"
|
7
|
+
require "markdown_ruby_documentation/generate"
|
8
|
+
require "markdown_ruby_documentation/git_hub_link"
|
9
|
+
require "markdown_ruby_documentation/git_hub_project"
|
10
|
+
require "markdown_ruby_documentation/reject_blank_methods"
|
11
|
+
require "markdown_ruby_documentation/method_linker"
|
12
|
+
require "markdown_ruby_documentation/method"
|
13
|
+
require "markdown_ruby_documentation/method/instance_method"
|
14
|
+
require "markdown_ruby_documentation/method/class_method"
|
15
|
+
require "markdown_ruby_documentation/method/null_method"
|
16
|
+
require "markdown_ruby_documentation/write_markdown_to_disk"
|
17
|
+
require "markdown_ruby_documentation/default_erb_methods"
|
18
|
+
require "markdown_ruby_documentation/constants_presenter"
|
19
|
+
require "ruby-progressbar"
|
20
|
+
require "active_support/core_ext/string"
|
21
|
+
require "method_source"
|
22
|
+
require "json"
|
23
|
+
require "active_support/dependencies/autoload"
|
24
|
+
require "active_support/number_helper"
|
25
|
+
|
26
|
+
module MarkdownRubyDocumentation
|
27
|
+
START_TOKEN = "=mark_doc".freeze
|
28
|
+
END_TOKEN = "=mark_end".freeze
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class ConstantsPresenter
|
3
|
+
|
4
|
+
attr_reader :subject
|
5
|
+
|
6
|
+
def initialize(subject)
|
7
|
+
@subject = subject
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(interface)
|
11
|
+
constants.each do |const_name, value|
|
12
|
+
interface[const_name] = { text: value, method_object: Method.create("#{subject.name}::#{const_name}", null_method: true) }
|
13
|
+
end
|
14
|
+
interface
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def constants
|
20
|
+
subject.constants.each_with_object({}) do |v, const|
|
21
|
+
c = subject.const_get(v)
|
22
|
+
const[v] = format(c) unless c.class == Module || c.class == Class
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def format(value)
|
27
|
+
case value
|
28
|
+
when Fixnum
|
29
|
+
ActiveSupport::NumberHelper.number_to_delimited(value)
|
30
|
+
when String
|
31
|
+
value.inspect
|
32
|
+
else
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class Generate
|
3
|
+
# @param [Class] subjects ruby classes to generate documentation from.
|
4
|
+
# @param [Module] erb_methods must contain #link_to_markdown and contain any additional methods for comment ERB
|
5
|
+
# @param [Proc] output_proc given name: and text: for use in saving the the files.
|
6
|
+
def self.run(
|
7
|
+
subjects:, erb_methods: DefaultErbMethods, output_proc: -> (name:, text:) { { name => text } }
|
8
|
+
)
|
9
|
+
erb_methods_class = Class.new
|
10
|
+
erb_methods_class.extend TemplateParser::CommentMacros
|
11
|
+
erb_methods_class.extend erb_methods
|
12
|
+
TemplateParser::CommentMacros.include erb_methods
|
13
|
+
left_padding = subjects.map(&:name).group_by(&:size).max.first
|
14
|
+
progressbar = ProgressBar.create(title: "Compiling Markdown".ljust(left_padding), total: subjects.count+ 1)
|
15
|
+
pages = subjects.map do |subject|
|
16
|
+
progressbar.title = subject.name.ljust(left_padding)
|
17
|
+
Page.new(subject: subject,
|
18
|
+
output_proc: output_proc,
|
19
|
+
erb_methods_class: erb_methods_class).call.tap { progressbar.increment }
|
20
|
+
end
|
21
|
+
|
22
|
+
return_value = pages.each_with_object({}) do |page, hash|
|
23
|
+
name_parts = page.subject.name.split("::")
|
24
|
+
name = name_parts.pop
|
25
|
+
namespace = name_parts.join("::")
|
26
|
+
hash[namespace] ||= {}
|
27
|
+
hash[namespace].merge!({ name => page })
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
progressbar.title = "Markdown Documentation Compilation Complete".ljust(left_padding)
|
31
|
+
progressbar.finish
|
32
|
+
return_value
|
33
|
+
end
|
34
|
+
|
35
|
+
class Page
|
36
|
+
attr_reader :subject, :output_proc, :erb_methods_class
|
37
|
+
|
38
|
+
def initialize(subject:,
|
39
|
+
methods: [],
|
40
|
+
output_proc:,
|
41
|
+
erb_methods_class:)
|
42
|
+
initialize_methods(methods, subject)
|
43
|
+
@erb_methods_class = erb_methods_class
|
44
|
+
@subject = subject
|
45
|
+
methods = methods
|
46
|
+
@methods = methods
|
47
|
+
@output_proc = output_proc
|
48
|
+
end
|
49
|
+
|
50
|
+
def call
|
51
|
+
methods_pipes = run_pipeline(methods_pipeline)
|
52
|
+
text = run_pipeline(string_pipeline, methods_pipes)
|
53
|
+
output_proc.call(name: subject.name,
|
54
|
+
text: text)
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def initialize_methods(methods, subject)
|
60
|
+
if methods.empty?
|
61
|
+
all_instance_and_class_methods(methods, subject)
|
62
|
+
else
|
63
|
+
methods.map! { |method| method.is_a?(Symbol) ? InstanceMethod.new("#{subject.name}##{method}", context: subject) : method }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_instance_and_class_methods(methods, subject)
|
68
|
+
instance_m = subject.instance_methods(false).concat(subject.private_instance_methods(false))
|
69
|
+
klass_m = subject.methods(false).concat(subject.private_methods(false)) - Object.methods
|
70
|
+
methods.concat instance_m.map { |method| InstanceMethod.new("#{subject.name}##{method}", context: subject) }
|
71
|
+
methods.concat klass_m.map { |method| ClassMethod.new("#{subject.name}.#{method}", context: subject) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def methods_pipeline
|
75
|
+
[
|
76
|
+
TemplateParser.new(subject, @methods),
|
77
|
+
RejectBlankMethod,
|
78
|
+
GitHubLink.new(subject: subject),
|
79
|
+
ConstantsPresenter.new(subject),
|
80
|
+
MarkdownPresenter.new(summary: summary, title_key: section_key),
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
def string_pipeline
|
85
|
+
[
|
86
|
+
MethodLinker.new(section_key: section_key, root_path: "./"),
|
87
|
+
]
|
88
|
+
end
|
89
|
+
|
90
|
+
def summary
|
91
|
+
@summary ||= Summary.new(subject: subject, erb_methods_class: erb_methods_class)
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_pipeline(pipeline, last_result=nil)
|
95
|
+
last_result ||= pipeline.shift.call
|
96
|
+
pipeline.each do |pipe|
|
97
|
+
last_result = pipe.call(last_result)
|
98
|
+
end
|
99
|
+
last_result
|
100
|
+
end
|
101
|
+
|
102
|
+
def section_key
|
103
|
+
subject.name.underscore.gsub("/", "-")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class GitHubLink
|
3
|
+
attr_reader :subject, :base_url, :root
|
4
|
+
|
5
|
+
def initialize(subject:, base_url: GitHubProject.url, root: GitHubProject.root_path)
|
6
|
+
@subject = subject
|
7
|
+
@base_url = base_url
|
8
|
+
@root = root
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(hash)
|
12
|
+
hash.each do |name, values|
|
13
|
+
hash[name][:text] = "#{values[:text]}\n\n[show on github](#{create_link(name: name, method_object: values[:method_object])})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_link(name: nil, method_object: nil)
|
18
|
+
if name && method_object.nil?
|
19
|
+
method_object = Method.create("##{name}")
|
20
|
+
end
|
21
|
+
MethodUrl.new(subject: subject, base_url: base_url, root: root, method_object: method_object).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
class FileUrl
|
25
|
+
attr_reader :file_path, :base_url, :root
|
26
|
+
|
27
|
+
def initialize(file_path:, base_url: GitHubProject.url, root: GitHubProject.root_path)
|
28
|
+
@file_path = file_path
|
29
|
+
@base_url = base_url
|
30
|
+
@root = root
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
link(file_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_pathname
|
38
|
+
Pathname(to_s)
|
39
|
+
end
|
40
|
+
|
41
|
+
def link(file, lineno=nil)
|
42
|
+
str = File.join(base_url, "blob", blob(file), relative_path(file))
|
43
|
+
unless lineno.nil?
|
44
|
+
str << "#L#{lineno}"
|
45
|
+
end
|
46
|
+
str.chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
def blob(file)
|
50
|
+
GitHubProject.branch
|
51
|
+
end
|
52
|
+
|
53
|
+
def relative_path(file)
|
54
|
+
file.sub(root, "")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class MethodUrl
|
59
|
+
attr_reader :base_url, :method_object, :subject, :root
|
60
|
+
|
61
|
+
def initialize(subject:, method_object:, base_url: GitHubProject.url, root: GitHubProject.root_path)
|
62
|
+
@subject = subject
|
63
|
+
@base_url = base_url
|
64
|
+
@root = root
|
65
|
+
@method_object = method_object
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
file, lineno = subject.public_send(method_object.type, method_object.name).source_location
|
70
|
+
FileUrl.new(file_path: file, base_url: base_url, root: root).link(file, lineno)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class GitHubProject
|
3
|
+
class << self
|
4
|
+
def git_url
|
5
|
+
`git config --get remote.origin.url`.chomp
|
6
|
+
end
|
7
|
+
|
8
|
+
def url
|
9
|
+
"https://github.com/#{git_url.split(":").last.gsub(".git", "")}".chomp
|
10
|
+
end
|
11
|
+
|
12
|
+
def root_path
|
13
|
+
`git rev-parse --show-toplevel`.chomp
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_branch(branch)
|
17
|
+
@branch = branch
|
18
|
+
end
|
19
|
+
|
20
|
+
def branch
|
21
|
+
@branch || current_branch
|
22
|
+
end
|
23
|
+
|
24
|
+
def current_branch
|
25
|
+
`git rev-parse --abbrev-ref HEAD`.chomp
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class InstanceToClassMethods
|
3
|
+
attr_reader :method_object
|
4
|
+
|
5
|
+
def initialize(method:)
|
6
|
+
@method_object = method
|
7
|
+
end
|
8
|
+
|
9
|
+
def eval_instance_method
|
10
|
+
_module = method_object.context.const_set(new_class_name, Class.new(method_object.context))
|
11
|
+
rescue_and_define_method(_module) do |_module|
|
12
|
+
create_method(method_object, _module)
|
13
|
+
_module.send(method_object.name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def new_class_name
|
20
|
+
"InstanceToClassMethods#{method_object.context.name}#{('A'..'Z').to_a.sample(5).join}".delete("::")
|
21
|
+
end
|
22
|
+
|
23
|
+
def rescue_and_define_method(_module, &block)
|
24
|
+
block.call(_module)
|
25
|
+
rescue NameError => e
|
26
|
+
if (undefined_method = e.message.match(/undefined local variable or method `(.+)'/).try!(:captures).try!(:first))
|
27
|
+
undefined_method = Method.create("##{undefined_method}", context: method_object.context)
|
28
|
+
create_method(undefined_method, _module)
|
29
|
+
rescue_and_define_method(_module, &block)
|
30
|
+
else
|
31
|
+
raise e
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_method(method, m=Module.new)
|
36
|
+
m.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
37
|
+
def self.#{method.name}
|
38
|
+
#{PrintMethodSource.new(method: method).print}
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
m
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class MarkdownPresenter
|
3
|
+
|
4
|
+
attr_reader :items, :title_key, :summary
|
5
|
+
|
6
|
+
def initialize(items: nil, summary:, title_key:, skip_blanks: true)
|
7
|
+
@items = items
|
8
|
+
@summary = summary
|
9
|
+
@title_key = title_key
|
10
|
+
@skip_blanks = skip_blanks
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(items=nil)
|
14
|
+
@items ||= items
|
15
|
+
<<-MD
|
16
|
+
# #{summary.title}
|
17
|
+
#{summary.summary}
|
18
|
+
|
19
|
+
#{present_items}
|
20
|
+
MD
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def present_items
|
26
|
+
items.map do |name, hash|
|
27
|
+
begin
|
28
|
+
%[## #{name.to_s.titleize}\n#{hash[:text]}] unless hash[:text].blank?
|
29
|
+
rescue =>e
|
30
|
+
puts e
|
31
|
+
end
|
32
|
+
end.join("\n\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module MarkdownRubyDocumentation
|
2
|
+
class Method
|
3
|
+
attr_reader :method_reference
|
4
|
+
protected :method_reference
|
5
|
+
|
6
|
+
def initialize(method_reference, context: Kernel)
|
7
|
+
@method_reference = method_reference.to_s
|
8
|
+
@context = context
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [String] method_reference
|
12
|
+
# @example
|
13
|
+
# ".class_method_name" class method in the current scope.
|
14
|
+
# "Constant.class_method_name" class method on a specific constant.
|
15
|
+
# "SomeClass#instance_method_name" an instance method on a specific constant.
|
16
|
+
# "#instance_method_name" an instance method in the current scope.
|
17
|
+
def self.create(method_reference, null_method: false, context: Kernel)
|
18
|
+
case method_reference
|
19
|
+
when InstanceMethod
|
20
|
+
InstanceMethod.new(method_reference, context: context)
|
21
|
+
when ClassMethod
|
22
|
+
ClassMethod.new(method_reference, context: context)
|
23
|
+
else
|
24
|
+
if null_method
|
25
|
+
NullMethod.new(method_reference, context: context)
|
26
|
+
else
|
27
|
+
raise ArgumentError, "method_reference is formatted incorrectly: '#{method_reference}'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other_method)
|
33
|
+
self.class == other_method.class && other_method.method_reference == self.method_reference
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :eql? :==
|
37
|
+
|
38
|
+
def hash
|
39
|
+
@method_reference.hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.===(value)
|
43
|
+
if value.is_a?(String)
|
44
|
+
value.include?(type_symbol)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
def type_symbol
|
52
|
+
self.class.type_symbol
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Class]
|
56
|
+
def context
|
57
|
+
if method_reference.start_with?(type_symbol)
|
58
|
+
@context
|
59
|
+
else
|
60
|
+
constant = method_reference.split(type_symbol).first
|
61
|
+
begin
|
62
|
+
constant.constantize
|
63
|
+
rescue NameError => e
|
64
|
+
@context.const_get(constant)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def context_name
|
70
|
+
if method_reference.start_with?(type_symbol)
|
71
|
+
@context.name
|
72
|
+
else
|
73
|
+
method_reference.split(type_symbol).first
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Symbol]
|
78
|
+
def name
|
79
|
+
method_reference.split(type_symbol).last.try!(:to_sym)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [String]
|
83
|
+
def to_s
|
84
|
+
method_reference
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [String]
|
88
|
+
def inspect
|
89
|
+
"#<#{self.class.name} #{to_s}>"
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Proc]
|
93
|
+
def to_proc
|
94
|
+
context.public_send(type, name)
|
95
|
+
end
|
96
|
+
|
97
|
+
def type
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|