isolate_assets 0.2.0 → 0.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -0
- data/README.md +75 -13
- data/features/asset_serving.feature +5 -0
- data/features/step_definitions/view_helper_steps.rb +9 -0
- data/features/view_helpers.feature +5 -0
- data/gemfiles/rails_7_2.gemfile +1 -0
- data/gemfiles/rails_8_0.gemfile +1 -0
- data/gemfiles/rails_8_1.gemfile +1 -0
- data/lib/isolate_assets/assets.rb +72 -17
- data/lib/isolate_assets/engine_extension.rb +11 -17
- data/lib/isolate_assets/helper.rb +32 -16
- data/lib/isolate_assets/version.rb +1 -1
- data/lib/isolate_assets.rb +49 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b7a0b5e25e5067341e5e3c33c79159bbe1b9fe946efd2683a9c7a1dad13f8d9a
|
|
4
|
+
data.tar.gz: 9374de1fe6e919a09f742e8d0b483720f9f115e05423487ef88abd3680e1a02c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98df37ce2970ad2849dced942d34ba434f8e22f4a0590be3fb3b9e693fd557638793f4d88d201b963b2d976d2cefbe7c96c712b67f307c848d7fb89e0c92f325
|
|
7
|
+
data.tar.gz: '08b88c5fd5862e0ac159c714fef265168554a1d25fbafa60f1aef12cf6e28c34f9b13bd67e74a38f71bdc01458f960a64f904f5a4da802e57b0e1390c946f563'
|
data/.github/workflows/ci.yml
CHANGED
data/README.md
CHANGED
|
@@ -54,13 +54,44 @@ my_engine/
|
|
|
54
54
|
stylesheets/
|
|
55
55
|
application.css
|
|
56
56
|
theme.css
|
|
57
|
+
images/
|
|
58
|
+
logo.png
|
|
59
|
+
icons/
|
|
60
|
+
menu.svg
|
|
61
|
+
fonts/
|
|
62
|
+
custom.woff2
|
|
57
63
|
```
|
|
58
64
|
|
|
59
65
|
IsolateAssets automatically excludes your engine's `app/assets/` directory from the host app's asset pipeline (Sprockets/Propshaft), so your assets won't conflict with or be processed by the host application.
|
|
60
66
|
|
|
61
|
-
### 3.
|
|
67
|
+
### 3. Use in your views
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
Call helper methods directly on your engine's namespace:
|
|
70
|
+
|
|
71
|
+
```erb
|
|
72
|
+
<%# Stylesheets %>
|
|
73
|
+
<%= MyEngine.stylesheet_link_tag "application" %>
|
|
74
|
+
|
|
75
|
+
<%# JavaScript %>
|
|
76
|
+
<%= MyEngine.javascript_include_tag "application" %>
|
|
77
|
+
|
|
78
|
+
<%# Images %>
|
|
79
|
+
<%= MyEngine.image_tag "logo.png", alt: "Logo" %>
|
|
80
|
+
<%= MyEngine.image_path "icon.svg" %>
|
|
81
|
+
|
|
82
|
+
<%# Other assets %>
|
|
83
|
+
<%= MyEngine.font_path "custom.woff2" %>
|
|
84
|
+
<%= MyEngine.asset_path "data.json" %>
|
|
85
|
+
|
|
86
|
+
<%# ES6 import maps with CDN dependencies %>
|
|
87
|
+
<%= MyEngine.javascript_importmap_tags "application", {
|
|
88
|
+
"jquery" => "https://cdn.jsdelivr.net/npm/jquery@3.7.1/+esm",
|
|
89
|
+
} %>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Alternative: Include helper for unprefixed access
|
|
93
|
+
|
|
94
|
+
If you prefer `stylesheet_link_tag` over `MyEngine.stylesheet_link_tag`, include the helper in your engine's ApplicationHelper:
|
|
64
95
|
|
|
65
96
|
```ruby
|
|
66
97
|
# app/helpers/my_engine/application_helper.rb
|
|
@@ -71,22 +102,32 @@ module MyEngine
|
|
|
71
102
|
end
|
|
72
103
|
```
|
|
73
104
|
|
|
74
|
-
|
|
105
|
+
Then in views:
|
|
75
106
|
|
|
76
107
|
```erb
|
|
77
|
-
|
|
78
|
-
<%=
|
|
108
|
+
<%= stylesheet_link_tag "application" %>
|
|
109
|
+
<%= image_tag "logo.png", alt: "Logo" %>
|
|
110
|
+
```
|
|
79
111
|
|
|
80
|
-
|
|
81
|
-
<%= engine_javascript_include_tag "application" %>
|
|
112
|
+
Note: This shadows Rails' built-in asset helpers within your engine's views.
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
<%= engine_javascript_importmap_tags "application", {
|
|
85
|
-
"jquery" => "https://cdn.jsdelivr.net/npm/jquery@3.7.1/+esm",
|
|
86
|
-
} %>
|
|
87
|
-
```
|
|
114
|
+
### Available helpers
|
|
88
115
|
|
|
89
|
-
|
|
116
|
+
| Helper | Description |
|
|
117
|
+
|--------|-------------|
|
|
118
|
+
| `stylesheet_link_tag(source, **options)` | `<link>` tag for CSS |
|
|
119
|
+
| `javascript_include_tag(source, **options)` | `<script>` tag for JS |
|
|
120
|
+
| `javascript_importmap_tags(entry_point, imports)` | ES6 import map |
|
|
121
|
+
| `image_tag(source, **options)` | `<img>` tag |
|
|
122
|
+
| `image_path(source)` | URL path for images |
|
|
123
|
+
| `asset_path(source)` | URL path for any asset (infers type from extension) |
|
|
124
|
+
| `font_path(source)` | URL path for fonts |
|
|
125
|
+
| `audio_tag(source, **options)` | `<audio>` tag |
|
|
126
|
+
| `audio_path(source)` | URL path for audio |
|
|
127
|
+
| `video_tag(source, **options)` | `<video>` tag |
|
|
128
|
+
| `video_path(source)` | URL path for video |
|
|
129
|
+
|
|
130
|
+
### Example output
|
|
90
131
|
|
|
91
132
|
```html
|
|
92
133
|
<script type="importmap">
|
|
@@ -113,6 +154,27 @@ This exclusion relies on filtering `config.assets.paths` after engines register
|
|
|
113
154
|
isolate_assets assets_subdir: "isolated_assets" # uses app/isolated_assets/
|
|
114
155
|
```
|
|
115
156
|
|
|
157
|
+
## Non-isolated engines
|
|
158
|
+
|
|
159
|
+
`isolate_assets` assumes `isolate_namespace` and a mounted route set. For an engine that isn't isolated — one that draws its routes directly into the application router — use `IsolateAssets.register` instead, then draw the asset route yourself with whatever path and name you want:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# lib/my_engine.rb
|
|
163
|
+
module MyEngine
|
|
164
|
+
class Engine < ::Rails::Engine; end
|
|
165
|
+
Assets = IsolateAssets.register(namespace: self, engine: Engine, route_name: :my_engine_asset)
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
# config/routes.rb (drawn into the application router)
|
|
171
|
+
Rails.application.routes.draw do
|
|
172
|
+
MyEngine::Assets.draw(self, "/my_engine/assets")
|
|
173
|
+
end
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This gives you the same namespaced helpers as the isolated path — `MyEngine.stylesheet_link_tag "app"`, `MyEngine.javascript_include_tag "app"`, etc. — fingerprinted against your chosen route.
|
|
177
|
+
|
|
116
178
|
## Requirements
|
|
117
179
|
|
|
118
180
|
- Ruby 3.2+
|
|
@@ -15,6 +15,11 @@ Feature: Asset Serving
|
|
|
15
15
|
And the content type should be "text/css"
|
|
16
16
|
And the response should contain "font-family: sans-serif"
|
|
17
17
|
|
|
18
|
+
Scenario: Serving an image file
|
|
19
|
+
When I request "/dummy/assets/logo.png"
|
|
20
|
+
Then I should receive a successful response
|
|
21
|
+
And the content type should be "image/png"
|
|
22
|
+
|
|
18
23
|
Scenario: Asset fingerprinting
|
|
19
24
|
When I request "/dummy/assets/application.js"
|
|
20
25
|
Then the response should have caching headers
|
|
@@ -22,3 +22,12 @@ Then("the import map should include {string}") do |key|
|
|
|
22
22
|
import_map = JSON.parse(script.text(:all))
|
|
23
23
|
expect(import_map["imports"]).to have_key(key)
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
Then("the page should have an image tag with src starting with {string}") do |path|
|
|
27
|
+
expect(page).to have_css("img[src^='#{path}']", visible: false)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Then("the image tag should include a fingerprint parameter") do
|
|
31
|
+
img = page.find("img", visible: false)
|
|
32
|
+
expect(img[:src]).to match(/\?v=[a-f0-9]{8}/)
|
|
33
|
+
end
|
|
@@ -12,3 +12,8 @@ Feature: View Helpers
|
|
|
12
12
|
When I visit the dummy engine root
|
|
13
13
|
Then the page should have an import map
|
|
14
14
|
And the import map should include "dummy/application"
|
|
15
|
+
|
|
16
|
+
Scenario: Image tag includes fingerprint
|
|
17
|
+
When I visit the dummy engine root
|
|
18
|
+
Then the page should have an image tag with src starting with "/dummy/assets/logo.png"
|
|
19
|
+
And the image tag should include a fingerprint parameter
|
data/gemfiles/rails_7_2.gemfile
CHANGED
data/gemfiles/rails_8_0.gemfile
CHANGED
data/gemfiles/rails_8_1.gemfile
CHANGED
|
@@ -2,26 +2,88 @@
|
|
|
2
2
|
|
|
3
3
|
module IsolateAssets
|
|
4
4
|
class Assets
|
|
5
|
-
attr_reader :engine, :assets_subdir
|
|
5
|
+
attr_reader :engine, :assets_subdir, :route_name
|
|
6
|
+
attr_accessor :controller
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
ASSET_DIRECTORIES = {
|
|
9
|
+
"js" => "javascripts",
|
|
10
|
+
"javascript" => "javascripts",
|
|
11
|
+
"css" => "stylesheets",
|
|
12
|
+
"stylesheet" => "stylesheets",
|
|
13
|
+
"png" => "images",
|
|
14
|
+
"jpg" => "images",
|
|
15
|
+
"jpeg" => "images",
|
|
16
|
+
"gif" => "images",
|
|
17
|
+
"svg" => "images",
|
|
18
|
+
"webp" => "images",
|
|
19
|
+
"ico" => "images",
|
|
20
|
+
"woff" => "fonts",
|
|
21
|
+
"woff2" => "fonts",
|
|
22
|
+
"ttf" => "fonts",
|
|
23
|
+
"otf" => "fonts",
|
|
24
|
+
"eot" => "fonts",
|
|
25
|
+
"mp3" => "audio",
|
|
26
|
+
"ogg" => "audio",
|
|
27
|
+
"wav" => "audio",
|
|
28
|
+
"mp4" => "video",
|
|
29
|
+
"webm" => "video",
|
|
30
|
+
"ogv" => "video"
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
CONTENT_TYPES = {
|
|
34
|
+
"js" => "application/javascript",
|
|
35
|
+
"javascript" => "application/javascript",
|
|
36
|
+
"css" => "text/css",
|
|
37
|
+
"stylesheet" => "text/css",
|
|
38
|
+
"png" => "image/png",
|
|
39
|
+
"jpg" => "image/jpeg",
|
|
40
|
+
"jpeg" => "image/jpeg",
|
|
41
|
+
"gif" => "image/gif",
|
|
42
|
+
"svg" => "image/svg+xml",
|
|
43
|
+
"webp" => "image/webp",
|
|
44
|
+
"ico" => "image/x-icon",
|
|
45
|
+
"woff" => "font/woff",
|
|
46
|
+
"woff2" => "font/woff2",
|
|
47
|
+
"ttf" => "font/ttf",
|
|
48
|
+
"otf" => "font/otf",
|
|
49
|
+
"eot" => "application/vnd.ms-fontobject",
|
|
50
|
+
"mp3" => "audio/mpeg",
|
|
51
|
+
"ogg" => "audio/ogg",
|
|
52
|
+
"wav" => "audio/wav",
|
|
53
|
+
"mp4" => "video/mp4",
|
|
54
|
+
"webm" => "video/webm",
|
|
55
|
+
"ogv" => "video/ogg"
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
def initialize(engine:, assets_subdir: "assets", route_name: :isolated_asset, url_helpers: nil)
|
|
8
59
|
@engine = engine
|
|
9
60
|
@assets_subdir = assets_subdir
|
|
61
|
+
@route_name = route_name
|
|
62
|
+
@url_helpers = url_helpers || -> { engine.routes.url_helpers }
|
|
10
63
|
@fingerprints = {}
|
|
11
64
|
end
|
|
12
65
|
|
|
66
|
+
# Draws the catch-all asset route into the given router (the application
|
|
67
|
+
# router for non-isolated engines, the engine's own for isolated ones).
|
|
68
|
+
def draw(mapper, path)
|
|
69
|
+
mapper.get "#{path}/*file", to: controller.action(:show), as: route_name
|
|
70
|
+
end
|
|
71
|
+
|
|
13
72
|
def asset_path(source, type)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
73
|
+
directory = ASSET_DIRECTORIES[type.to_s]
|
|
74
|
+
return nil unless directory
|
|
75
|
+
|
|
76
|
+
normalized = normalize_type(type)
|
|
77
|
+
engine.root.join("app/#{assets_subdir}/#{directory}", "#{source}.#{normalized}")
|
|
20
78
|
end
|
|
21
79
|
|
|
22
80
|
def asset_url(source, type)
|
|
23
81
|
fingerprint_value = fingerprint(source, type)
|
|
24
|
-
|
|
82
|
+
@url_helpers.call.public_send(
|
|
83
|
+
"#{route_name}_path",
|
|
84
|
+
"#{source}.#{normalize_type(type)}",
|
|
85
|
+
v: fingerprint_value,
|
|
86
|
+
)
|
|
25
87
|
end
|
|
26
88
|
|
|
27
89
|
def fingerprint(source, type)
|
|
@@ -35,14 +97,7 @@ module IsolateAssets
|
|
|
35
97
|
end
|
|
36
98
|
|
|
37
99
|
def content_type(type)
|
|
38
|
-
|
|
39
|
-
when "js", "javascript"
|
|
40
|
-
"application/javascript"
|
|
41
|
-
when "css", "stylesheet"
|
|
42
|
-
"text/css"
|
|
43
|
-
else
|
|
44
|
-
"application/octet-stream"
|
|
45
|
-
end
|
|
100
|
+
CONTENT_TYPES[type.to_s] || "application/octet-stream"
|
|
46
101
|
end
|
|
47
102
|
|
|
48
103
|
def javascript_files
|
|
@@ -5,8 +5,10 @@ module IsolateAssets
|
|
|
5
5
|
def isolate_assets(assets_subdir: "assets")
|
|
6
6
|
engine_class = self
|
|
7
7
|
|
|
8
|
-
# Exclude engine assets from host's asset pipeline
|
|
8
|
+
# Exclude engine assets from host's asset pipeline (if one exists)
|
|
9
9
|
initializer "#{engine_name}.isolate_assets.exclude_from_pipeline", before: :load_config_initializers do |app|
|
|
10
|
+
next unless app.config.respond_to?(:assets) && app.config.assets
|
|
11
|
+
|
|
10
12
|
asset_base = engine_class.root.join("app", assets_subdir)
|
|
11
13
|
app.config.assets.excluded_paths ||= []
|
|
12
14
|
if asset_base.exist?
|
|
@@ -18,29 +20,21 @@ module IsolateAssets
|
|
|
18
20
|
|
|
19
21
|
# Sprockets doesn't respect excluded_paths, so filter manually
|
|
20
22
|
initializer "#{engine_name}.isolate_assets.filter_asset_paths", after: :load_config_initializers do |app|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
next unless app.config.respond_to?(:assets) && app.config.assets
|
|
24
|
+
next unless app.config.assets.excluded_paths.present?
|
|
25
|
+
|
|
26
|
+
excluded = app.config.assets.excluded_paths.map(&:to_s)
|
|
27
|
+
app.config.assets.paths = app.config.assets.paths.reject do |path|
|
|
28
|
+
excluded.include?(path.to_s)
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
initializer "#{engine_name}.isolate_assets", before: :set_routes_reloader do
|
|
30
33
|
assets = IsolateAssets::Assets.new(engine: engine_class, assets_subdir: assets_subdir)
|
|
34
|
+
controller_class = IsolateAssets.build_controller(assets)
|
|
31
35
|
|
|
32
|
-
# Subclass the controller so that multiple engines using this gem get their own controller
|
|
33
|
-
controller_class = Class.new(IsolateAssets::Controller)
|
|
34
|
-
controller_class.isolated_assets = assets
|
|
35
|
-
|
|
36
|
-
# Hack in the helpers. There's gotta be a better way than this...
|
|
37
|
-
helper_module = Module.new do
|
|
38
|
-
define_method(:isolated_assets) { assets }
|
|
39
|
-
include IsolateAssets::Helper
|
|
40
|
-
end
|
|
41
36
|
if engine_class.respond_to?(:railtie_namespace) && engine_class.railtie_namespace
|
|
42
|
-
engine_class.railtie_namespace
|
|
43
|
-
engine_class.railtie_namespace.singleton_class.define_method(:isolated_assets_helper) { helper_module }
|
|
37
|
+
IsolateAssets.expose_helpers(engine_class.railtie_namespace, assets)
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
engine_class.routes.prepend do
|
|
@@ -2,40 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
module IsolateAssets
|
|
4
4
|
module Helper
|
|
5
|
+
include ActionView::Helpers::TagHelper
|
|
6
|
+
include ActionView::Helpers::AssetTagHelper
|
|
7
|
+
|
|
5
8
|
# Note: isolated_assets method is defined by the including module,
|
|
6
9
|
# created dynamically in EngineExtension#isolate_assets
|
|
7
10
|
|
|
8
|
-
def
|
|
9
|
-
fingerprint_value = isolated_assets.fingerprint(source, type)
|
|
10
|
-
normalized_type = case type.to_s
|
|
11
|
-
when "javascript" then "js"
|
|
12
|
-
when "stylesheet" then "css"
|
|
13
|
-
else type.to_s
|
|
14
|
-
end
|
|
15
|
-
isolated_asset_path("#{source}.#{normalized_type}", v: fingerprint_value)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def engine_stylesheet_link_tag(source, **options)
|
|
11
|
+
def stylesheet_link_tag(source, **options)
|
|
19
12
|
tag.link(
|
|
20
13
|
rel: "stylesheet",
|
|
21
|
-
href:
|
|
14
|
+
href: isolated_assets.asset_url(source, "css"),
|
|
22
15
|
**options
|
|
23
16
|
)
|
|
24
17
|
end
|
|
25
18
|
|
|
26
|
-
def
|
|
19
|
+
def javascript_include_tag(source, **options)
|
|
27
20
|
tag.script(
|
|
28
|
-
src:
|
|
21
|
+
src: isolated_assets.asset_url(source, "js"),
|
|
29
22
|
**options
|
|
30
23
|
)
|
|
31
24
|
end
|
|
32
25
|
|
|
33
|
-
def
|
|
26
|
+
def javascript_importmap_tags(entry_point = "application", imports = {})
|
|
34
27
|
assets_root = isolated_assets.engine.root.join("app/#{isolated_assets.assets_subdir}/javascripts")
|
|
35
28
|
engine_imports = isolated_assets.javascript_files.each_with_object({}) do |path, hash|
|
|
36
29
|
relative_path = path.relative_path_from(assets_root).to_s
|
|
37
30
|
key = "#{isolated_assets.engine.engine_name}/#{relative_path.sub(/\.js\z/, "")}"
|
|
38
|
-
hash[key] =
|
|
31
|
+
hash[key] = isolated_assets.asset_url(relative_path.sub(/\.js\z/, ""), "js")
|
|
39
32
|
end
|
|
40
33
|
[
|
|
41
34
|
tag.script(type: "importmap") do
|
|
@@ -46,5 +39,28 @@ module IsolateAssets
|
|
|
46
39
|
JS
|
|
47
40
|
].join("\n").html_safe
|
|
48
41
|
end
|
|
42
|
+
|
|
43
|
+
def asset_path(source)
|
|
44
|
+
ext = File.extname(source).delete_prefix(".")
|
|
45
|
+
source_without_ext = source.sub(/\.#{Regexp.escape(ext)}\z/, "")
|
|
46
|
+
isolated_assets.asset_url(source_without_ext, ext)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
alias_method :image_path, :asset_path
|
|
50
|
+
alias_method :font_path, :asset_path
|
|
51
|
+
alias_method :audio_path, :asset_path
|
|
52
|
+
alias_method :video_path, :asset_path
|
|
53
|
+
|
|
54
|
+
def image_tag(source, **options)
|
|
55
|
+
tag.img(src: image_path(source), **options)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def audio_tag(source, **options)
|
|
59
|
+
tag.audio(src: audio_path(source), **options)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def video_tag(source, **options)
|
|
63
|
+
tag.video(src: video_path(source), **options)
|
|
64
|
+
end
|
|
49
65
|
end
|
|
50
66
|
end
|
data/lib/isolate_assets.rb
CHANGED
|
@@ -9,6 +9,55 @@ module IsolateAssets
|
|
|
9
9
|
autoload :Controller, "isolate_assets/controller"
|
|
10
10
|
autoload :Helper, "isolate_assets/helper"
|
|
11
11
|
autoload :EngineExtension, "isolate_assets/engine_extension"
|
|
12
|
+
|
|
13
|
+
HELPER_METHODS = %i[
|
|
14
|
+
stylesheet_link_tag javascript_include_tag javascript_importmap_tags
|
|
15
|
+
asset_path image_path image_tag font_path audio_path audio_tag video_path video_tag
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
# Wires up an engine that draws its asset route into the application router
|
|
19
|
+
# rather than mounting an isolated route set. Returns the Assets handle; draw
|
|
20
|
+
# the route from the engine's routes file with `assets.draw(self, path)`.
|
|
21
|
+
def self.register(namespace:, engine:, route_name:, assets_subdir: "assets")
|
|
22
|
+
assets = Assets.new(
|
|
23
|
+
engine: engine,
|
|
24
|
+
assets_subdir: assets_subdir,
|
|
25
|
+
route_name: route_name,
|
|
26
|
+
url_helpers: -> { Rails.application.routes.url_helpers },
|
|
27
|
+
)
|
|
28
|
+
build_controller(assets)
|
|
29
|
+
expose_helpers(namespace, assets)
|
|
30
|
+
assets
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.build_controller(assets)
|
|
34
|
+
controller = Class.new(Controller)
|
|
35
|
+
controller.isolated_assets = assets
|
|
36
|
+
assets.controller = controller
|
|
37
|
+
controller
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Defines isolated_assets + the asset tag helpers (stylesheet_link_tag, etc.)
|
|
41
|
+
# as singleton methods on the given module, e.g. Dummy.stylesheet_link_tag.
|
|
42
|
+
def self.expose_helpers(namespace, assets)
|
|
43
|
+
helper_module = Module.new do
|
|
44
|
+
define_method(:isolated_assets) { assets }
|
|
45
|
+
include Helper
|
|
46
|
+
end
|
|
47
|
+
namespace.singleton_class.define_method(:isolated_assets) { assets }
|
|
48
|
+
namespace.singleton_class.define_method(:isolated_assets_helper) { helper_module }
|
|
49
|
+
|
|
50
|
+
helper_context = Class.new do
|
|
51
|
+
include Helper
|
|
52
|
+
define_method(:isolated_assets) { assets }
|
|
53
|
+
end.new
|
|
54
|
+
HELPER_METHODS.each do |method_name|
|
|
55
|
+
namespace.singleton_class.define_method(method_name) do |*args, **kwargs, &block|
|
|
56
|
+
helper_context.send(method_name, *args, **kwargs, &block)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
helper_module
|
|
60
|
+
end
|
|
12
61
|
end
|
|
13
62
|
|
|
14
63
|
# Extend Rails::Engine with isolate_assets
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: isolate_assets
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: railties
|