glslkit-rails 0.1.0.pre

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d51a2bdc32253c7b684bee4903f3b0153631fa67be1e0cb440d70a51de043c51
4
+ data.tar.gz: 5352b0f62770e5914db0de3bbe4f1e38d6f0dddc6dd835f1e1bd0e713b091b5b
5
+ SHA512:
6
+ metadata.gz: 93af6788fe949b71df49fe5d43a326123808e5d3a6a2621aac6f9c8d9e4ff91075caced01bdff0910dd22e9ee311013623fc4c5827a603cea0acc9328e783025
7
+ data.tar.gz: 4de7818cab7bb89ce2aed399c4b83130f3b43ab7b68f8ce525080135d2823e9c3ac4cafc8b95d828e366884f2f9b2898beae1c471fa5c024d1e44b62412f7458
data/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ 初回リリース (v0.1)。
6
+
7
+ ### 追加
8
+
9
+ - `.glsl` / `.vert` / `.frag` をPropshaftのアセットとして登録
10
+ (content_type: `x-shader/x-vertex` / `x-shader/x-fragment` / `text/plain`)
11
+ - `Glslkit::Rails::Compiler` — Propshaft経由で `#include` を解決した内容を配信
12
+ (developmentは毎リクエスト再処理、productionは`assets:precompile`時に1回)
13
+ - `rake glslkit:reflect` と `assets:precompile` フックによる
14
+ `glsl-manifest.json` の自動生成
15
+ - `Glslkit::Rails.manifest` — Ruby側からの読み取り窓口 (productionはメモ化)
16
+ - `glsl_script_tag` / `glsl_manifest_tag` ビューヘルパー
17
+ (`</script>`エスケープ、CSP nonce対応)
18
+ - `config.glslkit.validate` / `fail_on_warning` / `disabled_checks` —
19
+ `Glslkit::Validator` を `ManifestBuilder` に配線。development では検証結果を
20
+ ログに出すだけで失敗させないが、`assets:precompile` は検証エラーがあれば
21
+ `Glslkit::ValidationFailed` を投げてマニフェストを書き出す前に止める。
22
+ `rake glslkit:check` は設定に関わらず常に検証し、終了コードで判定できる
23
+ - development専用のライブリロードエンドポイント(`ActionController::API`
24
+ ベースのEngine、決定7)。`GET /glslkit/digests.json`(ソースdigestのみ)と
25
+ `GET /glslkit/programs/:name.json`(シェーダ本文・マニフェスト・SourceMap
26
+ ・診断)。production相当の環境ではミドルウェア自体を挿入しないため一切
27
+ 存在しない。`glslkit-webgl`側の`Context#reload_program`/`live_reload`と
28
+ 組み合わせて使う(0.1.0の看板機能)
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kyubey1228
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # glslkit-rails
2
+
3
+ [`glslkit`](../core) を Rails (Propshaft) のアセットパイプラインに統合する gem。
4
+ `.glsl` / `.vert` / `.frag` をアセットとして登録し、`#include` を解決した内容を
5
+ 配信し、`assets:precompile` 時にリフレクションマニフェストを書き出す。
6
+
7
+ ## インストール
8
+
9
+ ```ruby
10
+ gem "glslkit-rails"
11
+ ```
12
+
13
+ `glslkit`・`railties >= 7.1`・`propshaft >= 1.3.2` に依存する。
14
+
15
+ ## 設定
16
+
17
+ ```ruby
18
+ # config/application.rb
19
+ config.glslkit.paths = ["app/shaders"] # デフォルト: app/shaders
20
+ config.glslkit.minify = Rails.env.production?
21
+ config.glslkit.line_directives = !Rails.env.production?
22
+ config.glslkit.manifest_path = "glsl-manifest.json"
23
+ ```
24
+
25
+ ## アセットとしての配信
26
+
27
+ `config.glslkit.paths` 配下のファイルはPropshaftのload_pathに追加され、
28
+ `#include` を解決した内容がcontent_type `x-shader/x-vertex` /
29
+ `x-shader/x-fragment` / `text/plain` で配信される。developmentでは
30
+ 毎リクエスト再処理し、productionでは `assets:precompile` 時に1回だけ処理する。
31
+
32
+ ## マニフェスト
33
+
34
+ `rake assets:precompile` を実行すると、自動的に
35
+ `public/assets/<config.glslkit.manifest_path>` (デフォルト `glsl-manifest.json`)
36
+ にリフレクションマニフェストを書き出す。単体で実行する場合:
37
+
38
+ ```
39
+ rake glslkit:reflect
40
+ ```
41
+
42
+ Ruby側から読む場合:
43
+
44
+ ```ruby
45
+ Glslkit::Rails.manifest # => Hash (productionでは読み込み結果をメモ化する)
46
+ ```
47
+
48
+ ## ビューヘルパー
49
+
50
+ ```erb
51
+ <%= glsl_script_tag "pbr.vert" %>
52
+ <%# => <script type="x-shader/x-vertex" id="pbr-vert">...</script> (インライン埋め込み) %>
53
+
54
+ <%= glsl_manifest_tag %>
55
+ <%# => <script type="application/json" id="glsl-manifest">...</script> %>
56
+ ```
57
+
58
+ - インライン埋め込みの内容は `</script` をエスケープしている
59
+ - そのリクエストでCSPが実際に有効な場合(`content_security_policy?`)、
60
+ 自動的に `nonce` 属性を付与する。`nonce: true`/`false` で強制・抑止も
61
+ できる
62
+
63
+ ## 実践的な例
64
+
65
+ `app/shaders/` にPBR風のマテリアルシェーダーを置く。共有partialの
66
+ uniform block、複数テクスチャ、uniform配列を含む、より現実的な構成:
67
+
68
+ ```glsl
69
+ # app/shaders/common/camera.glsl
70
+ layout(std140, binding = 0) uniform Camera {
71
+ mat4 view;
72
+ mat4 projection;
73
+ vec3 eye_position;
74
+ };
75
+ ```
76
+
77
+ ```glsl
78
+ # app/shaders/material.vert
79
+ #version 300 es
80
+ #include "common/camera.glsl"
81
+
82
+ layout(location = 0) in vec3 a_position;
83
+ layout(location = 1) in vec3 a_normal;
84
+ layout(location = 2) in vec2 a_uv;
85
+
86
+ uniform mat4 u_model;
87
+
88
+ out vec3 v_world_position;
89
+ out vec3 v_normal;
90
+ out vec2 v_uv;
91
+
92
+ void main() {
93
+ vec4 world_position = u_model * vec4(a_position, 1.0);
94
+ v_world_position = world_position.xyz;
95
+ v_normal = mat3(u_model) * a_normal;
96
+ v_uv = a_uv;
97
+ gl_Position = projection * view * world_position;
98
+ }
99
+ ```
100
+
101
+ ```glsl
102
+ # app/shaders/material.frag
103
+ #version 300 es
104
+ precision highp float;
105
+ #include "common/camera.glsl"
106
+
107
+ in vec3 v_world_position;
108
+ in vec3 v_normal;
109
+ in vec2 v_uv;
110
+
111
+ uniform sampler2D u_albedo_map;
112
+ uniform vec4 u_base_color;
113
+ uniform vec3 u_light_positions[4];
114
+ uniform vec3 u_light_colors[4];
115
+ uniform int u_light_count;
116
+
117
+ out vec4 fragColor;
118
+
119
+ void main() {
120
+ vec3 albedo = texture(u_albedo_map, v_uv).rgb * u_base_color.rgb;
121
+ vec3 n = normalize(v_normal);
122
+
123
+ vec3 total = vec3(0.0);
124
+ for (int i = 0; i < u_light_count; i++) {
125
+ vec3 to_light = normalize(u_light_positions[i] - v_world_position);
126
+ total += albedo * u_light_colors[i] * max(dot(n, to_light), 0.0);
127
+ }
128
+
129
+ fragColor = vec4(total, u_base_color.a);
130
+ }
131
+ ```
132
+
133
+ ビューでは平坦化済みシェーダーとマニフェストをまとめて埋め込む:
134
+
135
+ ```erb
136
+ <%# app/views/scenes/show.html.erb %>
137
+ <%= glsl_script_tag "material.vert" %>
138
+ <%= glsl_script_tag "material.frag" %>
139
+ <%= glsl_manifest_tag %>
140
+ ```
141
+
142
+ `glsl_script_tag "material.vert"` は `#include "common/camera.glsl"` を
143
+ 解決した平坦化済みGLSLを `id="material-vert"` として埋め込み、
144
+ `glsl_manifest_tag` は attribute の location・uniform の型/setter/配列サイズ・
145
+ uniform blockのlayout/bindingを持つJSONを `id="glsl-manifest"` として埋め込む。
146
+
147
+ JS側は、このマニフェストを消費するだけでWebGLプログラムをセットアップできる。
148
+ **`gl.getActiveUniform`/`gl.getActiveAttrib` でイントロスペクションするループが
149
+ 不要になる**のがglslkitの狙い:
150
+
151
+ ```js
152
+ function compileProgram(gl, manifestProgram, shaderElementIds) {
153
+ const vertexShader = compileShader(gl, gl.VERTEX_SHADER,
154
+ document.getElementById(shaderElementIds.vertex).textContent);
155
+ const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER,
156
+ document.getElementById(shaderElementIds.fragment).textContent);
157
+
158
+ const program = gl.createProgram();
159
+ gl.attachShader(program, vertexShader);
160
+ gl.attachShader(program, fragmentShader);
161
+ gl.linkProgram(program);
162
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
163
+ throw new Error(gl.getProgramInfoLog(program));
164
+ }
165
+
166
+ const attributes = {};
167
+ for (const attr of manifestProgram.attributes) {
168
+ // location: null の場合だけ getAttribLocation を引けばよい
169
+ attributes[attr.name] = attr.location ?? gl.getAttribLocation(program, attr.name);
170
+ }
171
+
172
+ const uniforms = {};
173
+ let textureUnit = 0;
174
+ for (const uniform of manifestProgram.uniforms) {
175
+ const location = gl.getUniformLocation(program, uniform.name);
176
+ if (!location) continue; // 未使用uniformはコンパイラに削除されうる
177
+
178
+ if (uniform.sampler) {
179
+ const unit = textureUnit++;
180
+ uniforms[uniform.name] = (texture) => {
181
+ gl.activeTexture(gl.TEXTURE0 + unit);
182
+ gl.bindTexture(gl.TEXTURE_2D, texture);
183
+ gl.uniform1i(location, unit);
184
+ };
185
+ } else if (uniform.matrix) {
186
+ uniforms[uniform.name] = (value) => gl[uniform.setter](location, false, value);
187
+ } else {
188
+ uniforms[uniform.name] = (value) => gl[uniform.setter](location, value);
189
+ }
190
+ }
191
+
192
+ return {program, attributes, uniforms};
193
+ }
194
+
195
+ function compileShader(gl, type, source) {
196
+ const shader = gl.createShader(type);
197
+ gl.shaderSource(shader, source);
198
+ gl.compileShader(shader);
199
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
200
+ throw new Error(gl.getShaderInfoLog(shader));
201
+ }
202
+ return shader;
203
+ }
204
+
205
+ const manifest = JSON.parse(document.getElementById("glsl-manifest").textContent);
206
+ const material = compileProgram(gl, manifest.programs.material, {
207
+ vertex: "material-vert",
208
+ fragment: "material-frag",
209
+ });
210
+
211
+ material.uniforms.u_model(modelMatrix);
212
+ material.uniforms.u_base_color(new Float32Array([1, 1, 1, 1]));
213
+ material.uniforms.u_albedo_map(albedoTexture);
214
+ ```
215
+
216
+ `uniform.setter`/`uniform.matrix`/`uniform.sampler`はすべて
217
+ [`Glslkit::Types`](../core#マニフェスト生成)由来で、`spec/schema/reflection-v1.json`
218
+ に準拠する。
219
+
220
+ ## ライセンス
221
+
222
+ [MIT](LICENSE.txt)
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Glslkit::Rails::LiveReload::Engine 自身のルート(M11c、SPEC-livereload.md §3.3)。
4
+ # このファイルは `Rails::Engine.root` が glslkit-rails 自体になるため、
5
+ # ホストアプリの config/routes.rb とは無関係。ホストアプリへの接続は
6
+ # glslkit-rails/lib/glslkit/rails/live_reload/mount.rb がミドルウェアとして
7
+ # 行う(理由はそちらのコメントを参照)。
8
+ Glslkit::Rails::LiveReload::Engine.routes.draw do
9
+ get "digests.json", to: "programs#digests"
10
+ get "programs/:name", to: "programs#show"
11
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/glslkit/rails/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "glslkit-rails"
7
+ spec.version = Glslkit::Rails::VERSION
8
+ spec.authors = ["kyubey1228"]
9
+ spec.email = ["kyuuka1228@gmail.com"]
10
+
11
+ spec.summary = "Rails (Propshaft) integration for glslkit"
12
+ spec.description = "Registers .glsl/.vert/.frag assets with Propshaft, resolves " \
13
+ "#include directives at request/precompile time, and publishes a shader " \
14
+ "reflection manifest for consumption by views and JS at runtime."
15
+ spec.homepage = "https://github.com/kyubey1228/glslkit"
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 3.1"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/main/rails"
21
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/rails/CHANGELOG.md"
22
+
23
+ spec.files = Dir.glob("lib/**/*.{rb,rake}") + Dir.glob("config/**/*.rb") +
24
+ ["glslkit-rails.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"]
25
+ spec.require_paths = ["lib"]
26
+
27
+ # M12c: `~> 0.1.0` は `Gem::Requirement` の仕様上プレリリース版を一切
28
+ # 満たさない(`"~> 0.1.0".satisfied_by?("0.1.0.pre")` は常にfalse)。
29
+ # 0.1.0.pre のリリース期間中だけこの範囲にし、正式な0.1.0を切ったら
30
+ # `~> 0.1.0` に戻すこと。
31
+ spec.add_dependency "glslkit", ">= 0.1.0.pre", "< 0.2.0"
32
+ # M12b: `gem build`は無制限の">= 7.1"を非推奨としてwarnする。上限を
33
+ # 付ける案(CIで検証済みの範囲に基づき`< 9`等)も検討したが、採用しない。
34
+ # 上限は「まだ検証していない将来のメジャーバージョン」を無条件で締め出す
35
+ # ため、Rails側が実際には互換性を保ったままリリースしても、こちらが
36
+ # gemを更新するまで利用者は新しいRailsに上げられなくなる。CIで検証
37
+ # しているのはRuby 3.1→Rails 7.2、既定→Rails 8.1の範囲だが、それを
38
+ # 「動作保証の上限」と誤解させたくない。`gem build`の警告は許容する。
39
+ spec.add_dependency "railties", ">= 7.1"
40
+ # railties >= 7.1に推移的に含まれるため追加インストールは発生しないが、
41
+ # M11でActionController::APIベースのライブリロードEngineを提供するため
42
+ # 明示的に宣言する(SPEC-livereload.md 決定7)。
43
+ spec.add_dependency "actionpack", ">= 7.1"
44
+ # 実際にインストールされた1.3.2で確認したCompiler API
45
+ # (Propshaft::Compiler#compile(asset, input), #referenced_by(asset),
46
+ # Propshaft::Compilers#register(mime_type, klass)) に合わせて固定している。
47
+ # それより古い1.x系では確認していないため、より広い範囲は主張しない。
48
+ # 上限を付けない理由はrailties/actionpackと同じ。
49
+ spec.add_dependency "propshaft", ">= 1.3.2"
50
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "propshaft/compiler"
5
+
6
+ module Glslkit
7
+ module Rails
8
+ # x-shader/x-vertex, x-shader/x-fragment, text/plain (§4.1) の3つに
9
+ # 登録される。前者2つは我々専用だが、text/plainはPropshaftが配信しうる
10
+ # 他のあらゆるプレーンテキストアセットと共有される — Propshaftはcompiler
11
+ # の登録をcontent_typeで紐付ける(拡張子ではない)ため、これが無いと
12
+ # このcompilerが無関係なtext/plainアセットにまで作用してしまう。
13
+ # 以下の拡張子ガードがそれを防いでいる。
14
+ class Compiler < ::Propshaft::Compiler
15
+ RELEVANT_EXTENSIONS = %w[.glsl .vert .frag].freeze
16
+
17
+ def compile(asset, input)
18
+ return input unless relevant?(asset)
19
+
20
+ source = preprocess(asset)
21
+ minify? ? Glslkit::Minifier.minify(source.code) : source.code
22
+ end
23
+
24
+ def referenced_by(asset)
25
+ return Set.new unless relevant?(asset)
26
+
27
+ source = preprocess(asset)
28
+ source.source_map.files.drop(1).filter_map { |path| load_path.find(path) }.to_set
29
+ end
30
+
31
+ private
32
+
33
+ def relevant?(asset)
34
+ RELEVANT_EXTENSIONS.include?(asset.logical_path.extname)
35
+ end
36
+
37
+ def preprocess(asset)
38
+ resolver = Glslkit::Resolvers::FileSystem.new(load_paths: load_path.paths.map(&:to_s))
39
+ Glslkit::Preprocessor.new(resolver: resolver, line_directives: line_directives?).process(asset.logical_path.to_s)
40
+ end
41
+
42
+ def glslkit_config
43
+ ::Rails.application.config.glslkit
44
+ end
45
+
46
+ def minify?
47
+ glslkit_config.minify
48
+ end
49
+
50
+ def line_directives?
51
+ glslkit_config.line_directives
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glslkit
4
+ module Rails
5
+ # ActionView上でincludeされるビューヘルパー (§4.3)。
6
+ module Helper
7
+ # <%= glsl_script_tag "pbr.vert" %>
8
+ # => <script type="x-shader/x-vertex" id="pbr-vert">...</script>
9
+ def glsl_script_tag(logical_path, **html_options)
10
+ asset = find_glsl_asset(logical_path)
11
+ html_options[:id] ||= glsl_script_id(logical_path.to_s)
12
+ html_options[:type] = asset.content_type.to_s
13
+ apply_glsl_nonce!(html_options)
14
+
15
+ content_tag(:script, escape_closing_script_tags(asset.compiled_content).html_safe, html_options)
16
+ end
17
+
18
+ # <%= glsl_manifest_tag %>
19
+ # => <script type="application/json" id="glsl-manifest">...</script>
20
+ def glsl_manifest_tag(**html_options)
21
+ html_options[:id] ||= "glsl-manifest"
22
+ html_options[:type] = "application/json"
23
+ apply_glsl_nonce!(html_options)
24
+
25
+ content_tag(:script, escape_closing_script_tags(Glslkit::Rails.manifest.to_json).html_safe, html_options)
26
+ end
27
+
28
+ private
29
+
30
+ def find_glsl_asset(logical_path)
31
+ ::Rails.application.assets.load_path.find(logical_path.to_s) ||
32
+ raise(ArgumentError, "no such glslkit asset: #{logical_path.inspect}")
33
+ end
34
+
35
+ # "pbr.vert" => "pbr-vert", "effects/glow.frag" => "effects-glow-frag"
36
+ def glsl_script_id(logical_path)
37
+ base = logical_path.sub(/\.(vert|frag|glsl)\z/, "")
38
+ "#{base.tr("/", "-")}-#{Regexp.last_match(1)}"
39
+ end
40
+
41
+ # Rails自身のjavascript_tagがJS_ESCAPE_MAPで"</" => '<\/'として行っている
42
+ # のと同じ理由・同じエスケープ: script要素の中身に文字通り"</"が現れると、
43
+ # 中身がJS/JSONとして妥当かどうかに関わらずHTMLパーサがそこでタグを
44
+ # 閉じてしまう。
45
+ def escape_closing_script_tags(content)
46
+ content.gsub("</", '<\/')
47
+ end
48
+
49
+ # true=強制、false=抑止、未指定ならこのリクエストでCSPが実際に有効な
50
+ # 場合だけ自動付与する。
51
+ #
52
+ # 当初は javascript_tag と全く同じ規約
53
+ # (ActionView::Helpers::JavaScriptHelper.auto_include_nonce)に合わせて
54
+ # いたが、これは config.content_security_policy_nonce_auto という
55
+ # Rails 8.1で新規追加された設定に依存しており、railties >= 7.1という
56
+ # このgemの依存下限では単純にメソッドが存在せずNoMethodErrorになる
57
+ # (CIでRuby 3.1 → Rails 7.2に解決された際に発覚)。content_security_policy?
58
+ # はRails 7.1時点から存在するので、こちらを使う。
59
+ def apply_glsl_nonce!(html_options)
60
+ if html_options[:nonce] == false
61
+ html_options.delete(:nonce)
62
+ elsif html_options[:nonce] == true ||
63
+ (!html_options.key?(:nonce) && respond_to?(:content_security_policy?) && content_security_policy?)
64
+ html_options[:nonce] = content_security_policy_nonce
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+
5
+ module Glslkit
6
+ module Rails
7
+ module LiveReload
8
+ # ライブリロード用の2エンドポイント(§3.3)を提供するEngine。
9
+ # `isolate_namespace` によりホストアプリの `ProgramsController` 等と
10
+ # 衝突しない。マウントは `Railtie` が `config.glslkit.live_reload` を
11
+ # 見て判断する — このEngineが定義されていること自体は production でも
12
+ # 構わない(未マウントならルートは一切生成されない)。
13
+ #
14
+ # ルートは `rails/config/routes.rb`(このEngineの root 直下)に書いて
15
+ # いる。クラス本体で直接 `routes.draw do ... end` する形も動くが、
16
+ # それは一度きりの実行にしかならない。Rails::Engineは自身のroute_setを
17
+ # `app.routes_reloader.route_sets` に登録し、アプリのルートリロード
18
+ # サイクルで一緒に clear!/reload される — Rails 7.2ではこの
19
+ # サイクルで実際に一度 clear! されるため、クラス本体に書いた内容は
20
+ # 再度描画されず空になって消える(Rails 8.1では起きなかったため
21
+ # 実機検証で気づくまで見逃していた)。`config/routes.rb` ファイルに
22
+ # すればreload時に毎回 `load` され直すため、バージョンに関わらず安定する。
23
+ class Engine < ::Rails::Engine
24
+ isolate_namespace Glslkit::Rails::LiveReload
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glslkit
4
+ module Rails
5
+ module LiveReload
6
+ # プログラム名ごとに「最後に成功したビルドの files」をプロセス内メモリで
7
+ # 保持する。Glslkit::Bundle.build の `known_files:` に渡すためのもので、
8
+ # 前処理が失敗した回でも意味のある source_digest を計算できるようにする
9
+ # (SPEC-livereload.md §3.2 決定1)。development専用の機能なので永続化は
10
+ # 不要(プロセスを跨いだ保持は求めない)。
11
+ #
12
+ # development でも Puma は複数スレッドで動くため、単純な Hash への
13
+ # 読み書きではレースの余地がある。Mutex で保護する。
14
+ module KnownFiles
15
+ MUTEX = Mutex.new
16
+ @files_by_name = {}
17
+
18
+ module_function
19
+
20
+ def fetch(name)
21
+ MUTEX.synchronize { @files_by_name.fetch(name, []) }
22
+ end
23
+
24
+ def remember(name, files)
25
+ MUTEX.synchronize { @files_by_name[name] = files }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glslkit
4
+ module Rails
5
+ module LiveReload
6
+ # `/glslkit/*` へのリクエストをEngineへ振り分ける薄いミドルウェア。
7
+ #
8
+ # 当初は `app.routes.append { mount Engine => "/glslkit" }` を
9
+ # Railtieのinitializerから呼ぶ形にしていたが、これは
10
+ # `ActionDispatch::Routing::RouteSet#finalize!`(`@append`ブロックの
11
+ # 反映はここでしか起きない)が、そのinitializerより先に一度でも
12
+ # 走っているかどうかに依存してしまい、Railtie初期化順序次第で
13
+ # 再現性なく404になることが実機検証で判明した(finalize!後に
14
+ # appendしても再finalizeされない)。
15
+ #
16
+ # ミドルウェアスタックは全initializerが完了した後に一度だけ構築される
17
+ # ため、`config.middleware.use`によるこの登録はタイミング非依存で
18
+ # 安定して動く。Engine自体は本物のRails::Engineであり、内部の
19
+ # ルーティング(digests.json / programs/:name)は変更していない。
20
+ class Mount
21
+ PREFIX = "/glslkit"
22
+
23
+ def initialize(app, engine)
24
+ @app = app
25
+ @engine = engine
26
+ end
27
+
28
+ def call(env)
29
+ path = env["PATH_INFO"]
30
+ return @app.call(env) unless path == PREFIX || path.start_with?("#{PREFIX}/")
31
+
32
+ # 通常の `mount` がやるのと同じ書き換え(SCRIPT_NAME/PATH_INFOの
33
+ # 付け替え)を自分で行う。Engine自身のルート(`digests.json` /
34
+ # `programs/:name`)はプレフィックス無しのパスとして定義している
35
+ # ため、これをしないとEngine内のルーティングが一致しない。
36
+ rest = path.delete_prefix(PREFIX)
37
+ rest = "/#{rest}".squeeze("/")
38
+
39
+ env = env.merge(
40
+ "SCRIPT_NAME" => "#{env["SCRIPT_NAME"]}#{PREFIX}",
41
+ "PATH_INFO" => rest
42
+ )
43
+ @engine.call(env)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller/api"
4
+
5
+ module Glslkit
6
+ module Rails
7
+ module LiveReload
8
+ # ライブリロードのポーリング対象になる2つのエンドポイント(§3.3)。
9
+ # ActionController::API を使う(SPEC-livereload.md 決定7)。development
10
+ # でのみマウントされる(Engine#config.glslkit.live_reload が false の
11
+ # 場合は Railtie が一切マウントしないため、production ではこの
12
+ # コントローラ自体にルーティングで到達できない)。
13
+ #
14
+ # Preprocessor/Validator の失敗は200 + ペイロードで返す(§3.4)。それ以外の
15
+ # 想定外の例外はここで握り潰さず、素通しして500にする。
16
+ class ProgramsController < ActionController::API
17
+ # GET /glslkit/digests.json => {"neon" => "5059f642...", ...}
18
+ # ソースdigest(決定1)のみを返す軽量エンドポイント。
19
+ def digests
20
+ digests = ShaderPrograms.discover(rails_app).each_with_object({}) do |(name, stages), acc|
21
+ acc[name] = build(name, stages).source_digest
22
+ end
23
+ render json: digests
24
+ end
25
+
26
+ # GET /glslkit/programs/:name.json => §3.2 のJSON(失敗時は§3.4)。
27
+ def show
28
+ programs = ShaderPrograms.discover(rails_app)
29
+ stages = programs[params[:name]]
30
+ return head :not_found unless stages
31
+
32
+ render json: payload_for(build(params[:name], stages))
33
+ end
34
+
35
+ private
36
+
37
+ def rails_app
38
+ ::Rails.application
39
+ end
40
+
41
+ def glslkit_config
42
+ rails_app.config.glslkit
43
+ end
44
+
45
+ def build(name, stages)
46
+ resolver = Glslkit::Resolvers::FileSystem.new(load_paths: ShaderPrograms.roots(rails_app))
47
+ result = Glslkit::Bundle.build(
48
+ resolver: resolver, name: name, vertex: stages.fetch(:vertex), fragment: stages.fetch(:fragment),
49
+ line_directives: glslkit_config.line_directives, disabled_checks: glslkit_config.disabled_checks,
50
+ known_files: KnownFiles.fetch(name)
51
+ )
52
+ KnownFiles.remember(name, (result.vertex.source_map.files + result.fragment.source_map.files).uniq) if result.ok?
53
+ result
54
+ end
55
+
56
+ def payload_for(result)
57
+ return success_payload(result) if result.ok?
58
+
59
+ error_payload(result)
60
+ end
61
+
62
+ def success_payload(result)
63
+ {
64
+ name: result.name,
65
+ source_digest: result.source_digest,
66
+ vertex: result.vertex.code,
67
+ fragment: result.fragment.code,
68
+ manifest: result.manifest,
69
+ source_maps: {vertex: result.vertex.source_map.to_h, fragment: result.fragment.source_map.to_h},
70
+ diagnostics: result.diagnostics.map { |d| diagnostic_hash(d) }
71
+ }
72
+ end
73
+
74
+ def error_payload(result)
75
+ error = (result.kind == :validation) ? {diagnostics: result.diagnostics.map { |d| diagnostic_hash(d) }} : {message: result.error.message, class: result.error.class.name}
76
+
77
+ {name: result.name, source_digest: result.source_digest, error: {kind: result.kind.to_s, **error}}
78
+ end
79
+
80
+ def diagnostic_hash(diagnostic)
81
+ {
82
+ severity: diagnostic.severity, code: diagnostic.code, message: diagnostic.message,
83
+ program: diagnostic.program, stage: diagnostic.stage, name: diagnostic.name,
84
+ file: diagnostic.file, line: diagnostic.line
85
+ }.compact
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module Glslkit
6
+ module Rails
7
+ # config.glslkit.paths 配下からvertex/fragmentのペアを検出し、その
8
+ # すべてについて reflection-v1.json のマニフェスト(Glslkit::Manifest, M4)
9
+ # を組み立てる。stage.urlは Rails.application.assets.resolver 経由で
10
+ # 解決する。これはasset_path/asset_urlが内部で使っているのとまさに同じ
11
+ # 経路であり、developmentではDynamic、assets:precompile実行後はStatic
12
+ # (Propshaftが直前に書いた.manifest.jsonを読む)になる。
13
+ class ManifestBuilder
14
+ attr_reader :diagnostics
15
+
16
+ def initialize(app)
17
+ @app = app
18
+ @diagnostics = []
19
+ end
20
+
21
+ # Validatorは常に実行し、@diagnosticsに集める(config.glslkit.validateの
22
+ # 値によらない)。`config.glslkit.validate`は「診断を集めるかどうか」
23
+ # ではなく「その診断を理由にビルド/リクエストを失敗させるかどうか」を
24
+ # 制御する。呼び出し側(Glslkit::Rails.manifest / rake glslkit:reflect /
25
+ # rake glslkit:check)がそれぞれの文脈でdiagnostics/failing?を見て
26
+ # 反応を決める(SPEC.md §8.6)。
27
+ def build
28
+ @diagnostics = []
29
+ manifest = Glslkit::Manifest.new(generated_at: Time.now.utc.iso8601)
30
+ programs.each do |name, stages|
31
+ vertex_source = preprocessor.process(stages.fetch(:vertex))
32
+ fragment_source = preprocessor.process(stages.fetch(:fragment))
33
+ validate(name, vertex_source, fragment_source)
34
+
35
+ manifest.add_program(
36
+ name,
37
+ vertex: {path: stages.fetch(:vertex), source: vertex_source, url: asset_url(stages.fetch(:vertex))},
38
+ fragment: {path: stages.fetch(:fragment), source: fragment_source, url: asset_url(stages.fetch(:fragment))}
39
+ )
40
+ end
41
+ manifest.to_h
42
+ end
43
+
44
+ # エラーが1件でもあれば真。`fail_on_warning`が有効なら警告のみでも真。
45
+ # config.glslkit.validateがfalseなら常に偽(検証結果があっても
46
+ # ビルド/リクエストを失敗させない)。
47
+ def failing?
48
+ return false unless glslkit_config.validate
49
+
50
+ return true if @diagnostics.any?(&:error?)
51
+
52
+ glslkit_config.fail_on_warning && @diagnostics.any?(&:warning?)
53
+ end
54
+
55
+ private
56
+
57
+ # Validator#validate は必ず Manifest.build(ここでは manifest.add_program)
58
+ # より前に呼ぶこと(SPEC.md §8.4/§8.6)。同一ステージ内の重複などは
59
+ # マージ後には検出できないため。
60
+ def validate(name, vertex_source, fragment_source)
61
+ program = Glslkit::Program.new(name: name, sources: {vertex: vertex_source, fragment: fragment_source})
62
+ result = Glslkit::Validator.new(disabled: glslkit_config.disabled_checks).validate(program)
63
+ @diagnostics.concat(result.diagnostics)
64
+ end
65
+
66
+ def glslkit_config
67
+ @app.config.glslkit
68
+ end
69
+
70
+ # 発見ロジック本体はShaderProgramsに集約している(LiveReload::ProgramsController
71
+ # と共有するため、M11c)。
72
+ def shader_roots
73
+ ShaderPrograms.roots(@app)
74
+ end
75
+
76
+ def programs
77
+ ShaderPrograms.discover(@app)
78
+ end
79
+
80
+ def stage_input(relative_path)
81
+ {path: relative_path, source: preprocessor.process(relative_path), url: asset_url(relative_path)}
82
+ end
83
+
84
+ def preprocessor
85
+ @preprocessor ||= Glslkit::Preprocessor.new(
86
+ resolver: Glslkit::Resolvers::FileSystem.new(load_paths: shader_roots),
87
+ line_directives: glslkit_config.line_directives
88
+ )
89
+ end
90
+
91
+ def asset_url(relative_path)
92
+ @app.assets.resolver.resolve(relative_path)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Glslkit
6
+ module Rails
7
+ class << self
8
+ # productionでは `rake assets:precompile` が書き出したマニフェストを
9
+ # 読むだけ(メモ化する: プロセスが生きている間、ファイルは変化しない
10
+ # ため)。development/testでは呼び出すたびに現在のシェーダソースから
11
+ # 再構築する。これは config.glslkit.line_directives/minify の
12
+ # dev-vs-production の使い分け方針(SPEC.md §4.1, §4.4)と同じ考え方。
13
+ def manifest
14
+ if ::Rails.env.production?
15
+ @manifest ||= read_precompiled_manifest
16
+ else
17
+ build_and_log_diagnostics
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # developmentでは検証結果をログに出すが、決して例外を投げない
24
+ # (SPEC.md §8.6: 「development では検証するが失敗させない」)。
25
+ # 失敗させるのは `assets:precompile` フック(rails/lib/tasks/glslkit.rake)
26
+ # と `rake glslkit:check` の役目。
27
+ def build_and_log_diagnostics
28
+ builder = ManifestBuilder.new(::Rails.application)
29
+ result = builder.build
30
+ if ::Rails.application.config.glslkit.validate
31
+ builder.diagnostics.each { |diagnostic| ::Rails.logger.warn("[glslkit] #{diagnostic}") }
32
+ end
33
+ result
34
+ end
35
+
36
+ def read_precompiled_manifest
37
+ JSON.parse(File.read(manifest_path))
38
+ end
39
+
40
+ def manifest_path
41
+ config = ::Rails.application.config
42
+ config.assets.output_path.join(config.glslkit.manifest_path)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+
5
+ module Glslkit
6
+ module Rails
7
+ class Railtie < ::Rails::Railtie
8
+ config.glslkit = ActiveSupport::OrderedOptions.new
9
+ config.glslkit.paths = ["app/shaders"]
10
+ config.glslkit.minify = ::Rails.env.production?
11
+ config.glslkit.line_directives = !::Rails.env.production?
12
+ config.glslkit.manifest_path = "glsl-manifest.json"
13
+ # M11a: SPEC.md §8.6。デフォルトで検証は有効、警告での失敗は無効。
14
+ config.glslkit.validate = true
15
+ config.glslkit.fail_on_warning = false
16
+ config.glslkit.disabled_checks = []
17
+ # M11c: SPEC-livereload.md §3.3。既定はdevelopmentでのみ有効。
18
+ config.glslkit.live_reload = ::Rails.env.development?
19
+
20
+ # Mime::Type.register を使う (Propshaft::Asset#content_type 自身が
21
+ # 実際に使っている経路)。Marcelを直接使うことはしない — MarcelはPropshaft
22
+ # の内部的・推移的な実装詳細に過ぎず、そこに結合すべきではない。
23
+ initializer "glslkit.register_mime_types" do
24
+ ::Mime::Type.register "x-shader/x-vertex", :vert unless ::Mime::Type.lookup_by_extension("vert")
25
+ ::Mime::Type.register "x-shader/x-fragment", :frag unless ::Mime::Type.lookup_by_extension("frag")
26
+ # register ではなく register_alias を使う: "text/plain" は既に
27
+ # :text として登録済みなので、同じ文字列を :glsl として register で
28
+ # 再登録するとそのlookupエントリを上書きしてしまう。
29
+ ::Mime::Type.register_alias "text/plain", :glsl unless ::Mime::Type.lookup_by_extension("glsl")
30
+ end
31
+
32
+ initializer "glslkit.configure_propshaft", before: "propshaft.assets_middleware" do |app|
33
+ app.config.glslkit.paths.each do |path|
34
+ app.config.assets.paths << ::Rails.root.join(path).to_s
35
+ end
36
+
37
+ %w[x-shader/x-vertex x-shader/x-fragment text/plain].each do |mime_type|
38
+ app.config.assets.compilers << [mime_type, Glslkit::Rails::Compiler]
39
+ end
40
+ end
41
+
42
+ rake_tasks do
43
+ load File.expand_path("../../tasks/glslkit.rake", __dir__)
44
+ end
45
+
46
+ # M11c: SPEC-livereload.md §3.3。利用者のroutes.rb編集を要求しないため、
47
+ # ミドルウェアとして直接差し込む(`app.routes.append`によるmountは
48
+ # RouteSetのfinalize!タイミングに対してrace conditionがあり、
49
+ # 実機検証で再現性なく404になることが判明したため採用しなかった。
50
+ # 詳細はLiveReload::Mountのコメントを参照)。config.glslkit.live_reload
51
+ # が false(既定でproduction)ならミドルウェア自体を挿入しないため、
52
+ # `/glslkit/*` は通常のルーティングに委ねられ存在しない扱いになる。
53
+ initializer "glslkit.mount_live_reload" do |app|
54
+ next unless app.config.glslkit.live_reload
55
+
56
+ app.middleware.use Glslkit::Rails::LiveReload::Mount, Glslkit::Rails::LiveReload::Engine
57
+ end
58
+
59
+ initializer "glslkit.view_helpers" do
60
+ ActiveSupport.on_load(:action_view) { include Glslkit::Rails::Helper }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glslkit
4
+ module Rails
5
+ # `config.glslkit.paths` 配下の .vert/.frag ペアを名前ごとに集める。
6
+ # `ManifestBuilder`(静的な precompile)と `LiveReload::ProgramsController`
7
+ # (M11c、ポーリング用のエンドポイント)の両方が全く同じ発見ロジックを
8
+ # 必要とするため、ここに1箇所へ集約する。
9
+ module ShaderPrograms
10
+ module_function
11
+
12
+ def roots(app)
13
+ app.config.glslkit.paths.map { |path| app.root.join(path).to_s }
14
+ end
15
+
16
+ # {"pbr" => {vertex: "pbr.vert", fragment: "pbr.frag"}, ...} を返す。
17
+ # 同名の相方が無い .vert/.frag はスキップする(共有partialの可能性が
18
+ # あるため。SPEC.md §2.4/§3の「プログラムは両ステージ必須」の決定)。
19
+ def discover(app)
20
+ by_name = Hash.new { |h, k| h[k] = {} }
21
+
22
+ roots(app).each do |root|
23
+ Dir.glob("**/*.{vert,frag}", base: root).each do |relative|
24
+ ext = File.extname(relative)
25
+ stage = (ext == ".vert") ? :vertex : :fragment
26
+ by_name[relative.delete_suffix(ext)][stage] = relative
27
+ end
28
+ end
29
+
30
+ by_name.select { |_, stages| stages.key?(:vertex) && stages.key?(:fragment) }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glslkit
4
+ module Rails
5
+ VERSION = "0.1.0.pre"
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "glslkit"
4
+ require "propshaft"
5
+ require_relative "rails/version"
6
+ require_relative "rails/shader_programs"
7
+ require_relative "rails/compiler"
8
+ require_relative "rails/manifest_builder"
9
+ require_relative "rails/manifest_reader"
10
+ require_relative "rails/helper"
11
+ require_relative "rails/live_reload/known_files"
12
+ require_relative "rails/live_reload/programs_controller"
13
+ require_relative "rails/live_reload/engine"
14
+ require_relative "rails/live_reload/mount"
15
+ require_relative "rails/railtie"
16
+
17
+ module Glslkit
18
+ module Rails
19
+ # 注意: この名前空間の中では、素の定数`Rails`は`Glslkit::Rails`自身に
20
+ # 解決される。トップレベルのRailsフレームワークではない。フレームワーク
21
+ # を指すときは常に `::Rails` と書くこと (例: `::Rails::Engine`)。
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :glslkit do
4
+ desc "Generate the glslkit reflection manifest (config.assets.output_path/config.glslkit.manifest_path)"
5
+ task reflect: :environment do
6
+ app = Rails.application
7
+ builder = Glslkit::Rails::ManifestBuilder.new(app)
8
+ manifest = builder.build
9
+
10
+ # config.glslkit.validateが有効な間は、precompileの入口でビルドを
11
+ # 止める(SPEC.md §8.6)。マニフェストをファイルに書く前に検査すること
12
+ # (壊れたシェーダの情報を含むマニフェストをpublic/に残さないため)。
13
+ if builder.failing?
14
+ message = builder.diagnostics.select(&:error?).join("\n")
15
+ raise Glslkit::ValidationFailed, "glslkit validation failed:\n#{message}"
16
+ end
17
+
18
+ output_path = app.config.assets.output_path
19
+ FileUtils.mkdir_p(output_path)
20
+ File.write(output_path.join(app.config.glslkit.manifest_path), JSON.generate(manifest))
21
+ end
22
+
23
+ desc "Validate all glslkit shaders and exit non-zero on failure (ignores config.glslkit.validate)"
24
+ task check: :environment do
25
+ app = Rails.application
26
+ builder = Glslkit::Rails::ManifestBuilder.new(app)
27
+ builder.build
28
+
29
+ errors = builder.diagnostics.select(&:error?)
30
+ warnings = builder.diagnostics.select(&:warning?)
31
+
32
+ builder.diagnostics.each { |diagnostic| puts diagnostic }
33
+
34
+ # `rake glslkit:check` はconfig.glslkit.validateの設定に関わらず
35
+ # 常に検証結果に基づいて成否を判定する(CI等での明示的な検査用)。
36
+ abort("glslkit: #{errors.size} error(s)") if errors.any?
37
+ abort("glslkit: #{warnings.size} warning(s)") if app.config.glslkit.fail_on_warning && warnings.any?
38
+ end
39
+ end
40
+
41
+ # Propshaft自身のassets:precompile (propshaft/railties/assets.rake) の
42
+ # 後に実行される。そのためRails.application.assets.resolverは、その場での
43
+ # ファイルシステムスキャンではなく、Propshaftが直前に書いた.manifest.json
44
+ # を見て、そこからdigest付きURLを解決する。
45
+ Rake::Task["assets:precompile"].enhance do
46
+ Rake::Task["glslkit:reflect"].invoke
47
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glslkit-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - kyubey1228
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-08-24 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: glslkit
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.0.pre
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: 0.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: 0.1.0.pre
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: 0.2.0
32
+ - !ruby/object:Gem::Dependency
33
+ name: railties
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '7.1'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '7.1'
46
+ - !ruby/object:Gem::Dependency
47
+ name: actionpack
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '7.1'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '7.1'
60
+ - !ruby/object:Gem::Dependency
61
+ name: propshaft
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.3.2
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.3.2
74
+ description: 'Registers .glsl/.vert/.frag assets with Propshaft, resolves #include
75
+ directives at request/precompile time, and publishes a shader reflection manifest
76
+ for consumption by views and JS at runtime.'
77
+ email:
78
+ - kyuuka1228@gmail.com
79
+ executables: []
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - CHANGELOG.md
84
+ - LICENSE.txt
85
+ - README.md
86
+ - config/routes.rb
87
+ - glslkit-rails.gemspec
88
+ - lib/glslkit/rails.rb
89
+ - lib/glslkit/rails/compiler.rb
90
+ - lib/glslkit/rails/helper.rb
91
+ - lib/glslkit/rails/live_reload/engine.rb
92
+ - lib/glslkit/rails/live_reload/known_files.rb
93
+ - lib/glslkit/rails/live_reload/mount.rb
94
+ - lib/glslkit/rails/live_reload/programs_controller.rb
95
+ - lib/glslkit/rails/manifest_builder.rb
96
+ - lib/glslkit/rails/manifest_reader.rb
97
+ - lib/glslkit/rails/railtie.rb
98
+ - lib/glslkit/rails/shader_programs.rb
99
+ - lib/glslkit/rails/version.rb
100
+ - lib/tasks/glslkit.rake
101
+ homepage: https://github.com/kyubey1228/glslkit
102
+ licenses:
103
+ - MIT
104
+ metadata:
105
+ homepage_uri: https://github.com/kyubey1228/glslkit
106
+ source_code_uri: https://github.com/kyubey1228/glslkit/tree/main/rails
107
+ changelog_uri: https://github.com/kyubey1228/glslkit/blob/main/rails/CHANGELOG.md
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '3.1'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubygems_version: 3.6.2
123
+ specification_version: 4
124
+ summary: Rails (Propshaft) integration for glslkit
125
+ test_files: []