Module: Vivlio::Starter::CLI::Build::ChapterConfig
- Defined in:
- lib/vivlio/starter/cli/build/chapter_config.rb
Overview
章番号パース・ファイル解決モジュール
Class Method Summary collapse
-
.all_chapter_files ⇒ Object
contents ディレクトリ内の全 .md ファイルのベース名を取得 返り値: ソート済みのファイル名配列(例: [“00-preface.md”, “11-install.md”, …]).
-
.all_integers?(arr) ⇒ Boolean
配列が全て整数(または整数文字列)かチェック.
-
.configured_chapters ⇒ Object
catalog.yml から対象とする章ファイル名のリストを返す.
-
.convert_numbers_to_filenames(numbers) ⇒ Object
章番号配列をファイル名配列に変換 例: [0, 11, 12] → [“00-preface.md”, “11-install.md”, “12-tutorial.md”] 存在しないファイルはスキップ.
-
.detect_duplicate_chapter_numbers ⇒ Object
章番号の重複をチェック(同一番号で複数ファイルが存在する場合) 返り値: { 章番号 => [ファイル名配列] } の Hash(重複がある番号のみ).
-
.expand_chapter_range(range_str) ⇒ Object
範囲指定文字列(“02-12”)を章番号配列に展開 例: “02-12” → [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].
-
.filter_basenames_by_range(basenames, range, keep_numbers = nil) ⇒ Array<String>
ベース名配列を章番号レンジ+keepでフィルタ 注: _toc.html などアンダースコア始まりのファイルは A(d+)- パターンにマッチしないため自動的に除外される.
-
.htmls_for_range(base_dir, range, keep_numbers = nil) ⇒ Object
ディレクトリ内の *.html から、章番号レンジと keep_numbers でフィルタ 注: アンダースコア始まりのファイルは A(d+)- パターンにマッチしないため自動的に除外される.
-
.parse_chapter_numbers_from_string(str) ⇒ Object
カンマ区切り文字列から章番号配列を抽出(範囲展開含む) 例: “02, 11-13, 91” → [2, 11, 12, 13, 91].
-
.validate_no_duplicate_chapter_numbers! ⇒ Object
章番号の重複を検証し、重複があればエラーを発生させる.
Class Method Details
.all_chapter_files ⇒ Object
contents ディレクトリ内の全 .md ファイルのベース名を取得返り値: ソート済みのファイル名配列(例: [“00-preface.md”, “11-install.md”, …])
107 108 109 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 107 def all_chapter_files Dir.glob(File.join(Common::CONTENTS_DIR, '*.md')).map { |f| File.basename(f) }.sort end |
.all_integers?(arr) ⇒ Boolean
配列が全て整数(または整数文字列)かチェック
97 98 99 100 101 102 103 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 97 def all_integers?(arr) return false unless arr.is_a?(Array) arr.all? do |item| item.to_s.strip.match?(/\A\d+\z/) end end |
.configured_chapters ⇒ Object
catalog.yml から対象とする章ファイル名のリストを返す
catalog.yml の PREFACE / CHAPTERS / APPENDICES / POSTFACE からbasename を収集し、存在するファイルのみを返す。
返り値: ファイル名配列(例: [“00-preface.md”, “11-install.md”, …])
または空配列
118 119 120 121 122 123 124 125 126 127 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 118 def configured_chapters basenames = CatalogLoader.load_existing_basenames Common.log_info("[Catalog] loaded basenames=#{basenames.inspect}") # ファイル名配列に変換(.md 付き) basenames.map { |bn| "#{bn}.md" } rescue StandardError => e Common.log_error("catalog.yml の読み込みに失敗しました: #{e.}") raise end |
.convert_numbers_to_filenames(numbers) ⇒ Object
章番号配列をファイル名配列に変換例: [0, 11, 12] → [“00-preface.md”, “11-install.md”, “12-tutorial.md”] 存在しないファイルはスキップ
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 145 def convert_numbers_to_filenames(numbers) return [] unless numbers.is_a?(Array) files = Dir.glob(File.join(Common::CONTENTS_DIR, '*.md')) number_to_file = {} resolver = TokenResolver::Resolver.new files.each do |file| basename = File.basename(file, '.md') entry = resolver.resolve_file(basename) next unless entry.number number_to_file[entry.number.to_i] = "#{basename}.md" end result = numbers.map { |n| number_to_file[n] }.compact Common.log_info("[Subset] converted to filenames=#{result.inspect}") result end |
.detect_duplicate_chapter_numbers ⇒ Object
章番号の重複をチェック(同一番号で複数ファイルが存在する場合)返り値: { 章番号 => [ファイル名配列] } の Hash(重複がある番号のみ)
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 79 def detect_duplicate_chapter_numbers files = Dir.glob(File.join(Common::CONTENTS_DIR, '*.md')) number_to_files = Hash.new { |h, k| h[k] = [] } resolver = TokenResolver::Resolver.new files.each do |file| basename = File.basename(file, '.md') entry = resolver.resolve_file(basename) next unless entry.number number_to_files[entry.number.to_i] << basename end # 重複があるもののみ返す number_to_files.select { |_num, files_list| files_list.size > 1 } end |
.expand_chapter_range(range_str) ⇒ Object
範囲指定文字列(“02-12”)を章番号配列に展開例: “02-12” → [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 33 def (range_str) return [] unless range_str.is_a?(String) match = range_str.strip.match(/\A(\d+)-(\d+)\z/) return [] unless match start_num = match[1].to_i end_num = match[2].to_i return [] if start_num > end_num (start_num..end_num).to_a rescue StandardError [] end |
.filter_basenames_by_range(basenames, range, keep_numbers = nil) ⇒ Array<String>
ベース名配列を章番号レンジ+keepでフィルタ注: _toc.html などアンダースコア始まりのファイルは A(d+)- パターンにマッチしないため自動的に除外される
172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 172 def filter_basenames_by_range(basenames, range, keep_numbers = nil) keep_set = keep_numbers.respond_to?(:include?) ? keep_numbers : nil Array(basenames) .map(&:to_s) .grep(/\A(\d+)-/) .select do |bn| n = bn[/\A(\d+)-/, 1].to_i in_range = range.include?(n) allowed = keep_set ? keep_set.include?(n) : true in_range && allowed end .uniq .sort end |
.htmls_for_range(base_dir, range, keep_numbers = nil) ⇒ Object
ディレクトリ内の *.html から、章番号レンジと keep_numbers でフィルタ注: アンダースコア始まりのファイルは A(d+)- パターンにマッチしないため自動的に除外される
189 190 191 192 193 194 195 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 189 def htmls_for_range(base_dir, range, keep_numbers = nil) Dir.glob(File.join(base_dir, '*.html')).select do |path| bn = File.basename(path, '.html') n = bn[/\A(\d+)-/, 1]&.to_i n && range.include?(n) && (keep_numbers.nil? || keep_numbers.include?(n)) end.sort end |
.parse_chapter_numbers_from_string(str) ⇒ Object
カンマ区切り文字列から章番号配列を抽出(範囲展開含む)例: “02, 11-13, 91” → [2, 11, 12, 13, 91]
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 50 def parse_chapter_numbers_from_string(str) return [] unless str.is_a?(String) parts = str.split(',').map(&:strip).reject(&:empty?) numbers = [] parts.each do |part| if part.match?(/\A\d+-\d+\z/) # 範囲指定 numbers.concat((part)) elsif part.match?(/\A\d+\z/) # 単一番号 numbers << part.to_i else # 数字でない → ファイル名指定の可能性 raise ArgumentError, "混在形式は非対応です: '#{part}' は番号指定として無効です。" end end numbers.uniq.sort rescue ArgumentError => e raise e rescue StandardError => e Common.log_error("章番号の解析に失敗しました: #{e.}") [] end |
.validate_no_duplicate_chapter_numbers! ⇒ Object
章番号の重複を検証し、重複があればエラーを発生させる
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/vivlio/starter/cli/build/chapter_config.rb', line 130 def validate_no_duplicate_chapter_numbers! duplicates = detect_duplicate_chapter_numbers return unless duplicates.any? error_msg = "❌ 同一章番号で複数のファイルが存在します。ファイル名を見直してください:\n" duplicates.each do |num, files| error_msg += " 章番号 #{num}: #{files.join(', ')}\n" end Common.log_error(error_msg) raise StandardError, error_msg end |