yard-markdown 0.5.0 → 0.6.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.
@@ -1,58 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # https://github.com/lsegal/yard/blob/2d197a381c5d4cc5c55b2c60fff992b31c986361/docs/CodeObjects.md
4
-
5
- require "erb"
6
- require "csv"
3
+ require 'csv'
7
4
 
8
5
  include Helpers::ModuleHelper
9
6
 
10
7
  def init
11
- options.objects = objects = run_verifier(options.objects)
8
+ options.objects = objects = run_verifier(options.objects).reject { |item| item.name == :root }
12
9
 
13
10
  options.delete(:objects)
14
11
  options.delete(:files)
15
12
 
16
- options.serializer.extension = "md"
17
-
18
- generate_method_list
13
+ options.serializer.extension = 'md'
19
14
 
20
15
  objects.each do |object|
21
- next if object.name == :root
22
-
23
- begin
24
- Templates::Engine.with_serializer(object, options.serializer) { serialize(object) }
25
- rescue => e
26
- path = options.serializer.serialized_path(object)
27
- log.error "Exception occurred while generating '#{path}'"
28
- log.backtrace(e)
29
- end
16
+ Templates::Engine.with_serializer(object, options.serializer) { serialize(object) }
17
+ rescue StandardError => e
18
+ path = options.serializer.serialized_path(object)
19
+ log.error "Exception occurred while generating '#{path}'"
20
+ log.backtrace(e)
30
21
  end
31
22
 
32
23
  serialize_index(objects)
33
24
  end
34
25
 
26
+ def serialize(object)
27
+ T('module').run(options.merge(object: object))
28
+ end
29
+
35
30
  def serialize_index(objects)
36
31
  filepath = "#{options.serializer.basepath}/index.csv"
37
32
 
38
- CSV.open(filepath, "wb") do |csv|
33
+ CSV.open(filepath, 'wb') do |csv|
39
34
  csv << %w[name type path]
40
35
 
41
36
  objects.each do |object|
42
37
  next if object.name == :root
43
38
 
44
39
  if object.type == :class
45
- csv << [object.path, "Class", options.serializer.serialized_path(object)]
40
+ csv << [object.path, 'Class', options.serializer.serialized_path(object)]
46
41
  elsif object.type == :module
47
- csv << [object.path, "Module", options.serializer.serialized_path(object)]
42
+ csv << [object.path, 'Module', options.serializer.serialized_path(object)]
48
43
  end
49
44
 
50
- if constant_listing.size.positive?
51
- constant_listing.each do |cnst|
45
+ constants = constant_listing(object)
46
+ if constants.size.positive?
47
+ constants.each do |cnst|
52
48
  csv << [
53
49
  "#{object.path}.#{cnst.name(false)}",
54
- "Constant",
55
- (options.serializer.serialized_path(object) + "#" + aref(cnst)),
50
+ 'Constant',
51
+ (options.serializer.serialized_path(object) + '#' + aref(cnst))
56
52
  ]
57
53
  end
58
54
  end
@@ -61,8 +57,8 @@ def serialize_index(objects)
61
57
  insmeths.each do |item|
62
58
  csv << [
63
59
  "#{object.path}.#{item.name(false)}",
64
- "Method",
65
- options.serializer.serialized_path(object) + "#" + aref(item),
60
+ 'Method',
61
+ options.serializer.serialized_path(object) + '#' + aref(item)
66
62
  ]
67
63
  end
68
64
  end
@@ -71,143 +67,59 @@ def serialize_index(objects)
71
67
  pubmeths.each do |item|
72
68
  csv << [
73
69
  "#{object.path}.#{item.name(false)}",
74
- "Method",
75
- options.serializer.serialized_path(object) + "#" + aref(item),
70
+ 'Method',
71
+ options.serializer.serialized_path(object) + '#' + aref(item)
76
72
  ]
77
73
  end
78
74
  end
79
75
 
80
- if (attrs = attr_listing(object)).size > 0
81
- attrs.each do |item|
82
- csv << [
83
- item.name(false),
84
- "Attribute",
85
- options.serializer.serialized_path(object) + "#" + aref(item),
86
- ]
87
- end
76
+ next unless (attrs = attr_listing(object)).size > 0
77
+
78
+ attrs.each do |item|
79
+ csv << [
80
+ "#{object.path}.#{item.name(false)}",
81
+ 'Attribute',
82
+ options.serializer.serialized_path(object) + '#' + aref(item)
83
+ ]
88
84
  end
89
85
  end
90
86
  end
91
87
  end
92
88
 
