Class: BitClust::MarkdownTree

Inherits:
Object
  • Object
show all
Defined in:
lib/bitclust/markdown_tree.rb

Overview

新パイプラインのファイル発見(MARKUP_SPEC §1.1)。

md ツリー(manual/api/**/*.md 相当)を glob し、各ファイルの front matter と エンティティ H1 から次の3種に分類する。旧 LIBRARIES マニフェストと grouping 用 #@include は使わない。

  • エンティティ: エンティティ H1 を1つ以上持つ(front matter の library が所属)
  • ライブラリ: type: library を持つ(名前 = パスから .md を除いたもの)
  • 共有断片: どちらでもなく、いずれかの #@include から参照される

検証(ビルド警告に相当):

  • 孤児: どの分類にも入らないファイル(H1 の書き忘れ・参照されない断片の検出)
  • library の無いエンティティ / 未知の library を指すエンティティ
  • 関係リント: include/extend/alias の記述場所は front matter のみ。 マルチエンティティファイルの front matter 関係キーと、 本文の H1 直後の関係行(ファイル種を問わず)を警告する
  • #@include 先の欠損

Constant Summary collapse

ENTITY_H1_RE =
/\A#(?!#)\s*(class|module|object|reopen|redefine)\s+(\S+)/
RELATION_LINE_RE =
/\A(?:include|extend|alias)\s+\S/
RELATION_KEY_RE =
/\A(?:include|extend|alias):\s*\z/
INCLUDE_RE =
/\A\#[@%]include\s*\((.*?)\)/
FENCE_RE =
/\A`{3,}/
BLANK_RE =
/\A\s*\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root) ⇒ MarkdownTree

Returns a new instance of MarkdownTree.



34
35
36
37
38
39
40
# File 'lib/bitclust/markdown_tree.rb', line 34

def initialize(root)
  @root = root
  @libraries = {}   # name => { path: }
  @entities = {}    # path => { names:, library: }
  @fragments = []
  @warnings = []
end

Instance Attribute Details

#entitiesObject (readonly)

Returns the value of attribute entities.



32
33
34
# File 'lib/bitclust/markdown_tree.rb', line 32

def entities
  @entities
end

#fragmentsObject (readonly)

Returns the value of attribute fragments.



32
33
34
# File 'lib/bitclust/markdown_tree.rb', line 32

def fragments
  @fragments
end

#librariesObject (readonly)

Returns the value of attribute libraries.



32
33
34
# File 'lib/bitclust/markdown_tree.rb', line 32

def libraries
  @libraries
end

#warningsObject (readonly)

Returns the value of attribute warnings.



32
33
34
# File 'lib/bitclust/markdown_tree.rb', line 32

def warnings
  @warnings
end

Class Method Details

.scan(root) ⇒ Object



28
29
30
# File 'lib/bitclust/markdown_tree.rb', line 28

def self.scan(root)
  new(root).scan
end

Instance Method Details

#scanObject



42
43
44
45
46
47
48
49
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
76
77
78
79
80
81
82
83
84
85
# File 'lib/bitclust/markdown_tree.rb', line 42

def scan
  files = Dir.glob('**/*.md', base: @root).sort
  warn_case_collisions(files)
  infos = files.to_h { |f| [f, parse_file(f)] }

  referenced = {} #: Hash[String, bool]
  infos.each do |path, info|
    info[:includes].each do |target|
      if (resolved = resolve(path, target, infos))
        referenced[resolved] = true
      else
        @warnings << "include target not found: #{target} (from #{path})"
      end
    end
  end

  infos.each do |path, info|
    if info[:library_file]
      # 名前は原則パス由来。ファイル名衝突回避で改名されたファイル
      # (rdoc/rdoc.lib.md)は front matter の name: が正
      @libraries[info[:name] || path.sub(/\.md\z/, '')] =
        { path: path, since: info[:since], until: info[:until] }
    end
    # include 参照され front matter を持たないファイルは、H1 を含んでいても
    # 断片(transclusion 用。fiddle の版分岐チェーン等)
    info[:fragment] = referenced[path] && !info[:library_file] && info[:library].nil?
    if info[:fragment]
      @fragments << path
    elsif !info[:kinds].empty?
      @entities[path] = { names: info[:kinds].map(&:last), kinds: info[:kinds],
                          library: info[:library],
                          memberships: info[:memberships],
                          since: info[:since], until: info[:until] }
    elsif !info[:library_file]
      @warnings << "orphan file (no entity H1, no type: library, not included): #{path}"
    end
  end
  # 検証は全ライブラリの登録後に行う(辞書順で先に来るエンティティが
  # 未登録ライブラリを誤って「未知」と判定しないように)
  infos.each do |path, info|
    validate(path, info) unless info[:fragment]
  end
  self
end