bitclust-core 1.7.0 → 1.8.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/data/bitclust/template/class-index +0 -1
  3. data/data/bitclust/template.epub/class +5 -5
  4. data/data/bitclust/template.epub/class-index +0 -1
  5. data/data/bitclust/template.epub/contents +7 -4
  6. data/data/bitclust/template.epub/layout +4 -4
  7. data/data/bitclust/template.offline/class-index +0 -1
  8. data/lib/bitclust/generators/epub.rb +69 -5
  9. data/lib/bitclust/irb.rb +14 -8
  10. data/lib/bitclust/markdown_bridge.rb +11 -6
  11. data/lib/bitclust/markdown_tree.rb +118 -11
  12. data/lib/bitclust/methoddatabase.rb +17 -4
  13. data/lib/bitclust/nameutils.rb +13 -0
  14. data/lib/bitclust/preprocessor.rb +0 -36
  15. data/lib/bitclust/remote.rb +405 -0
  16. data/lib/bitclust/runner.rb +5 -10
  17. data/lib/bitclust/screen.rb +2 -1
  18. data/lib/bitclust/searcher.rb +43 -24
  19. data/lib/bitclust/subcommands/chm_command.rb +1 -1
  20. data/lib/bitclust/subcommands/epub_command.rb +2 -2
  21. data/lib/bitclust/subcommands/extract_command.rb +7 -0
  22. data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
  23. data/lib/bitclust/subcommands/methods_command.rb +5 -28
  24. data/lib/bitclust/subcommands/query_command.rb +1 -0
  25. data/lib/bitclust/subcommands/server_command.rb +21 -10
  26. data/lib/bitclust/subcommands/setup_command.rb +28 -10
  27. data/lib/bitclust/syntax_highlighter.rb +65 -4
  28. data/lib/bitclust/user_dirs.rb +62 -0
  29. data/lib/bitclust/version.rb +1 -1
  30. data/test/test_chm_command.rb +22 -0
  31. data/test/test_epub_command.rb +60 -0
  32. data/test/test_epub_generator.rb +330 -0
  33. data/test/test_extract_command.rb +45 -0
  34. data/test/test_irb_plugin.rb +100 -6
  35. data/test/test_markdown_bridge.rb +35 -0
  36. data/test/test_markdown_tree.rb +144 -0
  37. data/test/test_mdparser.rb +39 -0
  38. data/test/test_methods_command.rb +32 -0
  39. data/test/test_opensearchdescription_screen.rb +39 -0
  40. data/test/test_query_command.rb +30 -0
  41. data/test/test_remote.rb +374 -0
  42. data/test/test_runner.rb +61 -1
  43. data/test/test_searcher.rb +63 -0
  44. data/test/test_server_command.rb +41 -0
  45. data/test/test_setup_command.rb +143 -0
  46. data/test/test_syntax_highlighter.rb +124 -0
  47. data/test/test_user_dirs.rb +62 -0
  48. metadata +37 -2
  49. data/lib/bitclust/ridatabase.rb +0 -359
data/test/test_runner.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'bitclust'
2
2
  require 'bitclust/runner'
3
+ require 'bitclust/user_dirs'
4
+ require 'tmpdir'
5
+ require 'fileutils'
3
6
 
4
7
  class TestRunner < Test::Unit::TestCase
5
8
  def setup
@@ -116,7 +119,10 @@ class TestRunner < Test::Unit::TestCase
116
119
  original_stderr = $stderr
117
120
  $stderr = StringIO.new
118
121
  assert_raise(SystemExit) { @runner.run(["init"]) }
119
- assert_include($stderr.string, "--database")
122
+ message = $stderr.string
123
+ assert_include(message, "--database (-d)")
124
+ assert_include(message, "bitclust setup")
125
+ assert_include(message, BitClust::UserDirs.config_candidates.join(' or '))
120
126
  ensure
121
127
  $stderr = original_stderr
122
128
  end
@@ -146,3 +152,57 @@ class TestRunner < Test::Unit::TestCase
146
152
  end
147
153
  end
148
154
  end