93
- # @param object [YARD::CodeObjects::Base]
94
- # @return [String] markdown formatted string
95
- #
96
- # @todo Extract template out of setup.rb class.
97
- def serialize(object)
98
- template =
99
- ERB.new(
100
- '# <%= format_object_title object %>
101
- <% if CodeObjects::ClassObject === object && object.superclass %>
102
- **Inherits:** <%= object.superclass %>
103
- <% end %><% [[:class, "Extended by"], [:instance, "Includes"]].each do |scope, name| %>
104
- <% if (mix = run_verifier(object.mixins(scope))).size > 0 %>
105
- **<%= name %>:** <%= mix.sort_by {|o| o.path }.join(", ") %>
106
- <% end %><% end %>
107
-
108
- <%= rdoc_to_md object.docstring %>
109
-
110
- <%= render_tags object %>
111
-
112
- <% if (pubmeths = public_class_methods(object)).size > 0 %>
113
- # Class Methods
114
- <% pubmeths.each do |item| %>
115
- ## <%= item.name(false) %>(<%= item.parameters.map {|p| p.join(" ") }.join(", ") %>) [](#<%=aref(item)%>)
116
- <%= rdoc_to_md item.docstring %>
117
- <%= render_tags item %>
118
- <% end %><% end %>
119
- <% if (attrs = attr_listing(object)).size > 0 %>
120
- # Attributes
121
- <% attrs.each do |item|%>
122
- ## <%= item.name %><%= item.reader? ? "[RW]" : "[R]" %> [](#<%=aref(item)%>)
123
- <%= rdoc_to_md item.docstring %>
124
-
125
- <%= render_tags item %>
126
- <% end %><% end %>
127
-
128
- <% if (insmeths = public_instance_methods(object)).size > 0 %>
129
- # Instance Methods
130
- <% insmeths.each do |item| %>
131
- ## <%= item.name(false) %>(<%= item.parameters.map {|p| p.join("") }.join(", ")%>) [](#<%=aref(item)%>)
132
- <%= rdoc_to_md item.docstring %>
133
-
134
- <%= render_tags item %>
135
- <% end %><% end %>',
136
- trim_mode: "<>",
137
- )
138
-
139
- template.result(binding)
140
- end
141
-
142
- require "rdoc"
143
-
144
- ##
145
- # Converts rdoc to markdown.
146
- #
147
- # I didn't found a way to detect yard/rdoc docstrings, so we're running docstrings through rdoc to markdown converter in all cases. If it's yard docstring, it doesn't seem to have any negative effect on end results. But absense of bugs, doesn't mean that there are no issues.
148
- #
149
- # @param docstring [String, YARD::Docstring]
150
- # @return [String] markdown formatted string
151
- def rdoc_to_md(docstring)
152
- RDoc::Markup::ToMarkdown.new.convert(docstring)
153
- end
154
-
155
- ##
156
- # Formats yard tags belonging to a object.
157
- #
158
- # This is mostly a feature of yard and rdoc doesn't have any of that. Rdoc supports ":nodoc:" and other tags. Yard claims to have full support for rdoc, doesn't really handle tags like ":nodoc:" or anything else from rdoc.
159
- #
160
- # There is an attempt to handle @example tag differently, we surround it with a code block.
161
- #
162
- # @see https://rubydoc.info/gems/yard/file/docs/TagsArch.md
163
- #
164
- # @param object [YARD::CodeObjects::Base]
165
- # @return [String] markdown formatted string of Tags
89
+ def aref(object)
90
+ type = object.type
166
91
 
167
- def render_tags(object)
168
- result = String.new("")
169
- object.tags.each do |tag|
170
- result << if !(tag.tag_name == "example")
171
- "**@#{tag.tag_name}** [#{tag.types&.join(', ')}] #{tag.text}\n\n"
172
- else
173
- ""
174
- end
175
- end
92
+ return "class-#{anchor_component(object.path.gsub('::', '-'))}" if type == :class
93
+ return "module-#{anchor_component(object.path.gsub('::', '-'))}" if type == :module
94
+ return "constant-#{anchor_component(object.name(false))}" if type == :constant
95
+ return "classvariable-#{anchor_component(object.name(false))}" if type == :classvariable
176
96
 
177
- object.tags.each do |tag|
178
- result << if (tag.tag_name == "example")
179
- "\n**@#{tag.tag_name}**\n```ruby\n#{tag.text}\n```"
180
- else
181
- ""
182
- end
183
- end
184
-
185
- result
186
- end
97
+ scope = object.scope == :class ? 'c' : 'i'
187
98
 
