bitclust-core 1.8.0 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 299762c07571e6e3b72eebe00c9d1e1d520ef9f5c3b68de759adc0c173d2ee18
4
- data.tar.gz: beab538e8caec8fb1f7265808adceaa5574b22793dfb11a521651687204416d1
3
+ metadata.gz: c926bbf2b56a9b2553e97487db023f2ac8aac329ab1af22d2f8b56fa09afc8d5
4
+ data.tar.gz: 072201ad18fc53aac71ac620388018469fe618299f0af50bc5abf0a553e596ab
5
5
  SHA512:
6
- metadata.gz: a4d23713bd97acaa6cb98de51e95766c87f09329c0dba4c2b42152838023ec4ce681f1c7f38db582b7e3e20809a2749152da84a74399741dc113ad9251f0875d
7
- data.tar.gz: ce441c5b5a4abc9cbef8a46bf9efa2a57a5cae8bdd827c9edf7af095e60bea9c95a85ceee54baa73e850b0a7a4682b1d5afd41ce41e386040e78761ca88038fd
6
+ metadata.gz: 419ddb1ee1e46be729fc87e957a9aebd56a490810f8134b26617654986b77a5f1aef9b0599ecb7b6c9342dcb57c1417510cf93d9a7169614b36f195f67bd7d43
7
+ data.tar.gz: d683d676ce9daa86aa4544730f38898eb21ffdcd4e604711b01db22416531fe080685e5b6eddb533cafa8c71b135f7453f20fd42b58771f86faf762a8d7b602e
@@ -32,6 +32,11 @@ module BitClust
32
32
  new(root).scan
33
33
  end
34
34
 
35
+ # 1 ファイル分の front matter と本文の情報(scan が各ファイルで読むもの)
36
+ def self.file_info(path)
37
+ new(File.dirname(path)).file_info(File.basename(path))
38
+ end
39
+
35
40
  attr_reader :libraries, :entities, :fragments, :warnings
36
41
 
37
42
  def initialize(root)
@@ -87,6 +92,11 @@ module BitClust
87
92
  self
88
93
  end
89
94
 
95
+ # root 相対の path を 1 ファイルだけ読む
96
+ def file_info(path)
97
+ parse_file(path)
98
+ end
99
+
90
100
  private
91
101
 
92
102
  def validate(path, info)
@@ -216,7 +216,6 @@ module BitClust
216
216
  gate[:until] = until_version if until_version
217
217
  md_gate_covers?(version, gate)
218
218
  end
219
- private :md_version_covers?
220
219
 
221
220
  # membership のゲート(MarkdownTree の since/until/version/except)がこの版を
222
221
  # 含むか。version は「その版のみ」、except は「その版以外」(#%version V と
@@ -229,7 +228,6 @@ module BitClust
229
228
  return false if (except = gate[:except]) && except.any? { |x| v == Gem::Version.new(x) }
230
229
  true
231
230
  end
232
- private :md_gate_covers?
233
231
 
234
232
  def refs
235
233
  @refs ||= RefsDatabase.load(realpath('refs'))
@@ -14,13 +14,17 @@ module BitClust
14
14
  def initialize
15
15
  super
16
16
  @target = nil
17
+ @library = nil
17
18
  @templatedir = srcdir_root + "data/bitclust/template.offline"
18
19
  @baseurl = "file://" + srcdir_root.to_s
19
20
  @version = RUBY_VERSION
20
- @parser.banner = "Usage: #{File.basename($0, '.*')} htmlfile [options] rdfile"
21
+ @parser.banner = "Usage: #{File.basename($0, '.*')} htmlfile [options] rdfile|mdfile"
21
22
  @parser.on('--target=NAME', 'Compile NAME to HTML.') {|name|
22
23
  @target = name
23
24
  }
25
+ @parser.on('--library=NAME', 'Library of the Markdown entity file (overrides front matter library:).') {|name|
26
+ @library = name
27
+ }
24
28
  @parser.on('--[no-]force', '-f', 'Force to use rd_file template.') {|bool|
25
29
  @rd_file = bool
26
30
  }
@@ -47,7 +51,10 @@ module BitClust
47
51
  end
48
52
  @capi = options[:capi]
49
53
  target_file = argv[0]
50
- reject_markdown_source(target_file)
54
+ if target_file&.end_with?('.md')
55
+ exec_markdown(target_file, options[:prefix] ? db : nil)
56
+ return
57
+ end
51
58
  if /refm\/doc\// =~ target_file
52
59
  @rd_file = true if @rd_file.nil?
53
60
  end
@@ -89,10 +96,68 @@ module BitClust
89
96
 
90
97
  private
91
98
 
