bitclust-core 1.3.0 → 1.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.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/README.md +65 -0
- data/Rakefile +32 -0
- data/data/bitclust/catalog/ja_JP.UTF-8 +19 -1
- data/data/bitclust/searchpage/index.html +90 -0
- data/data/bitclust/template/class +69 -8
- data/data/bitclust/template/function +2 -2
- data/data/bitclust/template/layout +11 -3
- data/data/bitclust/template/method +2 -2
- data/data/bitclust/template/search +1 -1
- data/data/bitclust/template.epub/class +22 -1
- data/data/bitclust/template.epub/function +2 -2
- data/data/bitclust/template.epub/layout +1 -3
- data/data/bitclust/template.epub/method +2 -2
- data/data/bitclust/template.lillia/class +26 -5
- data/data/bitclust/template.lillia/layout +1 -3
- data/data/bitclust/template.lillia/method +2 -2
- data/data/bitclust/template.offline/class +48 -4
- data/data/bitclust/template.offline/function +2 -2
- data/data/bitclust/template.offline/layout +27 -3
- data/data/bitclust/template.offline/method +2 -2
- data/lib/bitclust/app.rb +16 -17
- data/lib/bitclust/capi_converter.rb +20 -0
- data/lib/bitclust/classentry.rb +67 -17
- data/lib/bitclust/compat.rb +2 -2
- data/lib/bitclust/completion.rb +58 -27
- data/lib/bitclust/crossrubyutils.rb +8 -5
- data/lib/bitclust/database.rb +28 -8
- data/lib/bitclust/doc_converter.rb +63 -0
- data/lib/bitclust/docentry.rb +1 -1
- data/lib/bitclust/entity_splitter.rb +221 -0
- data/lib/bitclust/entry.rb +47 -7
- data/lib/bitclust/functiondatabase.rb +13 -1
- data/lib/bitclust/functionentry.rb +5 -1
- data/lib/bitclust/functionreferenceparser.rb +4 -4
- data/lib/bitclust/generators/epub.rb +10 -3
- data/lib/bitclust/htmlutils.rb +1 -1
- data/lib/bitclust/include_graph.rb +375 -0
- data/lib/bitclust/include_pruner.rb +144 -0
- data/lib/bitclust/interface.rb +1 -1
- data/lib/bitclust/libraryentry.rb +8 -7
- data/lib/bitclust/lineinput.rb +7 -5
- data/lib/bitclust/link_checker.rb +179 -0
- data/lib/bitclust/markdown_bridge.rb +186 -0
- data/lib/bitclust/markdown_orchestrator.rb +267 -0
- data/lib/bitclust/markdown_to_rrd.rb +824 -0
- data/lib/bitclust/markdown_tree.rb +217 -0
- data/lib/bitclust/mdcompiler.rb +883 -0
- data/lib/bitclust/mdparser.rb +347 -0
- data/lib/bitclust/messagecatalog.rb +3 -3
- data/lib/bitclust/method_since_calculator.rb +117 -0
- data/lib/bitclust/methoddatabase.rb +124 -14
- data/lib/bitclust/methodentry.rb +125 -7
- data/lib/bitclust/methodid.rb +12 -11
- data/lib/bitclust/methodsignature.rb +10 -4
- data/lib/bitclust/nameutils.rb +41 -22
- data/lib/bitclust/preprocessor.rb +66 -29
- data/lib/bitclust/rdcompiler.rb +195 -95
- data/lib/bitclust/refsdatabase.rb +23 -8
- data/lib/bitclust/reloadable_request_handler.rb +72 -0
- data/lib/bitclust/requesthandler.rb +21 -16
- data/lib/bitclust/ridatabase.rb +10 -4
- data/lib/bitclust/rrd_to_markdown.rb +925 -0
- data/lib/bitclust/rrdparser.rb +196 -30
- data/lib/bitclust/runner.rb +17 -2
- data/lib/bitclust/screen.rb +80 -11
- data/lib/bitclust/search_index_generator.rb +288 -0
- data/lib/bitclust/searcher.rb +69 -20
- data/lib/bitclust/simplesearcher.rb +24 -12
- data/lib/bitclust/subcommand.rb +7 -1
- data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
- data/lib/bitclust/subcommands/checklink_command.rb +54 -0
- data/lib/bitclust/subcommands/chm_command.rb +15 -7
- data/lib/bitclust/subcommands/classes_command.rb +5 -0
- data/lib/bitclust/subcommands/extract_command.rb +5 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
- data/lib/bitclust/subcommands/init_command.rb +4 -1
- data/lib/bitclust/subcommands/list_command.rb +12 -4
- data/lib/bitclust/subcommands/lookup_command.rb +28 -9
- data/lib/bitclust/subcommands/methods_command.rb +10 -2
- data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
- data/lib/bitclust/subcommands/preproc_command.rb +5 -0
- data/lib/bitclust/subcommands/property_command.rb +2 -1
- data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
- data/lib/bitclust/subcommands/server_command.rb +10 -8
- data/lib/bitclust/subcommands/setup_command.rb +24 -10
- data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
- data/lib/bitclust/subcommands/update_command.rb +110 -2
- data/lib/bitclust/syntax_highlighter.rb +48 -4
- data/lib/bitclust/textutils.rb +3 -3
- data/lib/bitclust/version.rb +1 -1
- data/lib/bitclust/version_badges.rb +90 -0
- data/lib/bitclust/whole_file_gate.rb +137 -0
- data/test/test_app.rb +269 -0
- data/test/test_bitclust.rb +2 -0
- data/test/test_capi_converter.rb +91 -0
- data/test/test_class_screen.rb +413 -0
- data/test/test_copy_doc_md.rb +53 -0
- data/test/test_doc_converter.rb +78 -0
- data/test/test_entity_splitter.rb +231 -0
- data/test/test_entry.rb +207 -2
- data/test/test_eol_warning.rb +43 -0
- data/test/test_functionreferenceparser.rb +11 -0
- data/test/test_include_graph.rb +640 -0
- data/test/test_include_pruner.rb +133 -0
- data/test/test_link_checker.rb +114 -0
- data/test/test_lookup_command.rb +135 -0
- data/test/test_markdown_bridge.rb +217 -0
- data/test/test_markdown_orchestrator.rb +478 -0
- data/test/test_markdown_to_rrd.rb +757 -0
- data/test/test_markdown_tree.rb +369 -0
- data/test/test_mdcompiler.rb +1276 -0
- data/test/test_mdparser.rb +538 -0
- data/test/test_method_screen.rb +166 -0
- data/test/test_method_since_calculator.rb +290 -0
- data/test/test_methoddatabase.rb +16 -0
- data/test/test_methodentry.rb +328 -0
- data/test/test_methodsignature.rb +3 -1
- data/test/test_methodsince_command.rb +168 -0
- data/test/test_nameutils.rb +22 -0
- data/test/test_preprocessor.rb +215 -0
- data/test/test_rdcompiler.rb +401 -30
- data/test/test_refsdatabase.rb +32 -0
- data/test/test_rrd_to_markdown.rb +968 -0
- data/test/test_rrdparser.rb +295 -2
- data/test/test_run_ruby_wasm.rb +54 -0
- data/test/test_run_worker_prelude.rb +83 -0
- data/test/test_runner.rb +51 -0
- data/test/test_search_index_generator.rb +513 -0
- data/test/test_search_screen.rb +48 -0
- data/test/test_searcher.rb +132 -0
- data/test/test_searchpage_command.rb +167 -0
- data/test/test_simplesearcher.rb +28 -0
- data/test/test_statichtml_command.rb +204 -0
- data/test/test_syntax_highlighter.rb +60 -0
- data/test/test_whole_file_gate.rb +175 -0
- data/theme/default/js/NOTICE +42 -0
- data/theme/default/js/run-worker.js +111 -0
- data/theme/default/js/run.js +350 -0
- data/theme/default/js/search_controller.js +134 -0
- data/theme/default/js/search_init.js +176 -0
- data/theme/default/js/search_navigation.js +110 -0
- data/theme/default/js/search_page.js +202 -0
- data/theme/default/js/search_ranker.js +244 -0
- data/theme/default/script.js +91 -18
- data/theme/default/search.css +149 -0
- data/theme/default/style.css +193 -21
- data/theme/default/syntax-highlight.css +8 -0
- data/theme/lillia/style.css +45 -1
- metadata +161 -20
- data/README +0 -32
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'bitclust/mdparser'
|
|
7
|
+
require 'bitclust/rrdparser'
|
|
8
|
+
require 'bitclust/methoddatabase'
|
|
9
|
+
|
|
10
|
+
# MDParser: Markdown ソースを直接パースして DB エントリを作る(フェーズ3 M3)。
|
|
11
|
+
# 構造(クラス・エントリ・関係)は同等 rd の RRDParser と一致し、
|
|
12
|
+
# source には md 断片がそのまま入る。
|
|
13
|
+
class TestMDParser < Test::Unit::TestCase
|
|
14
|
+
PARAMS = { "version" => "3.4" }
|
|
15
|
+
|
|
16
|
+
def parse_md(md, libname = "_builtin")
|
|
17
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
18
|
+
lib = BitClust::MDParser.new(db).parse(StringIO.new(md), libname, PARAMS)
|
|
19
|
+
[db, lib]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def parse_rd(rd, libname = "_builtin")
|
|
23
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
24
|
+
lib = BitClust::RRDParser.new(db).parse(StringIO.new(rd), libname, PARAMS)
|
|
25
|
+
[db, lib]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_class_with_include_and_method
|
|
29
|
+
md = <<~MD
|
|
30
|
+
---
|
|
31
|
+
library: _builtin
|
|
32
|
+
include:
|
|
33
|
+
- Enumerable
|
|
34
|
+
---
|
|
35
|
+
# class Array < Object
|
|
36
|
+
|
|
37
|
+
配列クラス。
|
|
38
|
+
|
|
39
|
+
## Instance Methods
|
|
40
|
+
|
|
41
|
+
### def index(val) -> Integer
|
|
42
|
+
|
|
43
|
+
説明。
|
|
44
|
+
MD
|
|
45
|
+
rd = <<~RD
|
|
46
|
+
= class Array < Object
|
|
47
|
+
include Enumerable
|
|
48
|
+
|
|
49
|
+
配列クラス。
|
|
50
|
+
|
|
51
|
+
== Instance Methods
|
|
52
|
+
|
|
53
|
+
--- index(val) -> Integer
|
|
54
|
+
|
|
55
|
+
説明。
|
|
56
|
+
RD
|
|
57
|
+
_, mlib = parse_md(md)
|
|
58
|
+
_, rlib = parse_rd(rd)
|
|
59
|
+
|
|
60
|
+
mc = mlib.classes.first
|
|
61
|
+
rc = rlib.classes.first
|
|
62
|
+
assert_equal rc.name, mc.name
|
|
63
|
+
assert_equal rc.type, mc.type
|
|
64
|
+
assert_equal rc.superclass&.name, mc.superclass&.name
|
|
65
|
+
assert_equal rc.included.map(&:name), mc.included.map(&:name)
|
|
66
|
+
assert_equal rc.entries.map(&:names), mc.entries.map(&:names)
|
|
67
|
+
assert_equal rc.entries.first.type, mc.entries.first.type
|
|
68
|
+
|
|
69
|
+
# source は md のまま
|
|
70
|
+
assert_equal "配列クラス。", mc.source
|
|
71
|
+
assert_equal "### def index(val) -> Integer\n\n説明。\n", mc.entries.first.source
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_module_and_class_methods_section
|
|
75
|
+
md = <<~MD
|
|
76
|
+
---
|
|
77
|
+
library: _builtin
|
|
78
|
+
---
|
|
79
|
+
# module Comparable
|
|
80
|
+
|
|
81
|
+
比較演算モジュール。
|
|
82
|
+
|
|
83
|
+
## Class Methods
|
|
84
|
+
|
|
85
|
+
### def new -> Comparable
|
|
86
|
+
|
|
87
|
+
生成。
|
|
88
|
+
MD
|
|
89
|
+
_, lib = parse_md(md)
|
|
90
|
+
c = lib.classes.first
|
|
91
|
+
assert_equal "Comparable", c.name
|
|
92
|
+
assert_equal :module, c.type
|
|
93
|
+
assert_equal :singleton_method, c.entries.first.type
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_constants_section_with_const_keyword
|
|
97
|
+
md = <<~MD
|
|
98
|
+
---
|
|
99
|
+
library: _builtin
|
|
100
|
+
---
|
|
101
|
+
# class Float < Numeric
|
|
102
|
+
|
|
103
|
+
浮動小数点数。
|
|
104
|
+
|
|
105
|
+
## Constants
|
|
106
|
+
|
|
107
|
+
### const DIG -> Integer
|
|
108
|
+
|
|
109
|
+
桁数。
|
|
110
|
+
MD
|
|
111
|
+
_, lib = parse_md(md)
|
|
112
|
+
e = lib.classes.first.entries.first
|
|
113
|
+
assert_equal :constant, e.type
|
|
114
|
+
assert_equal ["DIG"], e.names
|
|
115
|
+
assert_equal "### const DIG -> Integer\n\n桁数。\n", e.source
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_reopen_with_dynamic_include_across_files
|
|
119
|
+
# ライブラリはファイル単位で組み立てる(module 定義とreopen が別ファイル、
|
|
120
|
+
# 同じ libname で順にパースすると同一 Library に合流する)
|
|
121
|
+
module_md = <<~MD
|
|
122
|
+
---
|
|
123
|
+
library: mylib
|
|
124
|
+
---
|
|
125
|
+
# module MyModule
|
|
126
|
+
|
|
127
|
+
モジュール。
|
|
128
|
+
MD
|
|
129
|
+
reopen_md = <<~MD
|
|
130
|
+
---
|
|
131
|
+
library: mylib
|
|
132
|
+
include:
|
|
133
|
+
- MyModule
|
|
134
|
+
---
|
|
135
|
+
# reopen Object
|
|
136
|
+
MD
|
|
137
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
138
|
+
BitClust::MDParser.new(db).parse(StringIO.new(module_md), "mylib", PARAMS)
|
|
139
|
+
BitClust::MDParser.new(db).parse(StringIO.new(reopen_md), "mylib", PARAMS)
|
|
140
|
+
assert_equal ["MyModule"], db.get_class("Object").dynamically_included.map(&:name)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_gvar_signature
|
|
144
|
+
md = <<~MD
|
|
145
|
+
---
|
|
146
|
+
library: _builtin
|
|
147
|
+
---
|
|
148
|
+
# module Kernel
|
|
149
|
+
|
|
150
|
+
カーネル。
|
|
151
|
+
|
|
152
|
+
## Special Variables
|
|
153
|
+
|
|
154
|
+
### gvar $stdin -> IO
|
|
155
|
+
|
|
156
|
+
標準入力。
|
|
157
|
+
MD
|
|
158
|
+
_, lib = parse_md(md)
|
|
159
|
+
e = lib.classes.first.entries.first
|
|
160
|
+
assert_equal :special_variable, e.type
|
|
161
|
+
assert_equal "### gvar $stdin -> IO\n\n標準入力。\n", e.source
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_relations_require_single_entity
|
|
165
|
+
# 案B: 関係(include 等)を持つファイルは単一エンティティでなければならない
|
|
166
|
+
md = <<~MD
|
|
167
|
+
---
|
|
168
|
+
library: _builtin
|
|
169
|
+
include:
|
|
170
|
+
- Enumerable
|
|
171
|
+
---
|
|
172
|
+
# class Foo < Object
|
|
173
|
+
|
|
174
|
+
ふー。
|
|
175
|
+
|
|
176
|
+
# class Bar < Object
|
|
177
|
+
|
|
178
|
+
ばー。
|
|
179
|
+
MD
|
|
180
|
+
assert_raise(BitClust::ParseError) { parse_md(md) }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_unsupported_signature_forms_are_rejected
|
|
184
|
+
# RBS 形式(MARKUP_SPEC §3.2 で構想)と self. プレフィクスは描画側
|
|
185
|
+
# (MethodSignature.parse)が受理できないため、パース時に拒否する
|
|
186
|
+
base = <<~MD
|
|
187
|
+
---
|
|
188
|
+
library: _builtin
|
|
189
|
+
---
|
|
190
|
+
# class Foo < Object
|
|
191
|
+
|
|
192
|
+
テスト。
|
|
193
|
+
|
|
194
|
+
## Instance Methods
|
|
195
|
+
|
|
196
|
+
%s
|
|
197
|
+
|
|
198
|
+
説明。
|
|
199
|
+
MD
|
|
200
|
+
['### def each: () { (untyped) -> void } -> self',
|
|
201
|
+
'### def self.new(size = 0) -> Foo'].each do |sig|
|
|
202
|
+
assert_raise(BitClust::ParseError, sig) { parse_md(base % sig) }
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_library_file_with_category
|
|
207
|
+
md = <<~MD
|
|
208
|
+
---
|
|
209
|
+
type: library
|
|
210
|
+
category: FileFormat
|
|
211
|
+
---
|
|
212
|
+
CSV を扱うライブラリです。
|
|
213
|
+
MD
|
|
214
|
+
_, lib = parse_md(md, "csv")
|
|
215
|
+
assert_equal "FileFormat", lib.category
|
|
216
|
+
assert_equal "CSV を扱うライブラリです。", lib.source
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def test_since_directive_in_body
|
|
220
|
+
md = <<~MD
|
|
221
|
+
---
|
|
222
|
+
library: _builtin
|
|
223
|
+
---
|
|
224
|
+
# class Array < Object
|
|
225
|
+
|
|
226
|
+
配列。
|
|
227
|
+
|
|
228
|
+
## Instance Methods
|
|
229
|
+
|
|
230
|
+
\#@since 3.4
|
|
231
|
+
### def newmethod -> nil
|
|
232
|
+
|
|
233
|
+
新しい。
|
|
234
|
+
\#@end
|
|
235
|
+
MD
|
|
236
|
+
_, lib = parse_md(md)
|
|
237
|
+
assert_equal [["newmethod"]], lib.classes.first.entries.map(&:names)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def test_update_by_markdowntree
|
|
241
|
+
# MarkdownTree 駆動の組み立て: lib ファイル → メンバー(reopen 後置)、
|
|
242
|
+
# 版ゲート外のライブラリ/メンバーはスキップ
|
|
243
|
+
require 'tmpdir'
|
|
244
|
+
Dir.mktmpdir do |root|
|
|
245
|
+
write = ->(rel, s) {
|
|
246
|
+
path = File.join(root, rel)
|
|
247
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
248
|
+
File.write(path, s)
|
|
249
|
+
}
|
|
250
|
+
write.call('mylib.md', <<~MD)
|
|
251
|
+
---
|
|
252
|
+
type: library
|
|
253
|
+
category: Text
|
|
254
|
+
---
|
|
255
|
+
mylib の説明。
|
|
256
|
+
MD
|
|
257
|
+
write.call('mylib/MyModule.md', <<~MD)
|
|
258
|
+
---
|
|
259
|
+
library: mylib
|
|
260
|
+
---
|
|
261
|
+
# module MyModule
|
|
262
|
+
|
|
263
|
+
モジュール。
|
|
264
|
+
MD
|
|
265
|
+
# 辞書順で module より先に来る reopen ファイル(後置されることを確認)
|
|
266
|
+
write.call('mylib/Kernel.md', <<~MD)
|
|
267
|
+
---
|
|
268
|
+
library: mylib
|
|
269
|
+
include:
|
|
270
|
+
- MyModule
|
|
271
|
+
---
|
|
272
|
+
# reopen Object
|
|
273
|
+
MD
|
|
274
|
+
write.call('mylib/Gone.md', <<~MD)
|
|
275
|
+
---
|
|
276
|
+
library: mylib
|
|
277
|
+
until: "3.0"
|
|
278
|
+
---
|
|
279
|
+
# class Gone < Object
|
|
280
|
+
|
|
281
|
+
3.0 で消えた。
|
|
282
|
+
MD
|
|
283
|
+
write.call('oldlib.md', <<~MD)
|
|
284
|
+
---
|
|
285
|
+
type: library
|
|
286
|
+
until: "2.0"
|
|
287
|
+
---
|
|
288
|
+
古いライブラリ。
|
|
289
|
+
MD
|
|
290
|
+
|
|
291
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
292
|
+
db.update_by_markdowntree(root)
|
|
293
|
+
|
|
294
|
+
assert_equal ["mylib"], db.libraries.map(&:name)
|
|
295
|
+
lib = db.fetch_library("mylib")
|
|
296
|
+
assert_equal "Text", lib.category
|
|
297
|
+
assert_equal "mylib の説明。", lib.source
|
|
298
|
+
# reopen はメソッド定義が無い限り lib.classes に載らない(rd と同じ)
|
|
299
|
+
assert_equal %w[MyModule], lib.classes.map(&:name).sort
|
|
300
|
+
assert_equal ["MyModule"], db.get_class("Object").dynamically_included.map(&:name)
|
|
301
|
+
# source_location は md の実パス
|
|
302
|
+
assert_equal "#{root}/mylib.md", lib.source_location.file
|
|
303
|
+
assert_equal "#{root}/mylib/MyModule.md", db.get_class("MyModule").source_location.file
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def test_update_by_markdowntree_gated_memberships
|
|
308
|
+
# 多重所属(ゲート付き library リスト): 版によって所属ライブラリが変わる。
|
|
309
|
+
# 3.4 では builtin のみ、3.0 では builtin と oldlib の両方に属する
|
|
310
|
+
require 'tmpdir'
|
|
311
|
+
Dir.mktmpdir do |root|
|
|
312
|
+
write = ->(rel, s) {
|
|
313
|
+
path = File.join(root, rel)
|
|
314
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
315
|
+
File.write(path, s)
|
|
316
|
+
}
|
|
317
|
+
write.call('builtin.md', "---\ntype: library\n---\nbuiltin。\n")
|
|
318
|
+
write.call('oldlib.md', "---\ntype: library\n---\noldlib。\n")
|
|
319
|
+
write.call('oldlib/Shared.md', <<~'MD')
|
|
320
|
+
---
|
|
321
|
+
library:
|
|
322
|
+
- builtin
|
|
323
|
+
#@until 3.2
|
|
324
|
+
- oldlib
|
|
325
|
+
#@end
|
|
326
|
+
---
|
|
327
|
+
# class Shared < Object
|
|
328
|
+
|
|
329
|
+
共有クラス。
|
|
330
|
+
MD
|
|
331
|
+
|
|
332
|
+
db34 = BitClust::MethodDatabase.dummy("version" => "3.4")
|
|
333
|
+
db34.update_by_markdowntree(root)
|
|
334
|
+
assert_equal %w[Shared], db34.fetch_library("builtin").classes.map(&:name)
|
|
335
|
+
assert_equal [], db34.fetch_library("oldlib").classes.map(&:name)
|
|
336
|
+
|
|
337
|
+
db30 = BitClust::MethodDatabase.dummy("version" => "3.0")
|
|
338
|
+
db30.update_by_markdowntree(root)
|
|
339
|
+
assert_equal %w[Shared], db30.fetch_library("builtin").classes.map(&:name)
|
|
340
|
+
assert_equal %w[Shared], db30.fetch_library("oldlib").classes.map(&:name)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def test_copy_doc_md_keeps_relative_location
|
|
345
|
+
# Windows CI 対応: doc エントリの source_location は md_root と同じ形
|
|
346
|
+
# (相対で渡せば相対 manual/doc/...)で格納する。絶対パス化すると
|
|
347
|
+
# Windows のドライブレター(D:)のコロンで Location の
|
|
348
|
+
# デシリアライズ(file:line 分割)が壊れる
|
|
349
|
+
require 'tmpdir'
|
|
350
|
+
Dir.mktmpdir do |root|
|
|
351
|
+
write = ->(rel, s) {
|
|
352
|
+
path = File.join(root, rel)
|
|
353
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
354
|
+
File.write(path, s)
|
|
355
|
+
}
|
|
356
|
+
write.call('manual/api/mylib.md', <<~MD)
|
|
357
|
+
---
|
|
358
|
+
type: library
|
|
359
|
+
category: Text
|
|
360
|
+
---
|
|
361
|
+
mylib の説明。
|
|
362
|
+
MD
|
|
363
|
+
write.call('manual/doc/help.md', "= ヘルプ\n\n本文。\n")
|
|
364
|
+
write.call('manual/doc/parts.md', "\#@include(included)\n")
|
|
365
|
+
write.call('manual/doc/included.md', "= 部品\n\n部品本文。\n")
|
|
366
|
+
|
|
367
|
+
db = BitClust::MethodDatabase.new(File.join(root, 'db'))
|
|
368
|
+
db.init
|
|
369
|
+
db.transaction {
|
|
370
|
+
db.propset 'version', '3.4'
|
|
371
|
+
db.propset 'encoding', 'utf-8'
|
|
372
|
+
}
|
|
373
|
+
Dir.chdir(root) do
|
|
374
|
+
db.transaction {
|
|
375
|
+
db.update_by_markdowntree('manual/api')
|
|
376
|
+
}
|
|
377
|
+
end
|
|
378
|
+
doc_ids = db.docs.map(&:name).sort
|
|
379
|
+
assert_equal %w[help parts], doc_ids # include 参照先はページにしない
|
|
380
|
+
assert_equal 'manual/doc/help.md',
|
|
381
|
+
db.docs.find { |d| d.name == 'help' }.source_location.file.to_s
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def test_location_deserialize_drops_line
|
|
386
|
+
# source_location の永続化はファイルのみ(行番号は生成物の diff を安定させる
|
|
387
|
+
# ため落とす。master b1d81d7 のポリシー)。旧形式の file:line も
|
|
388
|
+
# 後方互換で file だけを読む。Windows で壊れないよう doc の格納パスは
|
|
389
|
+
# 相対に保つ(copy_doc_md 側の慣例)
|
|
390
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
391
|
+
e = BitClust::DocEntry.new(db, 'x')
|
|
392
|
+
e.__send__(:_set_properties,
|
|
393
|
+
{ 'source_location' => 'manual/doc/help.md:12' })
|
|
394
|
+
loc = e.instance_variable_get(:@source_location)
|
|
395
|
+
assert_equal 'manual/doc/help.md', loc.file
|
|
396
|
+
assert_nil loc.line
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def test_function_parser
|
|
400
|
+
# capi: キーワード無しの ### 全行がシグネチャ。source は本文のみ(md のまま)
|
|
401
|
+
require 'bitclust/functiondatabase'
|
|
402
|
+
md = <<~MD
|
|
403
|
+
### VALUE rb_ary_new()
|
|
404
|
+
|
|
405
|
+
空の Ruby の配列を作成し返します。
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
VALUE ary = rb_ary_new();
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### static int rb_ary_size(VALUE ary)
|
|
412
|
+
|
|
413
|
+
内部関数です。
|
|
414
|
+
MD
|
|
415
|
+
db = BitClust::FunctionDatabase.dummy(PARAMS)
|
|
416
|
+
io = StringIO.new(md)
|
|
417
|
+
def io.path = "array.c.md"
|
|
418
|
+
BitClust::MDFunctionParser.new(db).parse(io, "array.c", PARAMS)
|
|
419
|
+
funcs = db.functions.sort_by(&:name)
|
|
420
|
+
assert_equal %w[rb_ary_new rb_ary_size], funcs.map(&:name)
|
|
421
|
+
f = funcs.first
|
|
422
|
+
assert_equal "array.c", f.filename
|
|
423
|
+
assert_equal "VALUE", f.type
|
|
424
|
+
# body はヘッダ直後の空行込み(レガシー FunctionReferenceParser と同じ)
|
|
425
|
+
assert_equal "\n空の Ruby の配列を作成し返します。\n\n```\nVALUE ary = rb_ary_new();\n```\n\n",
|
|
426
|
+
f.source
|
|
427
|
+
assert funcs.last.private
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def test_multi_entity_bundle_without_relations
|
|
431
|
+
md = <<~MD
|
|
432
|
+
---
|
|
433
|
+
library: _builtin
|
|
434
|
+
---
|
|
435
|
+
# class Errno::EPERM < SystemCallError
|
|
436
|
+
|
|
437
|
+
EPERM。
|
|
438
|
+
|
|
439
|
+
# class Errno::ENOENT < SystemCallError
|
|
440
|
+
|
|
441
|
+
ENOENT。
|
|
442
|
+
MD
|
|
443
|
+
_, lib = parse_md(md)
|
|
444
|
+
assert_equal %w[Errno::EPERM Errno::ENOENT], lib.classes.map(&:name)
|
|
445
|
+
end
|
|
446
|
+
def test_method_attribute_list_sets_kind
|
|
447
|
+
md = <<~MD
|
|
448
|
+
---
|
|
449
|
+
library: _builtin
|
|
450
|
+
---
|
|
451
|
+
# class Hoge
|
|
452
|
+
|
|
453
|
+
クラスの説明。
|
|
454
|
+
|
|
455
|
+
## Instance Methods
|
|
456
|
+
|
|
457
|
+
### def fuga -> nil
|
|
458
|
+
{: nomethod}
|
|
459
|
+
|
|
460
|
+
説明のためのエントリです。
|
|
461
|
+
|
|
462
|
+
### def piyo -> nil
|
|
463
|
+
{: undef}
|
|
464
|
+
MD
|
|
465
|
+
db, = parse_md(md)
|
|
466
|
+
assert_equal(:nomethod, db.get_method(BitClust::MethodSpec.parse('Hoge#fuga')).kind)
|
|
467
|
+
assert_equal(:undefined, db.get_method(BitClust::MethodSpec.parse('Hoge#piyo')).kind)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def test_method_attribute_on_every_alias_signature
|
|
471
|
+
md = <<~MD
|
|
472
|
+
---
|
|
473
|
+
library: _builtin
|
|
474
|
+
---
|
|
475
|
+
# class Hoge
|
|
476
|
+
|
|
477
|
+
クラスの説明。
|
|
478
|
+
|
|
479
|
+
## Instance Methods
|
|
480
|
+
|
|
481
|
+
### def fuga -> nil
|
|
482
|
+
{: nomethod}
|
|
483
|
+
### def fuga2 -> nil
|
|
484
|
+
{: nomethod}
|
|
485
|
+
|
|
486
|
+
説明のためのエントリです。
|
|
487
|
+
MD
|
|
488
|
+
db, = parse_md(md)
|
|
489
|
+
fuga = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
|
|
490
|
+
assert_equal(:nomethod, fuga.kind)
|
|
491
|
+
assert_equal(['fuga', 'fuga2'], fuga.names)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def test_method_attribute_since_is_recorded
|
|
495
|
+
md = <<~MD
|
|
496
|
+
---
|
|
497
|
+
library: _builtin
|
|
498
|
+
---
|
|
499
|
+
# class Hoge
|
|
500
|
+
|
|
501
|
+
クラスの説明。
|
|
502
|
+
|
|
503
|
+
## Instance Methods
|
|
504
|
+
|
|
505
|
+
### def fuga -> nil
|
|
506
|
+
{: since="3.1"}
|
|
507
|
+
|
|
508
|
+
説明のためのエントリです。
|
|
509
|
+
MD
|
|
510
|
+
db, = parse_md(md)
|
|
511
|
+
entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
|
|
512
|
+
assert_equal('3.1', entry.since_of('fuga'))
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def test_method_attribute_since_differs_per_alias_signature
|
|
516
|
+
md = <<~MD
|
|
517
|
+
---
|
|
518
|
+
library: _builtin
|
|
519
|
+
---
|
|
520
|
+
# class Hoge
|
|
521
|
+
|
|
522
|
+
クラスの説明。
|
|
523
|
+
|
|
524
|
+
## Instance Methods
|
|
525
|
+
|
|
526
|
+
### def fuga -> nil
|
|
527
|
+
{: since="2.0.0"}
|
|
528
|
+
### def fuga2 -> nil
|
|
529
|
+
{: since="3.0"}
|
|
530
|
+
|
|
531
|
+
説明のためのエントリです。
|
|
532
|
+
MD
|
|
533
|
+
db, = parse_md(md)
|
|
534
|
+
entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
|
|
535
|
+
assert_equal('2.0.0', entry.since_of('fuga'))
|
|
536
|
+
assert_equal('3.0', entry.since_of('fuga2'))
|
|
537
|
+
end
|
|
538
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'bitclust'
|
|
3
|
+
require 'bitclust/screen'
|
|
4
|
+
|
|
5
|
+
# Covers data/bitclust/template.offline, used by `bitclust statichtml` (the
|
|
6
|
+
# production docs.ruby-lang.org build) and `bitclust chm`.
|
|
7
|
+
#
|
|
8
|
+
# NOTE: only one templatedir is exercised here on purpose. Screen#run_template
|
|
9
|
+
# caches the compiled ERB template methods on the *class* (MethodScreen),
|
|
10
|
+
# keyed only by method name (e.g. "method_template"), regardless of which
|
|
11
|
+
# @template_repository the instance was built with. So constructing
|
|
12
|
+
# MethodScreen from two different templatedirs (e.g. this one and
|
|
13
|
+
# data/bitclust/template) within the same process is unreliable: whichever
|
|
14
|
+
# templatedir's ERB gets compiled first "wins" for every MethodScreen built
|
|
15
|
+
# afterwards, in every test file, for the rest of the run. This is a
|
|
16
|
+
# pre-existing quirk of Screen, not something introduced here.
|
|
17
|
+
#
|
|
18
|
+
# data/bitclust/template, data/bitclust/template.lillia and
|
|
19
|
+
# data/bitclust/template.epub were hand-verified (each in its own process,
|
|
20
|
+
# not committed as an automated test here) to build @title the same way as
|
|
21
|
+
# template.offline below, just keeping their pre-existing
|
|
22
|
+
# "#{entry.type_label} #{entry.label}" prefix -- i.e. the fix is
|
|
23
|
+
# entry.label -> entry.title_labels.join(', ') in all four `method`
|
|
24
|
+
# templates, so the CHM (template) and EPUB (template.epub) screens get the
|
|
25
|
+
# same alias listing.
|
|
26
|
+
class TestMethodScreenTitle < Test::Unit::TestCase
|
|
27
|
+
SRC = <<'HERE'
|
|
28
|
+
= class Enumerable2 < Object
|
|
29
|
+
enumerable class
|
|
30
|
+
== Instance Methods
|
|
31
|
+
--- collect -> Array
|
|
32
|
+
--- map -> Array
|
|
33
|
+
|
|
34
|
+
説明のためのエントリ
|
|
35
|
+
|
|
36
|
+
--- select -> Array
|
|
37
|
+
|
|
38
|
+
説明2
|
|
39
|
+
HERE
|
|
40
|
+
|
|
41
|
+
def setup
|
|
42
|
+
@lib, @db = BitClust::RRDParser.parse(SRC, 'testlib')
|
|
43
|
+
datadir = File.expand_path('../data/bitclust', __dir__)
|
|
44
|
+
@manager = BitClust::ScreenManager.new(
|
|
45
|
+
:templatedir => "#{datadir}/template.offline",
|
|
46
|
+
:catalogdir => "#{datadir}/catalog",
|
|
47
|
+
:encoding => 'utf-8',
|
|
48
|
+
:default_encoding => 'utf-8',
|
|
49
|
+
:base_url => '',
|
|
50
|
+
:target_version => '3.4'
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def method_html(spec)
|
|
55
|
+
entry = @db.get_method(BitClust::MethodSpec.parse(spec))
|
|
56
|
+
@manager.method_screen([entry], :database => @db).body
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def title_of(html)
|
|
60
|
+
html[/<title>(.*?) \(Ruby/, 1]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# template.offline's <title> has never included the "instance method"
|
|
64
|
+
# type_label prefix (only entry.label); the alias fix must keep that
|
|
65
|
+
# pre-existing convention and just expand the single name to every alias.
|
|
66
|
+
def test_title_lists_every_alias
|
|
67
|
+
assert_equal('Enumerable2#collect, Enumerable2#map',
|
|
68
|
+
title_of(method_html('Enumerable2#collect')))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Both aliases are the very same entry, so they share one page/title
|
|
72
|
+
# regardless of which alias URL was requested.
|
|
73
|
+
def test_title_is_the_same_regardless_of_which_alias_was_requested
|
|
74
|
+
assert_equal(method_html('Enumerable2#collect'), method_html('Enumerable2#map'))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# A method without any alias renders exactly as before: only its own name.
|
|
78
|
+
def test_title_is_unchanged_for_a_method_without_aliases
|
|
79
|
+
html = method_html('Enumerable2#select')
|
|
80
|
+
assert_equal('Enumerable2#select', title_of(html))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Only the <title> tag changes; the on-page h1 headline keeps showing only
|
|
84
|
+
# the entry's own (alphabetically-first) name, as before.
|
|
85
|
+
def test_h1_headline_is_not_affected
|
|
86
|
+
html = method_html('Enumerable2#collect')
|
|
87
|
+
assert_match(%r{<h1>instance method Enumerable2\#collect</h1>}, html)
|
|
88
|
+
assert_not_match(/<h1>[^<]*Enumerable2#map/, html)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# bitclust#132 P3: since/until バージョンバッジが method ページ(template.offline、
|
|
93
|
+
# statichtml/chm が使う経路)の <dt> に実際に描画されることのエンドツーエンド確認
|
|
94
|
+
class TestMethodScreenVersionBadges < Test::Unit::TestCase
|
|
95
|
+
SRC = <<'HERE'
|
|
96
|
+
= class Enumerable2 < Object
|
|
97
|
+
enumerable class
|
|
98
|
+
== Instance Methods
|
|
99
|
+
--- select -> Array
|
|
100
|
+
|
|
101
|
+
説明
|
|
102
|
+
HERE
|
|
103
|
+
|
|
104
|
+
def setup
|
|
105
|
+
@lib, @db = BitClust::RRDParser.parse(SRC, 'testlib')
|
|
106
|
+
datadir = File.expand_path('../data/bitclust', __dir__)
|
|
107
|
+
@manager = BitClust::ScreenManager.new(
|
|
108
|
+
:templatedir => "#{datadir}/template.offline",
|
|
109
|
+
:catalogdir => "#{datadir}/catalog",
|
|
110
|
+
:encoding => 'utf-8',
|
|
111
|
+
:default_encoding => 'utf-8',
|
|
112
|
+
:base_url => '',
|
|
113
|
+
:target_version => '3.4'
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_since_badge_is_rendered_after_fill_since
|
|
118
|
+
entry = @db.get_method(BitClust::MethodSpec.parse('Enumerable2#select'))
|
|
119
|
+
entry.fill_since('select', '3.2')
|
|
120
|
+
html = @manager.method_screen([entry], :database => @db).body
|
|
121
|
+
assert_include(html, '<span class="method-since-badge">Ruby 3.2 から</span>')
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# bitclust#250: Ruby 4.0 以降のドキュメントでは module function の表記を
|
|
126
|
+
# 独自の「.#」から「?.」に変える(表示のみ、識別子は変えない)。この
|
|
127
|
+
# メソッドページの見出し・<title> がまさに issue に挙がっている
|
|
128
|
+
# 「module function Kernel.#open」の例そのもの
|
|
129
|
+
class TestMethodScreenModuleFunctionDisplay < Test::Unit::TestCase
|
|
130
|
+
SRC = <<'HERE'
|
|
131
|
+
= module Kernel
|
|
132
|
+
description
|
|
133
|
+
== Module Functions
|
|
134
|
+
--- mf
|
|
135
|
+
|
|
136
|
+
説明
|
|
137
|
+
HERE
|
|
138
|
+
|
|
139
|
+
def render(version)
|
|
140
|
+
_lib, db = BitClust::RRDParser.parse(SRC, 'testlib', {'version' => version})
|
|
141
|
+
entry = db.get_method(BitClust::MethodSpec.parse('Kernel.#mf'))
|
|
142
|
+
datadir = File.expand_path('../data/bitclust', __dir__)
|
|
143
|
+
manager = BitClust::ScreenManager.new(
|
|
144
|
+
:templatedir => "#{datadir}/template.offline",
|
|
145
|
+
:catalogdir => "#{datadir}/catalog",
|
|
146
|
+
:encoding => 'utf-8',
|
|
147
|
+
:default_encoding => 'utf-8',
|
|
148
|
+
:base_url => '',
|
|
149
|
+
:target_version => version
|
|
150
|
+
)
|
|
151
|
+
manager.method_screen([entry], :database => db).body
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def test_headline_and_title_show_dot_hash_before_4_0
|
|
155
|
+
html = render('3.4')
|
|
156
|
+
assert_match(%r{<h1>module function Kernel\.#mf</h1>}, html)
|
|
157
|
+
assert_include(html, '<title>Kernel.#mf')
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def test_headline_and_title_switch_to_question_dot_at_4_0
|
|
161
|
+
html = render('4.0')
|
|
162
|
+
assert_match(%r{<h1>module function Kernel\?\.mf</h1>}, html)
|
|
163
|
+
assert_include(html, '<title>Kernel?.mf')
|
|
164
|
+
assert_not_include(html, 'Kernel.#mf')
|
|
165
|
+
end
|
|
166
|
+
end
|