open_gemdocs 0.2.4 → 0.3.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +37 -0
- data/CHANGELOG.md +4 -0
- data/CLAUDE.md +136 -0
- data/README.md +53 -2
- data/checksums/open_gemdocs-0.3.1.gem.sha512 +1 -0
- data/exe/document-bundle +8 -0
- data/exe/open-gem-docs +11 -11
- data/exe/open-gem-docs-mcp +52 -0
- data/exe/open-gem-docs-mcp-stdio +76 -0
- data/lib/open_gemdocs/browser.rb +12 -14
- data/lib/open_gemdocs/mcp/handlers.rb +92 -0
- data/lib/open_gemdocs/mcp/server.rb +77 -0
- data/lib/open_gemdocs/mcp/tools.rb +471 -0
- data/lib/open_gemdocs/version.rb +1 -1
- data/lib/open_gemdocs/yard.rb +21 -7
- data/lib/open_gemdocs/yard_json_formatter.rb +225 -0
- data/lib/open_gemdocs.rb +11 -3
- data.tar.gz.sig +1 -3
- metadata +29 -2
- metadata.gz.sig +0 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yard"
|
4
|
+
require "json"
|
5
|
+
require "fileutils"
|
6
|
+
require "tmpdir"
|
7
|
+
|
8
|
+
module OpenGemdocs
|
9
|
+
class YardJsonFormatter
|
10
|
+
def self.format_gem_docs(gem_name, object_path = nil)
|
11
|
+
# Ensure YARD server data is available
|
12
|
+
yard_db_path = find_yard_db(gem_name)
|
13
|
+
|
14
|
+
unless yard_db_path
|
15
|
+
# Try to generate the .yardoc if it doesn't exist
|
16
|
+
gem_path = find_gem_path(gem_name)
|
17
|
+
return { error: "Gem '#{gem_name}' not found" } unless gem_path
|
18
|
+
|
19
|
+
# Create a temporary .yardoc path
|
20
|
+
yard_db_path = File.join(Dir.tmpdir, "yard_#{gem_name}_#{Process.pid}", ".yardoc")
|
21
|
+
FileUtils.mkdir_p(File.dirname(yard_db_path))
|
22
|
+
|
23
|
+
# Generate YARD documentation
|
24
|
+
YARD::CLI::Yardoc.run("--no-output", "--db", yard_db_path, gem_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Load the YARD registry
|
28
|
+
YARD::Registry.load(yard_db_path)
|
29
|
+
|
30
|
+
if object_path
|
31
|
+
# Return specific object documentation
|
32
|
+
obj = YARD::Registry.at(object_path)
|
33
|
+
return { error: "Object '#{object_path}' not found in #{gem_name}" } unless obj
|
34
|
+
|
35
|
+
begin
|
36
|
+
format_object(obj)
|
37
|
+
rescue StandardError => e
|
38
|
+
{ error: "Failed to format object: #{e.message}\n#{e.backtrace.first(3).join("\n")}" }
|
39
|
+
end
|
40
|
+
else
|
41
|
+
# Return gem overview with main classes and modules
|
42
|
+
{
|
43
|
+
gem: gem_name,
|
44
|
+
summary: get_gem_summary(gem_name),
|
45
|
+
namespaces: format_namespaces,
|
46
|
+
classes: format_classes,
|
47
|
+
modules: format_modules
|
48
|
+
}
|
49
|
+
end
|
50
|
+
ensure
|
51
|
+
YARD::Registry.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
private_class_method def self.find_yard_db(gem_name)
|
55
|
+
# Check common locations for .yardoc databases
|
56
|
+
possible_paths = [
|
57
|
+
File.join(Dir.home, ".yard", "gems", "#{gem_name}-*", ".yardoc"),
|
58
|
+
File.join(Gem.dir, "doc", "#{gem_name}-*", ".yardoc")
|
59
|
+
]
|
60
|
+
|
61
|
+
possible_paths.each do |pattern|
|
62
|
+
matches = Dir.glob(pattern)
|
63
|
+
return matches.first if matches.any?
|
64
|
+
end
|
65
|
+
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
private_class_method def self.find_gem_path(gem_name)
|
70
|
+
spec = Gem::Specification.find_by_name(gem_name)
|
71
|
+
spec.full_gem_path if spec
|
72
|
+
rescue Gem::LoadError
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
private_class_method def self.get_gem_summary(gem_name)
|
77
|
+
spec = Gem::Specification.find_by_name(gem_name)
|
78
|
+
{
|
79
|
+
version: spec.version.to_s,
|
80
|
+
description: spec.description,
|
81
|
+
summary: spec.summary,
|
82
|
+
homepage: spec.homepage
|
83
|
+
}
|
84
|
+
rescue Gem::LoadError
|
85
|
+
{}
|
86
|
+
end
|
87
|
+
|
88
|
+
private_class_method def self.format_namespaces
|
89
|
+
YARD::Registry.all(:module, :class).select { |obj| obj.namespace.root? }.map do |obj|
|
90
|
+
{
|
91
|
+
name: obj.name.to_s,
|
92
|
+
path: obj.path,
|
93
|
+
type: obj.type.to_s
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private_class_method def self.format_classes
|
99
|
+
YARD::Registry.all(:class).map do |obj|
|
100
|
+
{
|
101
|
+
name: obj.name.to_s,
|
102
|
+
path: obj.path,
|
103
|
+
namespace: obj.namespace.path == "" ? nil : obj.namespace.path,
|
104
|
+
superclass: obj.superclass ? obj.superclass.path : nil,
|
105
|
+
docstring: obj.docstring.to_s.empty? ? nil : obj.docstring.to_s,
|
106
|
+
methods_count: obj.meths.count
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private_class_method def self.format_modules
|
112
|
+
YARD::Registry.all(:module).map do |obj|
|
113
|
+
{
|
114
|
+
name: obj.name.to_s,
|
115
|
+
path: obj.path,
|
116
|
+
namespace: obj.namespace.path == "" ? nil : obj.namespace.path,
|
117
|
+
docstring: obj.docstring.to_s.empty? ? nil : obj.docstring.to_s,
|
118
|
+
methods_count: obj.meths.count
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private_class_method def self.format_object(obj)
|
124
|
+
return nil unless obj
|
125
|
+
|
126
|
+
base_info = {
|
127
|
+
name: obj.name.to_s,
|
128
|
+
path: obj.path,
|
129
|
+
type: obj.type.to_s,
|
130
|
+
namespace: obj.namespace && obj.namespace.path != "" ? obj.namespace.path : nil,
|
131
|
+
docstring: obj.docstring.to_s.empty? ? nil : obj.docstring.to_s,
|
132
|
+
tags: format_tags(obj.tags),
|
133
|
+
source: obj.file ? { file: obj.file, line: obj.line } : nil
|
134
|
+
}
|
135
|
+
|
136
|
+
case obj.type
|
137
|
+
when :class
|
138
|
+
base_info.merge!(
|
139
|
+
superclass: obj.superclass ? obj.superclass.path : nil,
|
140
|
+
includes: obj.mixins(:instance).map(&:path),
|
141
|
+
extends: obj.mixins(:class).map(&:path),
|
142
|
+
methods: format_methods(obj.meths),
|
143
|
+
attributes: format_attributes(obj.attributes)
|
144
|
+
)
|
145
|
+
when :module
|
146
|
+
base_info.merge!(
|
147
|
+
includes: obj.mixins(:instance).map(&:path),
|
148
|
+
extends: obj.mixins(:class).map(&:path),
|
149
|
+
methods: format_methods(obj.meths),
|
150
|
+
attributes: format_attributes(obj.attributes)
|
151
|
+
)
|
152
|
+
when :method
|
153
|
+
base_info.merge!(
|
154
|
+
signature: obj.signature,
|
155
|
+
parameters: format_parameters(obj.parameters),
|
156
|
+
visibility: obj.visibility.to_s,
|
157
|
+
scope: obj.scope.to_s,
|
158
|
+
aliases: obj.aliases.map(&:name).map(&:to_s)
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
base_info
|
163
|
+
end
|
164
|
+
|
165
|
+
private_class_method def self.format_tags(tags)
|
166
|
+
tags.map do |tag|
|
167
|
+
result = {
|
168
|
+
tag_name: tag.tag_name,
|
169
|
+
text: tag.text
|
170
|
+
}
|
171
|
+
result[:types] = tag.types if tag.respond_to?(:types)
|
172
|
+
result[:name] = tag.name if tag.respond_to?(:name)
|
173
|
+
result
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private_class_method def self.format_methods(methods)
|
178
|
+
methods.map do |meth|
|
179
|
+
{
|
180
|
+
name: meth.name.to_s,
|
181
|
+
path: meth.path,
|
182
|
+
signature: meth.signature,
|
183
|
+
visibility: meth.visibility.to_s,
|
184
|
+
scope: meth.scope.to_s,
|
185
|
+
docstring: meth.docstring.to_s.empty? ? nil : meth.docstring.to_s,
|
186
|
+
parameters: format_parameters(meth.parameters),
|
187
|
+
return_type: extract_return_type(meth)
|
188
|
+
}.compact
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private_class_method def self.format_parameters(params)
|
193
|
+
return [] unless params
|
194
|
+
|
195
|
+
params.map do |param|
|
196
|
+
name, default = param
|
197
|
+
{
|
198
|
+
name: name,
|
199
|
+
default: default
|
200
|
+
}
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
private_class_method def self.format_attributes(attrs)
|
205
|
+
attrs.map do |_name, attr|
|
206
|
+
# Handle case where both read and write might be nil (shouldn't happen but defensive)
|
207
|
+
accessor = attr[:read] || attr[:write]
|
208
|
+
next unless accessor
|
209
|
+
|
210
|
+
{
|
211
|
+
name: accessor.name.to_s.sub("=", ""),
|
212
|
+
read: !attr[:read].nil?,
|
213
|
+
write: !attr[:write].nil?,
|
214
|
+
docstring: accessor.docstring.to_s
|
215
|
+
}
|
216
|
+
end.compact
|
217
|
+
end
|
218
|
+
|
219
|
+
private_class_method def self.extract_return_type(method)
|
220
|
+
return_tag = method.tags.find { |t| t.tag_name == "return" }
|
221
|
+
return_tag ? return_tag.types : nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
data/lib/open_gemdocs.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
3
|
+
require_relative "open_gemdocs/version"
|
4
|
+
require_relative "open_gemdocs/browser"
|
5
|
+
require_relative "open_gemdocs/yard"
|
6
6
|
|
7
7
|
module OpenGemdocs
|
8
8
|
class Error < StandardError; end
|
9
|
+
|
10
|
+
# MCP components are loaded on-demand by the executable
|
11
|
+
module MCP
|
12
|
+
# Autoload MCP components to avoid loading them unless needed
|
13
|
+
autoload :Server, "open_gemdocs/mcp/server"
|
14
|
+
autoload :Handlers, "open_gemdocs/mcp/handlers"
|
15
|
+
autoload :Tools, "open_gemdocs/mcp/tools"
|
16
|
+
end
|
9
17
|
end
|
data.tar.gz.sig
CHANGED
@@ -1,3 +1 @@
|
|
1
|
-
|
2
|
-
��
|
3
|
-
�\M'�g!GH>�;3]�ƨ:�g2+���x� 6ԭ��� ����@�3mj�]a9���W
|
1
|
+
R����GR�)�N���G(�����٣)�7)�����yBLQ���o���{��R_j��E��* (�r^�5����,�-��LCS��ԟ�NYi��2�W�쨒��*��)N����-��+LH���c/T�N�L�Ƅ��b��ec����M�(*$F�Sfu"�A��}���r��5���6���þ���!��z����z�aG���}�H)m�\h�����gJ���O&���-�T�Q5H��0����e9םG��7���W#0�I^7�1�����Uw�rB�\��=�4�۵<Lc�=�S�����3#�h������s�� 7���!���iL�ip(A8eA/2��ē���q�&��ڽ^�K��
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: open_gemdocs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean McCleary
|
@@ -36,6 +36,20 @@ cert_chain:
|
|
36
36
|
-----END CERTIFICATE-----
|
37
37
|
date: 1980-01-02 00:00:00.000000000 Z
|
38
38
|
dependencies:
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: webrick
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - "~>"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.8'
|
46
|
+
type: :runtime
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '1.8'
|
39
53
|
- !ruby/object:Gem::Dependency
|
40
54
|
name: yard
|
41
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,7 +68,10 @@ description: ''
|
|
54
68
|
email:
|
55
69
|
- seanmcc@gmail.com
|
56
70
|
executables:
|
71
|
+
- document-bundle
|
57
72
|
- open-gem-docs
|
73
|
+
- open-gem-docs-mcp
|
74
|
+
- open-gem-docs-mcp-stdio
|
58
75
|
- open-local-docs
|
59
76
|
extensions: []
|
60
77
|
extra_rdoc_files: []
|
@@ -62,6 +79,7 @@ files:
|
|
62
79
|
- ".rspec"
|
63
80
|
- ".rubocop.yml"
|
64
81
|
- CHANGELOG.md
|
82
|
+
- CLAUDE.md
|
65
83
|
- LICENSE.txt
|
66
84
|
- README.md
|
67
85
|
- Rakefile
|
@@ -72,12 +90,20 @@ files:
|
|
72
90
|
- checksums/open_gemdocs-0.2.2.gem.sha512
|
73
91
|
- checksums/open_gemdocs-0.2.3.gem.sha512
|
74
92
|
- checksums/open_gemdocs-0.2.4.gem.sha512
|
93
|
+
- checksums/open_gemdocs-0.3.1.gem.sha512
|
94
|
+
- exe/document-bundle
|
75
95
|
- exe/open-gem-docs
|
96
|
+
- exe/open-gem-docs-mcp
|
97
|
+
- exe/open-gem-docs-mcp-stdio
|
76
98
|
- exe/open-local-docs
|
77
99
|
- lib/open_gemdocs.rb
|
78
100
|
- lib/open_gemdocs/browser.rb
|
101
|
+
- lib/open_gemdocs/mcp/handlers.rb
|
102
|
+
- lib/open_gemdocs/mcp/server.rb
|
103
|
+
- lib/open_gemdocs/mcp/tools.rb
|
79
104
|
- lib/open_gemdocs/version.rb
|
80
105
|
- lib/open_gemdocs/yard.rb
|
106
|
+
- lib/open_gemdocs/yard_json_formatter.rb
|
81
107
|
- sig/open_gemdocs.rbs
|
82
108
|
homepage: https://github.com/mrinterweb/open_gemdocs
|
83
109
|
licenses:
|
@@ -87,6 +113,7 @@ metadata:
|
|
87
113
|
homepage_uri: https://github.com/mrinterweb/open_gemdocs
|
88
114
|
source_code_uri: https://github.com/mrinterweb/open_gemdocs
|
89
115
|
changelog_uri: https://github.com/mrinterweb/open_gemdocs/blob/main/CHANGELOG.md
|
116
|
+
rubygems_mfa_required: 'true'
|
90
117
|
rdoc_options: []
|
91
118
|
require_paths:
|
92
119
|
- lib
|
@@ -101,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
128
|
- !ruby/object:Gem::Version
|
102
129
|
version: '0'
|
103
130
|
requirements: []
|
104
|
-
rubygems_version: 3.
|
131
|
+
rubygems_version: 3.7.1
|
105
132
|
specification_version: 4
|
106
133
|
summary: Simple command line tool to open the documentation for a gem.
|
107
134
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|