92
- # 旧 RD ソース(refm)専用なので、Markdown は未対応として案内する
93
- def reject_markdown_source(path)
94
- return unless path && path.end_with?('.md')
95
- error "#{path}: htmlfile は旧 RD ソース(refm)専用で、Markdown(manual/ の .md)には未対応です。manual/ の内容は doctree の rake generate:X.Y と bitclust server(または rake statichtml:X.Y)で確認してください"
99
+ # Markdown ソース(manual/ の .md)を変換する。パースは source_format が
100
+ # markdown の dummy DB で行い、描画には real_db があればそれを使う
101
+ def exec_markdown(path, real_db)
102
+ require 'bitclust/mdparser'
103
+ require 'bitclust/markdown_tree'
104
+ props = { 'version' => @version } #: Database::database_properties
105
+ db = MethodDatabase.dummy(props)
106
+ db.properties['source_format'] = 'markdown'
107
+ screen_db = real_db || db
108
+ manager = ScreenManager.new(:templatedir => @templatedir,
109
+ :base_url => @baseurl,
110
+ :cgi_url => @baseurl,
111
+ :default_encoding => 'utf-8')
112
+ @rd_file = true if @rd_file.nil? && %r<(?:\A|/)manual/doc/> =~ path
113
+ if @rd_file
114
+ puts manager.doc_screen(markdown_doc_entry(db, path, props), { :database => screen_db }).body
115
+ elsif @capi
116
+ fdb = FunctionDatabase.dummy(props)
117
+ fdb.properties['source_format'] = 'markdown'
118
+ funcs = MDFunctionParser.new(fdb).parse_file(path, File.basename(path, '.md'), props)
119
+ unless @target
120
+ raise NotImplementedError, "generating a C API html without --target=NAME is not implemented yet."
121
+ end
122
+ puts manager.entry_screen(lookup(funcs, @target), { :database => screen_db, :target_version => @version }).body
123
+ else
124
+ lib = MDParser.new(db).parse_file(path, markdown_libname(db, path), props)
125
+ entry = @target ? lookup(lib, @target) : lib
126
+ puts manager.entry_screen(entry, { :database => screen_db, :target_version => @version }).body
127
+ end
128
+ rescue ParseError, WriterError => ex
129
+ error "#{path}: #{ex.message}"
130
+ end
131
+
132
+ # manual/doc/ の md を doc ページにする(MethodDatabase#copy_doc_md と同じ手順)
133
+ def markdown_doc_entry(db, path, props)
134
+ rel = path[%r<(?:\A|/)manual/doc/(.+)\.md\z>, 1] || File.basename(path, '.md')
135
+ entry = DocEntry.new(db, NameUtils.libname2id(rel))
136
+ title, source = MDParser.split_doc(Preprocessor.read(path, props))
137
+ entry.title = title
138
+ entry.source = source
139
+ entry.source_location = Location.new(path, 1)
140
+ entry
141
+ end
142
+
143
+ # 所属ライブラリ名。--library > ライブラリルート(type: library)の
144
+ # name: またはパス(manual/api/ からの相対、無ければファイル名)>
145
+ # エンティティの library:(この版で生きている先頭の所属)
146
+ def markdown_libname(db, path)
147
+ info = MarkdownTree.file_info(path)
148
+ unless db.md_version_covers?(@version, info[:since], info[:until])
149
+ $stderr.puts "warning: #{path}: front matter の since/until により Ruby #{@version} には含まれないファイルです"
150
+ end
151
+ if (name = @library)
152
+ return name
153
+ end
154
+ if info[:library_file]
155
+ return info[:name] || path[%r<(?:\A|/)api/(.+)\.md\z>, 1] || File.basename(path, '.md')
156
+ end
157
+ memberships = info[:memberships]
158
+ m = memberships.find {|x| db.md_gate_covers?(@version, x) } || memberships.first
159
+ return m[:library] if m
160
+ error "#{path}: front matter に library: がありません。--library=NAME で所属ライブラリを指定してください"
96
161
  end
97
162
 
98
163
  def lookup(lib, key)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module BitClust
3
- VERSION = "1.8.0"
3
+ VERSION = "1.9.0"
4
4
  end
@@ -3,7 +3,6 @@ require 'stringio'
3
3
  require 'tmpdir'
4
4
  require 'bitclust'
5
5
  require 'bitclust/subcommands/extract_command'
6
- require 'bitclust/subcommands/htmlfile_command'
7
6
 
8
7
  class TestRdOnlyCommandsRejectMarkdown < Test::Unit::TestCase
9
8
  def with_md_file
@@ -32,14 +31,4 @@ class TestRdOnlyCommandsRejectMarkdown < Test::Unit::TestCase
32
31
  assert_match(/refm/, msg)
33
32
  end
34
33
  end