188
- def aref(object)
189
- if object.type == :constant
190
- "constant-#{object.name(false)}"
191
- elsif !object.attr_info.nil?
192
- "attribute-#{object.scope[0]}-#{object.name(false)}"
99
+ if object.respond_to?(:attr_info) && !object.attr_info.nil?
100
+ "attribute-#{scope}-#{anchor_component(object.name(false))}"
193
101
  else
194
- "#{object.type}-#{object.scope[0]}-#{object.name(false)}"
102
+ "method-#{scope}-#{anchor_component(object.name(false))}"
195
103
  end
196
104
  end
197
105
 
198
- def constant_listing
199
- return @constants if defined?(@constants) && @constants
106
+ def anchor_component(value)
107
+ value.to_s.each_char.map do |char|
108
+ char.match?(/[A-Za-z0-9_-]/) ? char : format('-%X', char.ord)
109
+ end.join
110
+ end
200
111
 
201
- @constants = object.constants(included: false, inherited: false)
202
- @constants += object.cvars
203
- @constants
112
+ def constant_listing(object)
113
+ constants = object.constants(included: false, inherited: false)
114
+ constants + object.cvars
204
115
  end
205
116
 
206
117
  def public_method_list(object)
207
118
  prune_method_listing(
208
119
  object.meths(inherited: false, visibility: [:public]),
209
- included: false,
210
- ).sort_by { |m| m.name.to_s }
120
+ included: false
121
+ ).reject { |item| hidden_object?(item) }
122
+ .sort_by { |m| m.name.to_s }
211
123
  end
212
124
 
213
125
  def public_class_methods(object)
@@ -219,58 +131,26 @@ def public_instance_methods(object)
219
131
  end
220
132
 
221
133
  def attr_listing(object)
222
- @attrs = []
223
- object
224
- .inheritance_tree(true)
225
- .each do |superclass|
226
- next if superclass.is_a?(CodeObjects::Proxy)
227
- next if !options.embed_mixins.empty? && !options.embed_mixins_match?(superclass)
228
- %i[class instance].each do |scope|
229
- superclass.attributes[scope].each do |_name, rw|
230
- attr = prune_method_listing([rw[:read], rw[:write]].compact, false).first
231
- @attrs << attr if attr
232
- end
134
+ attrs = []
135
+ object.inheritance_tree(true).each do |superclass|
136
+ next if superclass.is_a?(CodeObjects::Proxy)
137
+ next if !options.embed_mixins.empty? && !options.embed_mixins_match?(superclass)
138
+
139
+ %i[class instance].each do |scope|
140
+ superclass.attributes[scope].each do |_name, rw|
141
+ attr = prune_method_listing([rw[:read], rw[:write]].compact, false).first
142
+ attrs << attr if attr
233
143
  end
234
- break if options.embed_mixins.empty?
235
144
  end
236
- sort_listing @attrs
237
- end
238
-
239
- def generate_method_list
240
- @items = prune_method_listing(Registry.all(:method), false)
241
- @items = @items.reject { |m| m.name.to_s =~ /=$/ && m.is_attribute? }
242
- @items = @items.sort_by { |m| m.name.to_s }
243
-
244
- # @list_title = "Method List"
245
- # @list_type = "method"
246
- # generate_list_contents
247
- # binding.irb
145
+ break if options.embed_mixins.empty?
146
+ end
147
+ sort_listing(attrs)
248
148
  end
249
149
 
250
150
  def sort_listing(list)
251
151
  list.sort_by { |o| [o.scope.to_s, o.name.to_s.downcase] }
252
152
  end
253
153
 
254
- def groups(list, type = "Method")
255
- groups_data = object.groups
256
- if groups_data
257
- list.each { |m| groups_data |= [m.group] if m.group && owner != m.namespace }
258
- others = list.select { |m| !m.group || !groups_data.include?(m.group) }
259
- groups_data.each do |name|
260
- items = list.select { |m| m.group == name }
261
- yield(items, name) unless items.empty?
262
- end
263
- else
264
- others = []
265
- group_data = {}
266
- list.each { |itm| itm.group ? (group_data[itm.group] ||= []) << itm : others << itm }
267
- group_data.each { |group, items| yield(items, group) unless items.empty? }
268
- end
269
-
270
- return if others.empty?
271
- if others.first.respond_to?(:scope)
272
- scopes(others) { |items, scope| yield(items, "#{scope.to_s.capitalize} #{type}") }
273
- else
274
- yield(others, type)
275
- end
154
+ def hidden_object?(object)
155
+ object.docstring.to_s.strip.start_with?(':nodoc:')
276
156
  end