Skip to content

Commit

Permalink
Add RDoc::Server and --server flag
Browse files Browse the repository at this point in the history
  • Loading branch information
st0012 committed Aug 24, 2024
1 parent cd50972 commit 27a8909
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 8 deletions.
1 change: 1 addition & 0 deletions lib/rdoc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def self.home
autoload :Store, "#{__dir__}/rdoc/store"
autoload :Task, "#{__dir__}/rdoc/task"
autoload :Text, "#{__dir__}/rdoc/text"
autoload :Server, "#{__dir__}/rdoc/server"

autoload :Markdown, "#{__dir__}/rdoc/markdown"
autoload :Markup, "#{__dir__}/rdoc/markup"
Expand Down
12 changes: 7 additions & 5 deletions lib/rdoc/generator/darkfish.rb
Original file line number Diff line number Diff line change
Expand Up @@ -238,19 +238,21 @@ def write_style_sheet
# Build the initial indices and output objects based on an array of TopLevel
# objects containing the extracted information.

def generate
def generate(server_mode: false)
setup

write_style_sheet
generate_index
generate_class_files
generate_file_files
generate_table_of_contents
@json_index.generate
@json_index.generate_gzipped

copy_static

unless server_mode
# For the server, we only generate the JSON index if requested
@json_index.generate
@json_index.generate_gzipped
copy_static
end
rescue => e
debug_msg "%s: %s\n %s" % [
e.class.name, e.message, e.backtrace.join("\n ")
Expand Down
15 changes: 15 additions & 0 deletions lib/rdoc/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ class RDoc::Options

attr_reader :visibility

##
# The server's port to listen on. `nil` means the server is disabled.

attr_reader :server_port

##
# Indicates if files of test suites should be skipped
attr_accessor :skip_tests
Expand Down Expand Up @@ -392,6 +397,7 @@ def init_ivars # :nodoc:
@encoding = Encoding::UTF_8
@charset = @encoding.name
@skip_tests = true
@server_port = false
end

def init_with map # :nodoc:
Expand Down Expand Up @@ -1116,6 +1122,15 @@ def parse argv

opt.separator nil

opt.on(
"--server[=PORT]",
Integer,
"[Experimental] Run WEBrick server with generated documentation.",
"Uses port 4000 by default. Will use ./tmp for file output."
) do |port|
@server_port = port || 4000
end

opt.on("--help", "-h", "Display this help") do
RDoc::RDoc::GENERATORS.each_key do |generator|
setup_generator generator
Expand Down
31 changes: 29 additions & 2 deletions lib/rdoc/rdoc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ def document options
exit
end

unless @options.coverage_report then
unless @options.coverage_report || @options.server_port
@last_modified = setup_output_dir @options.op_dir, @options.force_update
end

Expand Down Expand Up @@ -496,9 +496,16 @@ def document options

@generator = gen_klass.new @store, @options

generate
if @options.server_port
start_server
else
generate
end
end

# Don't need to run stats for server mode
return if @options.server_port

if @stats and (@options.coverage_report or not @options.quiet) then
puts
puts @stats.summary.accept RDoc::Markup::ToRdoc.new
Expand All @@ -507,6 +514,26 @@ def document options
exit @stats.fully_documented? if @options.coverage_report
end

def start_server
begin
require 'webrick'
rescue LoadError
abort "webrick is not found. You may need to `gem install webrick` to install webrick."
end

# Change the output directory to tmp so it doesn't overwrite the current documentation
Dir.chdir "tmp" do
server = WEBrick::HTTPServer.new Port: @options.server_port

server.mount '/', RDoc::Server, self

trap 'INT' do server.shutdown end
trap 'TERM' do server.shutdown end

server.start
end
end

##
# Generates documentation for +file_info+ (from #parse_files) into the
# output dir using the generator selected
Expand Down
175 changes: 175 additions & 0 deletions lib/rdoc/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# frozen_string_literal: true
require 'erb'
require 'time'
require 'json'

class RDoc::Server < WEBrick::HTTPServlet::AbstractServlet
##
# Creates an instance of this servlet that shares cached data between
# requests.

def self.get_instance server, rdoc # :nodoc:
new server, rdoc
end

##
# Creates a new WEBrick servlet.
#
# +server+ is provided automatically by WEBrick when mounting.
# +rdoc+ is the RDoc::RDoc instance to display documentation from.

def initialize server, rdoc
super server

@rdoc = rdoc
@generator = rdoc.generator
@generator.file_output = false

darkfish_dir = File.join(__dir__, 'generator/template/darkfish/')
json_index_dir = File.join(__dir__, 'generator/template/json_index/')

@asset_dirs = {
:darkfish => darkfish_dir,
:json_index => json_index_dir,
}
end

##
# GET request entry point. Fills in +res+ for the path, etc. in +req+.

def do_GET req, res
req.path.sub!(/\A\//, '')

case req.path
when '/'
res.body = @generator.generate_servlet_root installed_docs
res.content_type = 'text/html'
when 'js/darkfish.js', 'js/jquery.js', 'js/search.js', %r{^css/}, %r{^images/}, %r{^fonts/}
asset :darkfish, req, res
when 'js/navigation.js', 'js/searcher.js'
asset :json_index, req, res
when 'js/search_index.js'
res.body = "var search_data = #{JSON.dump @generator.json_index.build_index}"
res.content_type = 'application/javascript'
else
show_documentation req, res
end
rescue WEBrick::HTTPStatus::NotFound => e
not_found @generator, req, res, e.message
rescue WEBrick::HTTPStatus::Status
raise
rescue => e
$stderr.puts e.full_message
error e, req, res
end

private

def asset generator_name, req, res
asset_dir = @asset_dirs[generator_name]

asset_path = File.join asset_dir, req.path

res.body = File.read asset_path

res.content_type = case req.path
when /\.css\z/ then 'text/css'
when /\.js\z/ then 'application/javascript'
else 'application/octet-stream'
end
end

def error exception, req, res
backtrace = exception.backtrace.join "\n"

res.content_type = 'text/html'
res.status = 500
res.body = <<-BODY
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<title>Error - #{ERB::Util.html_escape exception.class}</title>
<link type="text/css" media="screen" href="/css/rdoc.css" rel="stylesheet">
</head>
<body>
<h1>Error</h1>
<p>While processing <code>#{ERB::Util.html_escape req.request_uri}</code> the
RDoc (#{ERB::Util.html_escape RDoc::VERSION}) server has encountered a
<code>#{ERB::Util.html_escape exception.class}</code>
exception:
<pre>#{ERB::Util.html_escape exception.message}</pre>
<p>Please report this to the
<a href="https://github.com/ruby/rdoc/issues">RDoc issues tracker</a>. Please
include the RDoc version, the URI above and exception class, message and
backtrace. If you're viewing a gem's documentation, include the gem name and
version. If you're viewing Ruby's documentation, include the version of ruby.
<p>Backtrace:
<pre>#{ERB::Util.html_escape backtrace}</pre>
</body>
</html>
BODY
end

def not_found generator, req, res, message = nil
message ||= "The page <kbd>#{ERB::Util.h req.path}</kbd> was not found"
res.body = generator.generate_servlet_not_found message
res.status = 404
end

PAGE_NAME_SUB_REGEXP = /_([^_]*)\z/

def show_documentation req, res
store = @rdoc.store
# Clear all the previous data
store.classes_hash.clear
store.modules_hash.clear
store.files_hash.clear

# RDoc instance use last_modified list to avoid reparsing files
# We need to clear it to force reparsing
@rdoc.last_modified.clear

# Reparse the files
@rdoc.parse_files(@rdoc.options.files)

# Regenerate the documentation and asserts
@generator.generate(server_mode: true)

case req.path
when nil, '', 'index.html'
res.body = @generator.generate_index
when 'table_of_contents.html'
res.body = @generator.generate_table_of_contents
else
text_name = req.path.chomp '.html'
name = text_name.gsub '/', '::'

content = if klass = store.find_class_or_module(name)
@generator.generate_class klass
elsif page = store.find_text_page(name.sub(PAGE_NAME_SUB_REGEXP, '.\1'))
@generator.generate_page page
elsif page = store.find_text_page(text_name.sub(PAGE_NAME_SUB_REGEXP, '.\1'))
@generator.generate_page page
elsif page = store.find_file_named(text_name.sub(PAGE_NAME_SUB_REGEXP, '.\1'))
@generator.generate_page page
end

if content
res.body = content
else
not_found @generator, req, res
end
end
ensure
res.content_type ||= 'text/html'
end
end
1 change: 1 addition & 0 deletions rdoc.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ RDoc includes the +rdoc+ and +ri+ tools for generating and displaying documentat
"lib/rdoc/ri/task.rb",
"lib/rdoc/ri/servlet.rb",
"lib/rdoc/rubygems_hook.rb",
"lib/rdoc/server.rb",
"lib/rdoc/single_class.rb",
"lib/rdoc/stats.rb",
"lib/rdoc/stats/normal.rb",
Expand Down
3 changes: 2 additions & 1 deletion test/rdoc/test_rdoc_options.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative 'helper'

class TestRDocOptions < RDoc::TestCase
class RDocOptionsTest < RDoc::TestCase

def setup
super
Expand Down Expand Up @@ -82,6 +82,7 @@ def test_to_yaml
'title' => nil,
'visibility' => :protected,
'webcvs' => nil,
'server_port' => false,
'skip_tests' => true,
}

Expand Down

0 comments on commit 27a8909

Please sign in to comment.