155
+
156
+ # Runner#load_config が UserDirs 経由で設定ファイルを読むこと(実ファイルで検証)。
157
+ #
158
+ # テストリスト:
159
+ # [x] XDG の場所(~/.config/bitclust/config)にあればそれを読む
160
+ # [x] 従来の場所(~/.bitclust/config)にあればそれを読む
161
+ # [x] どちらも無ければ nil
162
+ class TestRunnerLoadConfig < Test::Unit::TestCase
163
+ ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME
164
+ REFE2_SERVER BITCLUST_SERVER REFE2_DATADIR BITCLUST_DATADIR].freeze
165
+
166
+ def setup
167
+ @saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
168
+ @home = Dir.mktmpdir
169
+ ENV['HOME'] = @home
170
+ (ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
171
+ @runner = BitClust::Runner.new
172
+ end
173
+
174
+ def teardown
175
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
176
+ FileUtils.rm_rf(@home)
177
+ end
178
+
179
+ def write_config(path, prefix, version)
180
+ FileUtils.mkdir_p(File.dirname(path))
181
+ File.write(path, { :default_version => version, :database_prefix => prefix }.to_yaml)
182
+ end
183
+
184
+ def test_load_config_from_xdg_location
185
+ write_config(File.join(@home, '.config', 'bitclust', 'config'), '/xdg/db', '4.0')
186
+ config = @runner.load_config
187
+ assert_equal('/xdg/db', config[:database_prefix])
188
+ assert_equal('4.0', config[:default_version])
189
+ end
190
+
191
+ def test_load_config_from_legacy_location
192
+ write_config(File.join(@home, '.bitclust', 'config'), '/legacy/db', '3.4')
193
+ config = @runner.load_config
194
+ assert_equal('/legacy/db', config[:database_prefix])
195
+ assert_equal('3.4', config[:default_version])
196
+ end
197
+
198
+ def test_load_config_prefers_xdg_over_legacy
199
+ write_config(File.join(@home, '.bitclust', 'config'), '/legacy/db', '3.4')
200
+ write_config(File.join(@home, '.config', 'bitclust', 'config'), '/xdg/db', '4.0')
201
+ config = @runner.load_config
202
+ assert_equal('/xdg/db', config[:database_prefix])
203
+ end
204
+
205
+ def test_load_config_nil_without_any_config_file
206
+ assert_nil(@runner.load_config)
207
+ end
208
+ end
@@ -1,8 +1,10 @@
1
1
  require "test/unit"
2
2
  require "bitclust"
3
3
  require "bitclust/searcher"
4
+ require "bitclust/user_dirs"
4
5
  require "fileutils"
5
6
  require "tmpdir"
7
+ require "yaml"
6
8
 
7
9
  class TestTerminalView < Test::Unit::TestCase
8
10
  include BitClust
@@ -151,3 +153,64 @@ class TestSearcherModuleFunctionQuery < Test::Unit::TestCase
151
153
  assert_equal ["Foo", ".", "b?"], parse("Foo.b?")
152
154
  end
153
155
  end
156
+
157
+ # refe/bitclust search が、環境変数指定が無いとき設定ファイル(UserDirs)
158
+ # 経由で DB を見つけること。XDG の場所→従来の ~/.bitclust の順
159
+ class TestSearcherDefaultDbpath < Test::Unit::TestCase
160
+ include BitClust
161
+
162
+ ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME
163
+ REFE2_SERVER BITCLUST_SERVER REFE2_DATADIR BITCLUST_DATADIR].freeze
164
+
165
+ def setup
166
+ @saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
167
+ @home = Dir.mktmpdir
168
+ ENV['HOME'] = @home
169
+ (ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
170
+ end
171
+
172
+ def teardown
173
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
174
+ FileUtils.rm_rf(@home)
175
+ end
176
+
177
+ def write_config(path, prefix)
178
+ FileUtils.mkdir_p(File.dirname(path))
179
+ File.write(path, { :default_version => '9.9', :database_prefix => prefix }.to_yaml)
180
+ end
181
+
182
+ def test_default_dbpath_via_xdg_config
183
+ prefix = File.join(@home, 'data', 'bitclust', 'db')
184
+ MethodDatabase.new("#{prefix}-9.9").init
185
+ write_config(File.join(@home, '.config', 'bitclust', 'config'), prefix)
186
+ searcher = Searcher.new
187
+ assert_equal("#{prefix}-9.9", searcher.send(:default_dbpath))
188
+ assert_true(searcher.local_database?)
189
+ end
190
+
191
+ def test_default_dbpath_via_legacy_config
192
+ prefix = File.join(@home, '.bitclust', 'db')
193
+ MethodDatabase.new("#{prefix}-9.9").init
194
+ write_config(File.join(@home, '.bitclust', 'config'), prefix)
195
+ searcher = Searcher.new
196
+ assert_equal("#{prefix}-9.9", searcher.send(:default_dbpath))
197
+ assert_true(searcher.local_database?)
198
+ end
199
+
200
+ def test_default_dbpath_prefers_xdg_over_legacy
201
+ legacy_prefix = File.join(@home, '.bitclust', 'db')
202
+ MethodDatabase.new("#{legacy_prefix}-9.9").init
203
+ write_config(File.join(@home, '.bitclust', 'config'), legacy_prefix)
204
+ xdg_prefix = File.join(@home, 'data', 'bitclust', 'db')
205
+ MethodDatabase.new("#{xdg_prefix}-9.9").init
206
+ write_config(File.join(@home, '.config', 'bitclust', 'config'), xdg_prefix)
207
+ searcher = Searcher.new
208
+ assert_equal("#{xdg_prefix}-9.9", searcher.send(:default_dbpath))
209
+ end
210
+
211
+ def test_default_dbpath_nil_without_config
212
+ searcher = Searcher.new
213
+ assert_nil(searcher.send(:default_dbpath))
214
+ assert_false(searcher.local_database?)
215
+ end
216
+ end
@@ -0,0 +1,41 @@
1
+ require 'test/unit'
2
+ require 'stringio'
3
+ require 'bitclust'
4
+ require 'bitclust/subcommands/server_command'
5
+
6
+ class TestServerCommand < Test::Unit::TestCase
7
+ def build(argv)
8
+ cmd = BitClust::Subcommands::ServerCommand.new
9
+ cmd.parse(argv)
10
+ cmd
11
+ end
12
+
13
+ def test_does_not_require_global_database
14
+ assert_false BitClust::Subcommands::ServerCommand.new.needs_database?
15
+ end
16
+
17
+ def test_parse_accepts_short_option
18
+ cmd = build(['--baseurl=', '-d', '/tmp/db-x'])
19
+ assert_equal '/tmp/db-x', cmd.__send__(:resolve_dbpath, {:prefix => nil, :capi => false})
20
+ end
21
+
22
+ def test_parse_accepts_long_option
23
+ cmd = build(['--baseurl=', '--database=/tmp/db-y'])
24
+ assert_equal '/tmp/db-y', cmd.__send__(:resolve_dbpath, {:prefix => '/tmp/global', :capi => false})
25
+ end
26
+
27
+ def test_global_database_is_used_as_fallback
28
+ cmd = build(['--baseurl='])
29
+ assert_equal '/tmp/global', cmd.__send__(:resolve_dbpath, {:prefix => '/tmp/global', :capi => false})
30
+ end
31
+
32
+ def test_missing_database_aborts_with_guidance
33
+ cmd = build(['--baseurl='])
34
+ err = StringIO.new
35
+ orig, $stderr = $stderr, err
36
+ assert_raise(SystemExit) { cmd.__send__(:resolve_dbpath, {:prefix => nil, :capi => false}) }
37
+ assert_match(/--database/, err.string)
38
+ ensure
39
+ $stderr = orig
40
+ end
41
+ end
@@ -2,6 +2,10 @@
2
2
  require 'test/unit'
3
3
  require 'bitclust'
4
4
  require 'bitclust/subcommands/setup_command'
5
+ require 'bitclust/user_dirs'
6
+ require 'tmpdir'
7
+ require 'fileutils'
8
+ require 'yaml'
5
9
 
6
10
  # setup サブコマンドの既定バージョン。
7
11
  #
@@ -19,3 +23,142 @@ class TestSetupCommand < Test::Unit::TestCase
19
23
  end
20
24
  end
21
25
  end
26
+
27
+ # setup サブコマンドが選ぶ、設定・DB・doctree checkout の置き場所。
28
+ #
29
+ # テストリスト:
30
+ # [x] 何も無ければ XDG の場所(~/.config/bitclust, ~/.local/share/bitclust)を使う
31
+ # [x] 従来の ~/.bitclust/config だけあれば、そのレイアウトを維持する
32
+ # [x] 両方あれば XDG を使う
33
+ class TestSetupCommandLayout < Test::Unit::TestCase
34
+ ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
35
+
36
+ def setup
37
+ @saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
38
+ @home = Dir.mktmpdir
39
+ ENV['HOME'] = @home
40
+ (ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
41
+ @cmd = BitClust::Subcommands::SetupCommand.new
42
+ end
43
+
44
+ def teardown
45
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
46
+ FileUtils.rm_rf(@home)
47
+ end
48
+
49
+ def touch_config(path)
50
+ FileUtils.mkdir_p(File.dirname(path))
51
+ FileUtils.touch(path)
52
+ end
53
+
54
+ def test_layout_uses_xdg_when_nothing_exists
55
+ config_path, database_prefix, rubydoc_dir = @cmd.send(:layout)
56
+ assert_equal(File.join(@home, '.config', 'bitclust', 'config'), config_path.to_s)
57
+ assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), database_prefix)
58
+ assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'rubydoc'), rubydoc_dir.to_s)
59
+ end
60
+
61
+ def test_layout_keeps_legacy_when_only_legacy_config_exists
62
+ touch_config(File.join(@home, '.bitclust', 'config'))
63
+ config_path, database_prefix, rubydoc_dir = @cmd.send(:layout)
64
+ assert_equal(File.join(@home, '.bitclust', 'config'), config_path.to_s)
65
+ assert_equal(File.join(@home, '.bitclust', 'db'), database_prefix)
66
+ assert_equal(File.join(@home, '.bitclust', 'rubydoc'), rubydoc_dir.to_s)
67
+ end
68
+
69
+ def test_layout_uses_xdg_when_both_exist
70
+ touch_config(File.join(@home, '.bitclust', 'config'))
71
+ touch_config(File.join(@home, '.config', 'bitclust', 'config'))
72
+ config_path, database_prefix, _rubydoc_dir = @cmd.send(:layout)
73
+ assert_equal(File.join(@home, '.config', 'bitclust', 'config'), config_path.to_s)
74
+ assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), database_prefix)
75
+ end
76
+ end
77
+
78
+ # setup --prepare が layout の選択どおりに設定ファイルを書くこと
79
+ # (checkout はネットワークに出るのでスタブする)
80
+ class TestSetupCommandPrepare < Test::Unit::TestCase
81
+ ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
82
+
83
+ def setup
84
+ @saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
85
+ @home = Dir.mktmpdir
86
+ ENV['HOME'] = @home
87
+ (ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
88
+ @cmd = BitClust::Subcommands::SetupCommand.new
89
+ stub(@cmd).checkout {|_dir| nil }
90
+ end
91
+
92
+ def teardown
93
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
94
+ FileUtils.rm_rf(@home)
95
+ end
96
+
97
+ def test_prepare_writes_config_under_xdg_config_home
98
+ @cmd.send(:prepare)
99
+ config_path = File.join(@home, '.config', 'bitclust', 'config')
100
+ assert_true(File.exist?(config_path))
101
+ config = YAML.load_file(config_path)
102
+ assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), config[:database_prefix])
103
+ end
104
+
105
+ def test_prepare_keeps_legacy_layout_when_legacy_config_exists
106
+ legacy_config = File.join(@home, '.bitclust', 'config')
107
+ FileUtils.mkdir_p(File.dirname(legacy_config))
108
+ versions = @cmd.instance_variable_get(:@versions)
109
+ File.write(legacy_config, {
110
+ :database_prefix => File.join(@home, '.bitclust', 'db'),
111
+ :encoding => 'utf-8',
112
+ :versions => versions,
113
+ :default_version => versions.max,
114
+ :mdtree => File.join(@home, '.bitclust', 'rubydoc', 'manual', 'api'),
115
+ :capi_mdtree => File.join(@home, '.bitclust', 'rubydoc', 'manual', 'capi'),
116
+ :baseurl => 'http://localhost:10080',
117
+ :port => '10080',
118
+ :pid_file => '/tmp/bitclust.pid',
119
+ }.to_yaml)
120
+ @cmd.send(:prepare)
121
+ assert_false(File.exist?(File.join(@home, '.config', 'bitclust', 'config')))
122
+ config = YAML.load_file(legacy_config)
123
+ assert_equal(File.join(@home, '.bitclust', 'db'), config[:database_prefix])
124
+ end
125
+ end
126
+
127
+ # setup --purge が新旧すべての置き場所を消すこと
128
+ class TestSetupCommandPurge < Test::Unit::TestCase
129
+ ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
130
+
131
+ def setup
132
+ @saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
133
+ @home = Dir.mktmpdir
134
+ ENV['HOME'] = @home
135
+ (ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
136
+ @cmd = BitClust::Subcommands::SetupCommand.new
137
+ end
138
+
139
+ def teardown
140
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
141
+ FileUtils.rm_rf(@home)
142
+ end
143
+
144
+ def test_purge_removes_legacy_xdg_config_data_and_cache_dirs
145
+ dirs = [
146
+ File.join(@home, '.bitclust'),
147
+ File.join(@home, '.config', 'bitclust'),
148
+ File.join(@home, '.local', 'share', 'bitclust'),
149
+ File.join(@home, '.cache', 'bitclust'),
150
+ ]
151
+ dirs.each {|dir| FileUtils.mkdir_p(dir) }
152
+ out, _err = capture_output do
153
+ assert_raise(SystemExit) { @cmd.send(:purge) }
154
+ end
155
+ dirs.each do |dir|
156
+ assert_false(File.exist?(dir), dir)
157
+ assert_include(out, dir)
158
+ end
159
+ end
160
+
161
+ def test_purge_is_fine_when_nothing_exists
162
+ assert_raise(SystemExit) { capture_output { @cmd.send(:purge) } }
163
+ end
164
+ end
@@ -123,6 +123,98 @@ class TestSyntaxHighlighter < Test::Unit::TestCase
123
123
  assert_equal(expected, highlight(source))
124
124
  end
125
125
 
126
+ sub_test_case "symbol span" do
127
+ test 'ivar symbol' do
128
+ source = "x = :@records\n"
129
+ expected = "x <span class=\"o\">=</span> <span class=\"ss\">:@records</span>\n"
130
+ assert_equal(expected, highlight(source))
131
+ end
132
+
133
+ test 'cvar symbol' do
134
+ source = "x = :@@cv\n"
135
+ expected = "x <span class=\"o\">=</span> <span class=\"ss\">:@@cv</span>\n"
136
+ assert_equal(expected, highlight(source))
137
+ end
138
+
139
+ test 'gvar symbol' do
140
+ source = "x = :$stdout\n"
141
+ expected = "x <span class=\"o\">=</span> <span class=\"ss\">:$stdout</span>\n"
142
+ assert_equal(expected, highlight(source))
143
+ end
144
+
145
+ test 'operator symbols' do
146
+ source = <<~END
147
+ a = :+
148
+ b = :<<
149
+ c = :[]
150
+ d = :[]=
151
+ e = :<=>
152
+ f = :!
153
+ END
154
+ expected = <<~END
155
+ a <span class="o">=</span> <span class="ss">:+</span>
156
+ b <span class="o">=</span> <span class="ss">:&lt;&lt;</span>
157
+ c <span class="o">=</span> <span class="ss">:[]</span>
158
+ d <span class="o">=</span> <span class="ss">:[]=</span>
159
+ e <span class="o">=</span> <span class="ss">:&lt;=&gt;</span>
160
+ f <span class="o">=</span> <span class="ss">:!</span>
161
+ END
162
+ assert_equal(expected, highlight(source))
163
+ end
164
+
165
+ test '%s dynamic symbol literal' do
166
+ source = "x = %s!sym!\n"
167
+ expected = "x <span class=\"o\">=</span> <span class=\"ss\">%s!sym!</span>\n"
168
+ assert_equal(expected, highlight(source))
169
+ end
170
+
171
+ test 'double-quoted dynamic symbol' do
172
+ source = "x = :\"dyn\"\n"
173
+ expected = "x <span class=\"o\">=</span> <span class=\"ss\">:&quot;dyn&quot;</span>\n"
174
+ assert_equal(expected, highlight(source))
175
+ end
176
+
177
+ test 'well-formedness of mixed symbol spans' do
178
+ source = <<~END
179
+ x = :@records
180
+ y = :@@cv
181
+ z = :$stdout
182
+ a = :+
183
+ b = :<<
184
+ c = :[]
185
+ s = %s!sym!
186
+ d = :"dyn"
187
+ END
188
+ result = highlight(source)
189
+ assert_equal(result.scan('<span').size, result.scan('</span>').size)
190
+ end
191
+
192
+ test 'symbol does not leak the stack into later tokens' do
193
+ source = <<~END
194
+ x = :@records
195
+ def foo
196
+ end
197
+ END
198
+ baseline = highlight("def foo\nend\n")
199
+ result = highlight(source)
200
+ assert(
201
+ result.end_with?(baseline),
202
+ "expected result to end with unaffected `def foo/end` rendering:\n" \
203
+ " baseline: #{baseline.inspect}\n" \
204
+ " result: #{result.inspect}"
205
+ )
206
+ end
207
+
208
+ test 'label is not treated as a symbol' do
209
+ source = "h = { a: 1 }\nfoo(:x, k: 2)\n"
210
+ result = highlight(source)
211
+ assert_equal(result.scan('<span').size, result.scan('</span>').size)
212
+ assert_match(%r{<span class="ss">a:</span>}, result)
213
+ assert_match(%r{<span class="ss">k:</span>}, result)
214
+ assert_match(%r{<span class="ss">:x</span>}, result)
215
+ end
216
+ end
217
+
126
218
  test 'one liner module' do
127
219
  source = <<~END
128
220
  module Foo; end
@@ -142,4 +234,36 @@ class TestSyntaxHighlighter < Test::Unit::TestCase
142
234
  END
143
235
  assert_equal(expected, highlight(source))
144
236
  end
237
+
238
+ sub_test_case "heredoc argument and quoted label" do
239
+ def assert_balanced(html)
240
+ assert_equal html.scan(/<span\b/).size, html.scan(%r{</span>}).size, html
241
+ end
242
+
243
+ test 'identifier after a heredoc argument is rendered normally' do
244
+ html = highlight("method(arg1, <<LABEL, arg2)\n x\nLABEL\n")
245
+ assert_include(html, ', arg2<span class="p">)</span>')
246
+ assert_balanced(html)
247
+ end
248
+
249
+ test 'block argument after a heredoc argument' do
250
+ html = highlight("ERB.new(<<~'END_PRODUCT', eoutvar: \"@product\").result b\n x\nEND_PRODUCT\n")
251
+ assert_balanced(html)
252
+ assert_not_include(html, '<span class="no">b')
253
+ end
254
+
255
+ test 'quoted symbol keys close the string span' do
256
+ html = highlight(%q({ "a":"A", 'b':"B" }) + "\n")
257
+ assert_balanced(html)
258
+ assert_include(html, %q(<span class="s1">'b':</span>))
259
+ assert_include(html, %q(<span class="s2">a</span><span class="s2">&quot;:</span>))
260
+ end
261
+
262
+ test 'quoted symbol key does not leak the stack' do
263
+ with = highlight(%q({ "a":"A" }) + "\ndef foo\nend\n")
264
+ without = highlight("def foo\nend\n")
265
+ assert_include(with, without.lines[0].chomp)
266
+ assert_balanced(with)
267
+ end
268
+ end
145
269
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'test/unit'
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+ require 'bitclust/user_dirs'
6
+
7
+ # 設定・DB・キャッシュの置き場所(XDG 準拠+従来の ~/.bitclust fallback)。
8
+ #
9
+ # テストリスト:
10
+ # [x] XDG_* 未設定なら ~/.config ~/.local/share ~/.cache の下の bitclust/
11
+ # [x] XDG_* が絶対パスならそれを使う。相対パスは無視して既定に戻る
12
+ # [x] config_file は XDG → ~/.bitclust の順で存在するものを返す。無ければ nil
13
+ # [x] load_config は設定ファイルの YAML を返す。無ければ nil
14
+ class TestUserDirs < Test::Unit::TestCase
15
+ KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
16
+
17
+ def setup
18
+ @saved = KEYS.to_h {|k| [k, ENV[k]] }
19
+ @home = Dir.mktmpdir
20
+ ENV['HOME'] = @home
21
+ %w[XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].each {|k| ENV.delete(k) }
22
+ end
23
+
24
+ def teardown
25
+ @saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
26
+ FileUtils.rm_rf(@home)
27
+ end
28
+
29
+ def test_defaults_under_home
30
+ assert_equal(File.join(@home, '.config', 'bitclust'), BitClust::UserDirs.config_home.to_s)
31
+ assert_equal(File.join(@home, '.local', 'share', 'bitclust'), BitClust::UserDirs.data_home.to_s)
32
+ assert_equal(File.join(@home, '.cache', 'bitclust'), BitClust::UserDirs.cache_home.to_s)
33
+ assert_equal(File.join(@home, '.bitclust'), BitClust::UserDirs.legacy_dir.to_s)
34
+ end
35
+
36
+ def test_xdg_environment_variables
37
+ ENV['XDG_CONFIG_HOME'] = File.join(@home, 'cfg')
38
+ ENV['XDG_DATA_HOME'] = File.join(@home, 'data')
39
+ ENV['XDG_CACHE_HOME'] = 'relative/cache'
40
+ assert_equal(File.join(@home, 'cfg', 'bitclust'), BitClust::UserDirs.config_home.to_s)
41
+ assert_equal(File.join(@home, 'data', 'bitclust'), BitClust::UserDirs.data_home.to_s)
42
+ assert_equal(File.join(@home, '.cache', 'bitclust'), BitClust::UserDirs.cache_home.to_s)
43
+ end
44
+
45
+ def test_config_file_prefers_xdg_then_legacy
46
+ assert_nil(BitClust::UserDirs.config_file)
47
+ legacy = File.join(@home, '.bitclust', 'config')
48
+ FileUtils.mkdir_p(File.dirname(legacy))
49
+ File.write(legacy, { default_version: '3.4', database_prefix: '/legacy/db' }.to_yaml)
50
+ assert_equal(legacy, BitClust::UserDirs.config_file.to_s)
51
+ assert_equal('/legacy/db', BitClust::UserDirs.load_config[:database_prefix])
52
+ xdg = File.join(@home, '.config', 'bitclust', 'config')
53
+ FileUtils.mkdir_p(File.dirname(xdg))
54
+ File.write(xdg, { default_version: '4.0', database_prefix: '/xdg/db' }.to_yaml)
55
+ assert_equal(xdg, BitClust::UserDirs.config_file.to_s)
56
+ assert_equal('/xdg/db', BitClust::UserDirs.load_config[:database_prefix])
57
+ end
58
+
59
+ def test_load_config_without_file
60
+ assert_nil(BitClust::UserDirs.load_config)
61
+ end
62
+ 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.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/rurema
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rexml
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: rack
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -278,8 +292,8 @@ files:
278
292
  - lib/bitclust/rdcompiler.rb
279
293
  - lib/bitclust/refsdatabase.rb
280
294
  - lib/bitclust/reloadable_request_handler.rb
295
+ - lib/bitclust/remote.rb
281
296
  - lib/bitclust/requesthandler.rb
282
- - lib/bitclust/ridatabase.rb
283
297
  - lib/bitclust/rrd_to_markdown.rb
284
298
  - lib/bitclust/rrdparser.rb
285
299
  - lib/bitclust/runner.rb
@@ -313,6 +327,7 @@ files:
313
327
  - lib/bitclust/subcommands/update_command.rb
314
328
  - lib/bitclust/syntax_highlighter.rb
315
329
  - lib/bitclust/textutils.rb
330
+ - lib/bitclust/user_dirs.rb
316
331
  - lib/bitclust/version.rb
317
332
  - lib/bitclust/version_badges.rb
318
333
  - lib/bitclust/whole_file_gate.rb
@@ -320,12 +335,16 @@ files:
320
335
  - test/test_app.rb
321
336
  - test/test_bitclust.rb
322
337
  - test/test_capi_converter.rb
338
+ - test/test_chm_command.rb
323
339
  - test/test_class_screen.rb
324
340
  - test/test_copy_doc_md.rb
325
341
  - test/test_doc_converter.rb
326
342
  - test/test_entity_splitter.rb
327
343
  - test/test_entry.rb
328
344
  - test/test_eol_warning.rb
345
+ - test/test_epub_command.rb
346
+ - test/test_epub_generator.rb
347
+ - test/test_extract_command.rb
329
348
  - test/test_functiondatabase.rb
330
349
  - test/test_functionreferenceparser.rb
331
350
  - test/test_include_graph.rb
@@ -345,15 +364,19 @@ files:
345
364
  - test/test_method_since_calculator.rb
346
365
  - test/test_methoddatabase.rb
347
366
  - test/test_methodentry.rb
367
+ - test/test_methods_command.rb
348
368
  - test/test_methodsignature.rb
349
369
  - test/test_methodsince_command.rb
350
370
  - test/test_nameutils.rb
371
+ - test/test_opensearchdescription_screen.rb
351
372
  - test/test_preprocessor.rb
373
+ - test/test_query_command.rb
352
374
  - test/test_rbs_overload_matcher.rb
353
375
  - test/test_rbs_sig_importer.rb
354
376
  - test/test_rbssig_command.rb
355
377
  - test/test_rdcompiler.rb
356
378
  - test/test_refsdatabase.rb
379
+ - test/test_remote.rb
357
380
  - test/test_rrd_to_markdown.rb
358
381
  - test/test_rrdparser.rb
359
382
  - test/test_run_ruby_wasm.rb
@@ -363,10 +386,12 @@ files:
363
386
  - test/test_search_screen.rb
364
387
  - test/test_searcher.rb
365
388
  - test/test_searchpage_command.rb
389
+ - test/test_server_command.rb
366
390
  - test/test_setup_command.rb
367
391
  - test/test_simplesearcher.rb
368
392
  - test/test_statichtml_command.rb
369
393
  - test/test_syntax_highlighter.rb
394
+ - test/test_user_dirs.rb
370
395
  - test/test_whole_file_gate.rb
371
396
  - theme/default/images/external.png
372
397
  - theme/default/js/NOTICE
@@ -418,12 +443,16 @@ test_files:
418
443
  - test/test_app.rb
419
444
  - test/test_bitclust.rb
420
445
  - test/test_capi_converter.rb
446
+ - test/test_chm_command.rb
421
447
  - test/test_class_screen.rb
422
448
  - test/test_copy_doc_md.rb
423
449
  - test/test_doc_converter.rb
424
450
  - test/test_entity_splitter.rb
425
451
  - test/test_entry.rb
426
452
  - test/test_eol_warning.rb
453
+ - test/test_epub_command.rb
454
+ - test/test_epub_generator.rb
455
+ - test/test_extract_command.rb
427
456
  - test/test_functiondatabase.rb
428
457
  - test/test_functionreferenceparser.rb
429
458
  - test/test_include_graph.rb
@@ -443,15 +472,19 @@ test_files:
443
472
  - test/test_method_since_calculator.rb
444
473
  - test/test_methoddatabase.rb
445
474
  - test/test_methodentry.rb
475
+ - test/test_methods_command.rb
446
476
  - test/test_methodsignature.rb
447
477
  - test/test_methodsince_command.rb
448
478
  - test/test_nameutils.rb
479
+ - test/test_opensearchdescription_screen.rb
449
480
  - test/test_preprocessor.rb
481
+ - test/test_query_command.rb
450
482
  - test/test_rbs_overload_matcher.rb
451
483
  - test/test_rbs_sig_importer.rb
452
484
  - test/test_rbssig_command.rb
453
485
  - test/test_rdcompiler.rb
454
486
  - test/test_refsdatabase.rb
487
+ - test/test_remote.rb
455
488
  - test/test_rrd_to_markdown.rb
456
489
  - test/test_rrdparser.rb
457
490
  - test/test_run_ruby_wasm.rb
@@ -461,8 +494,10 @@ test_files:
461
494
  - test/test_search_screen.rb
462
495
  - test/test_searcher.rb
463
496
  - test/test_searchpage_command.rb
497
+ - test/test_server_command.rb
464
498
  - test/test_setup_command.rb
465
499
  - test/test_simplesearcher.rb
466
500
  - test/test_statichtml_command.rb
467
501
  - test/test_syntax_highlighter.rb
502
+ - test/test_user_dirs.rb
468
503
  - test/test_whole_file_gate.rb