35
-
36
- def test_htmlfile_explains_markdown_is_unsupported
37
- with_md_file do |path|
38
- cmd = BitClust::Subcommands::HtmlfileCommand.new
39
- cmd.parse([path])
40
- msg = capture_stderr { assert_raise(SystemExit) { cmd.exec([path], {:prefix => nil, :capi => false}) } }
41
- assert_match(/Markdown/, msg)
42
- assert_match(/rake generate|bitclust server/, msg)
43
- end
44
- end
45
34
  end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+ require 'test/unit'
3
+ require 'stringio'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+ require 'bitclust'
7
+ require 'bitclust/subcommands/htmlfile_command'
8
+
9
+ # htmlfile サブコマンドの Markdown ソース(manual/)の変換。
10
+ #
11
+ # テストリスト:
12
+ # [x] エンティティ md は front matter の library: を所属として変換する
13
+ # [x] library: が多重所属なら、その版で生きている先頭の所属を使う
14
+ # [x] library: が無く --library も無ければエラー
15
+ # [x] --library で所属を指定できる
16
+ # [x] ライブラリルート md(type: library)はファイル名をライブラリ名にする
17
+ # [x] --ruby の版で #%since が解決される
18
+ # [x] manual/doc/ の md は doc ページとして変換する
19
+ # [x] --capi の md は C API として変換する
20
+ # [x] RD ソースは RD として変換する
21
+ class TestHtmlfileCommand < Test::Unit::TestCase
22
+ FOO_MD = <<~'MD'
23
+ ---
24
+ library: foo
25
+ ---
26
+ # class Foo < Object
27
+
28
+ クラスの説明。
29
+
30
+ ## Instance Methods
31
+
32
+ ### def bar -> nil
33
+
34
+ bar の **説明**。
35
+
36
+ #%since 3.4
37
+ ### def baz -> nil
38
+
39
+ baz の説明。
40
+ #%end
41
+ MD
42
+
43
+ def setup
44
+ @tmpdir = Dir.mktmpdir
45
+ @api = File.join(@tmpdir, 'manual', 'api')
46
+ FileUtils.mkdir_p(File.join(@api, 'foo'))
47
+ end
48
+
49
+ def teardown
50
+ FileUtils.rm_rf(@tmpdir)
51
+ end
52
+
53
+ def write(rel, content)
54
+ path = File.join(@tmpdir, rel)
55
+ FileUtils.mkdir_p(File.dirname(path))
56
+ File.write(path, content)
57
+ path
58
+ end
59
+
60
+ def run_htmlfile(args, capi: false)
61
+ cmd = BitClust::Subcommands::HtmlfileCommand.new
62
+ argv = args.dup
63
+ cmd.parse(argv)
64
+ out = StringIO.new
65
+ err = StringIO.new
66
+ orig_out, orig_err = $stdout, $stderr
67
+ $stdout, $stderr = out, err
68
+ status = nil
69
+ begin
70
+ cmd.exec(argv, {:prefix => nil, :capi => capi})
71
+ rescue SystemExit => e
72
+ status = e.status
73
+ ensure
74
+ $stdout, $stderr = orig_out, orig_err
75
+ end
76
+ [out.string, err.string, status]
77
+ end
78
+
79
+ def test_entity_uses_library_in_front_matter
80
+ path = write('manual/api/foo/Foo.md', FOO_MD)
81
+ out, _err, status = run_htmlfile(['--ruby=3.4', '--target=Foo', path])
82
+ assert_nil(status)
83
+ assert_match(/class Foo/, out)
84
+ assert_match(%r{library/foo">library foo<}, out)
85
+ assert_match(%r{<strong>説明</strong>}, out)
86
+ end
87
+
88
+ def test_entity_uses_first_live_membership
89
+ path = write('manual/api/foo/Foo.md', <<~'MD')
90
+ ---
91
+ library:
92
+ #%until 3.4
93
+ - old_foo
94
+ #%end
95
+ - foo
96
+ - foo_ext
97
+ ---
98
+ # class Foo < Object
99
+
100
+ クラスの説明。
101
+ MD
102
+ out, _err, status = run_htmlfile(['--ruby=3.4', '--target=Foo', path])
103
+ assert_nil(status)
104
+ assert_match(%r{library/foo">library foo<}, out)
105
+ out, _err, _status = run_htmlfile(['--ruby=3.3', '--target=Foo', path])
106
+ assert_match(%r{library/old_foo">library old_foo<}, out)
107
+ end
108
+
109
+ def test_entity_without_library_is_error
110
+ path = write('manual/api/foo/Foo.md', FOO_MD.sub(/\A---\nlibrary: foo\n---\n/, ''))
111
+ out, err, status = run_htmlfile(['--target=Foo', path])
112
+ assert_equal(1, status)
113
+ assert_equal('', out)
114
+ assert_match(/library:/, err)
115
+ assert_match(/--library/, err)
116
+ end
117
+
118
+ def test_library_option_gives_library
119
+ path = write('manual/api/foo/Foo.md', FOO_MD.sub(/\A---\nlibrary: foo\n---\n/, ''))
120
+ out, _err, status = run_htmlfile(['--library=bar', '--target=Foo', path])
121
+ assert_nil(status)
122
+ assert_match(%r{library/bar">library bar<}, out)
123
+ end
124
+
125
+ def test_library_root_uses_file_name
126
+ write('manual/api/foo/Foo.md', FOO_MD)
127
+ path = write('manual/api/net/foo.md', <<~'MD')
128
+ ---
129
+ type: library
130
+ category: Network
131
+ ---
132
+ foo ライブラリの **説明**。
133
+ MD
134
+ out, _err, status = run_htmlfile([path])
135
+ assert_nil(status)
136
+ assert_match(%r{<h1>library net/foo</h1>}, out)
137
+ assert_match(%r{<strong>説明</strong>}, out)
138
+ end
139
+
140
+ def test_builtin_library_root
141
+ path = write('manual/api/_builtin.md', "---\ntype: library\n---\n組み込みライブラリ。\n")
142
+ out, _err, status = run_htmlfile([path])
143
+ assert_nil(status)
144
+ assert_match(%r{<title>library _builtin }, out)
145
+ assert_match(/組み込みライブラリ。/, out)
146
+ end
147
+
148
+ def test_ruby_version_resolves_gates
149
+ path = write('manual/api/foo/Foo.md', FOO_MD)
150
+ out, _err, _status = run_htmlfile(['--ruby=3.4', '--target=Foo', path])
151
+ assert_match(/baz/, out)
152
+ out, _err, _status = run_htmlfile(['--ruby=3.3', '--target=Foo', path])
153
+ assert_match(/bar/, out)
154
+ assert_not_match(/baz/, out)
155
+ end
156
+
157
+ def test_target_method
158
+ path = write('manual/api/foo/Foo.md', FOO_MD)
159
+ out, _err, status = run_htmlfile(['--ruby=3.4', '--target=Foo#bar', path])
160
+ assert_nil(status)
161
+ assert_match(/bar の <strong>説明<\/strong>/, out)
162
+ assert_not_match(/baz の説明/, out)
163
+ end
164
+
165
+ def test_doc_page
166
+ path = write('manual/doc/spec/foo.md', <<~'MD')
167
+ # Foo の仕様
168
+
169
+ 本文の **強調**。
170
+ MD
171
+ out, _err, status = run_htmlfile([path])
172
+ assert_nil(status)
173
+ assert_match(/Foo の仕様/, out)
174
+ assert_match(%r{本文の <strong>強調</strong>}, out)
175
+ end
176
+
177
+ def test_capi
178
+ path = write('manual/capi/array.c.md', <<~'MD')
179
+ ### VALUE rb_foo_new()
180
+
181
+ 空の **Foo** を作ります。
182
+
183
+ ### VALUE rb_foo_bar(VALUE foo)
184
+
185
+ bar を返します。
186
+ MD
187
+ out, _err, status = run_htmlfile(['--target=rb_foo_new', path], capi: true)
188
+ assert_nil(status)
189
+ assert_match(/rb_foo_new/, out)
190
+ assert_match(%r{空の <strong>Foo</strong> を作ります。}, out)
191
+ assert_not_match(/bar を返します/, out)
192
+ end
193
+
194
+ def test_rd_source
195
+ path = write('refm/api/src/foo.rd', <<~'RD')
196
+ = class Foo < Object
197
+
198
+ クラスの説明。
199
+
200
+ == Instance Methods
201
+
202
+ --- bar -> nil
203
+
204
+ bar の説明。
205
+ RD
206
+ out, _err, status = run_htmlfile(['--target=Foo', path])
207
+ assert_nil(status)
208
+ assert_match(/class Foo/, out)
209
+ assert_match(/bar の説明。/, out)
210
+ end
211
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitclust-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/rurema
@@ -347,6 +347,7 @@ files:
347
347
  - test/test_extract_command.rb
348
348
  - test/test_functiondatabase.rb
349
349
  - test/test_functionreferenceparser.rb
350
+ - test/test_htmlfile_command.rb
350
351
  - test/test_include_graph.rb
351
352
  - test/test_include_pruner.rb
352
353
  - test/test_irb_plugin.rb
@@ -455,6 +456,7 @@ test_files:
455
456
  - test/test_extract_command.rb
456
457
  - test/test_functiondatabase.rb
457
458
  - test/test_functionreferenceparser.rb
459
+ - test/test_htmlfile_command.rb
458
460
  - test/test_include_graph.rb
459
461
  - test/test_include_pruner.rb
460
462
  - test/test_irb_plugin.rb