Module: Glslkit::Bundle

Defined in:
lib/glslkit/bundle.rb

Overview

1プログラム分(vertex+fragment)を「前処理→Program化→検証→Manifest組み立て」 まで通す中間表現(M11b、SPEC-livereload.md §3.2)。core/rails/wasmのどれにも 属さない — Glslkit::WebGL::Embed はこれを .rb に整形するだけの薄い ラッパーになり、Rails側のライブリロードは同じ結果をJSONに整形して返す (シリアライズ形式だけが違う、というのがこのクラスを切り出した理由)。

.build は例外を投げない。前処理(#include解決)が失敗した場合も、 Validatorがエラーを返した場合も、結果は常に Result として返る。 例外にして呼び出し側に伝える/伝えないの判断は呼び出し側の役目 (Embedは前者、Railsのライブリロードエンドポイントは後者になる想定)。

Defined Under Namespace

Classes: Result

Constant Summary collapse

PREPROCESS_ERRORS =

Preprocessor#process とその内部で使うResolverが実際に投げうる例外を 尽くす。ここに列挙されていない例外(想定外のバグ等)は握り潰さず そのまま伝播させる。

[
  IncludeNotFound,
  CircularIncludeError,
  VersionConflictError,
  PathTraversalError
].freeze

Class Method Summary collapse

Class Method Details

.build(resolver:, name:, vertex:, fragment:, line_directives: true, disabled_checks: [], urls: {vertex: vertex, fragment: fragment}, known_files: [], now: Time.now) ⇒ Object

resolver: #include解決に使うResolver(呼び出し側が用意する。FileSystem/Hash等)。 vertex/fragment: resolverへ渡すエントリポイントのリクエスト文字列(相対パス)。 urls: Manifestのstage.urlに載せる値。省略時はvertex/fragmentのパス自身を使う (Embedの用途。実URLが要るRails側は明示的に渡す)。 known_files: ポーリング用digest(§3.2 決定1)を、前処理が失敗した回にも 計算するための「前回成功時のfiles」。呼び出し側が持ち回る (Bundle自身はステートレスに保つ)。



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/glslkit/bundle.rb', line 63

def build(resolver:, name:, vertex:, fragment:, line_directives: true, disabled_checks: [],
  urls: {vertex: vertex, fragment: fragment}, known_files: [], now: Time.now)
  preprocessor = Preprocessor.new(resolver: resolver, line_directives: line_directives)

  begin
    vertex_source = preprocessor.process(vertex)
    fragment_source = preprocessor.process(fragment)
  rescue *PREPROCESS_ERRORS => e
    digest = source_digest(resolver, fallback_files(known_files, [vertex, fragment]))
    return Result.new(name: name, kind: :preprocess, source_digest: digest, error: e)
  end

  program = Program.new(name: name, sources: {vertex: vertex_source, fragment: fragment_source})
  validation = Validator.new(disabled: disabled_checks).validate(program)
  digest = source_digest(resolver, (vertex_source.source_map.files + fragment_source.source_map.files).uniq)

  unless validation.ok?
    return Result.new(
      name: name, kind: :validation, source_digest: digest,
      vertex: vertex_source, fragment: fragment_source, diagnostics: validation.diagnostics
    )
  end

  manifest = Manifest.build(programs: [program], urls: {name => urls}, now: now)
  Result.new(
    name: name, source_digest: digest, vertex: vertex_source, fragment: fragment_source,
    manifest: manifest, diagnostics: validation.diagnostics
  )
end

.fallback_files(known_files, entry_points) ⇒ Object



93
94
95
# File 'lib/glslkit/bundle.rb', line 93

def fallback_files(known_files, entry_points)
  (known_files.empty? ? entry_points : known_files + entry_points).uniq
end

.read_raw(resolver, path) ⇒ Object

digest計算専用の再読み込み。known_filesに含まれる「前回成功時のファイル」が その後削除されている等で読めなくなっていても、digest計算そのものは 落とさない(ポーリングを壊さないことを優先する。digestは変化を検知できれば 十分で、読めなかった1ファイルの厳密な反映までは求めない)。



111
112
113
114
115
116
# File 'lib/glslkit/bundle.rb', line 111

def read_raw(resolver, path)
  _, content = resolver.read(path, from: nil)
  content
rescue
  nil
end

.source_digest(resolver, files) ⇒ Object

依存する全ソースファイルの生バイト列(前処理後のコードではない)を パスでソートして "path\0content" で連結し、SHA256を取る(決定1)。



99
100
101
102
103
104
105
# File 'lib/glslkit/bundle.rb', line 99

def source_digest(resolver, files)
  entries = files.sort.filter_map do |path|
    content = read_raw(resolver, path)
    "#{path}\0#{content}" unless content.nil?
  end
  Digest.hexdigest(entries.join)
end