asset_ram 0.2.0 → 1.1.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/CHANGELOG.md +6 -1
- data/Gemfile.lock +35 -0
- data/README.md +67 -48
- data/lib/asset_ram/version.rb +1 -1
- data/lib/asset_ram.rb +26 -9
- data/test-data.png +0 -0
- metadata +10 -13
- data/asset_ram.gemspec +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e51caa4c493e188fe6522e16c6473c7ff3c723a8afb5926ea85353ad30e675ab
|
4
|
+
data.tar.gz: 66c580f13e0eea34e2e27e25b38ebd1c567e37e604999247dec453418ecaeacb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3c0b278d8a39ca23a0cd87cc640793572309f15c61c22eea267cec3ef40b77899c883d35947ac00589d645a5e808c9836861c9f760b3f8badb9e8c4a5902b77
|
7
|
+
data.tar.gz: 99dd95d7025ea7159c867559b21bcf05051cfc15449cffb1a91d70f71252901b8c6589218d0de6f922b1731260eccb9b83a3dc749e5f39884cedd67a6d2cf886
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
## [
|
1
|
+
## [1.1.0] - 2025-06-12
|
2
|
+
|
3
|
+
- Added a new, simpler API: `AssetRam.cache { ... }` as the preferred way to cache asset computations in views. The legacy `AssetRam::Helper.cache` API is still supported for compatibility.
|
4
|
+
- Improved documentation to highlight the new API and update usage examples.
|
5
|
+
- Added comprehensive RSpec tests for caching behavior, keying, and environment variable handling.
|
6
|
+
|
2
7
|
|
3
8
|
## [0.1.0] - 2021-09-25
|
4
9
|
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
asset_ram (1.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.6.2)
|
10
|
+
rake (13.3.0)
|
11
|
+
rspec (3.13.1)
|
12
|
+
rspec-core (~> 3.13.0)
|
13
|
+
rspec-expectations (~> 3.13.0)
|
14
|
+
rspec-mocks (~> 3.13.0)
|
15
|
+
rspec-core (3.13.4)
|
16
|
+
rspec-support (~> 3.13.0)
|
17
|
+
rspec-expectations (3.13.5)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.13.0)
|
20
|
+
rspec-mocks (3.13.5)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.13.0)
|
23
|
+
rspec-support (3.13.4)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
arm64-darwin-24
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
asset_ram!
|
31
|
+
rake (~> 13.0)
|
32
|
+
rspec (~> 3.0)
|
33
|
+
|
34
|
+
BUNDLED WITH
|
35
|
+
2.6.9
|
data/README.md
CHANGED
@@ -1,25 +1,40 @@
|
|
1
1
|
# AssetRam
|
2
2
|
|
3
|
-
|
3
|
+
**Rails 7 update: I measured a 35% reduction in allocations.** I tested
|
4
|
+
[my app's home page](https://www.public.law) in production:
|
4
5
|
|
5
|
-
*
|
6
|
-
*
|
6
|
+
* Rails 7.0.6
|
7
|
+
* Ruby 3.2.2
|
8
|
+
* Sprockets v3 and v4
|
9
|
+
* JE_MALLOC
|
10
|
+
* `--enable-yjit`
|
7
11
|
|
8
|
-
|
12
|
+
The page is simple with only five images. If you have more, you'll get
|
13
|
+
an even bigger boost:
|
9
14
|
|
15
|
+
<img src="https://github.com/dogweather/asset_ram/raw/master/test-data.png" alt="Test Data" style="width: 70%;">
|
10
16
|
|
11
|
-
|
17
|
+
The savings come from avoiding asset calculations. The app is faster, too.
|
18
|
+
But it's hard for me to measure precisely: enabling AssetRam, this page time
|
19
|
+
goes down from ~9ms to ~6ms.
|
12
20
|
|
13
|
-
Without AssetRam:
|
14
21
|
|
15
|
-
|
16
|
-
|
22
|
+
> [!TIP]
|
23
|
+
> Set env var `ASSET_RAM_DISABLE` to do this comparison in your own app.
|
24
|
+
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
gem 'asset_ram'
|
17
32
|
```
|
18
33
|
|
19
|
-
|
34
|
+
And then execute:
|
20
35
|
|
21
36
|
```
|
22
|
-
|
37
|
+
$ bundle install
|
23
38
|
```
|
24
39
|
|
25
40
|
|
@@ -27,50 +42,70 @@ Completed 200 OK in 11ms (Views: 5.1ms | ActiveRecord: 1.3ms | Allocations: 5208
|
|
27
42
|
|
28
43
|
Wrap every asset helper call with `#cache`, like this:
|
29
44
|
|
45
|
+
|
30
46
|
### Before
|
31
47
|
|
32
48
|
```ruby
|
33
49
|
<%= favicon_link_tag('favicon/favicon.ico', rel: 'icon') %>
|
50
|
+
# ...
|
51
|
+
<%= javascript_include_tag('application.js') %>
|
34
52
|
```
|
35
53
|
|
36
|
-
|
54
|
+
|
55
|
+
### After (Preferred)
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
<%= AssetRam.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') } %>
|
59
|
+
# ...
|
60
|
+
<%= AssetRam.cache { javascript_include_tag('application.js') } %>
|
61
|
+
```
|
62
|
+
|
63
|
+
Or, for compatibility, you can still use:
|
37
64
|
|
38
65
|
```ruby
|
39
66
|
<%= AssetRam::Helper.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') } %>
|
40
67
|
```
|
41
68
|
|
42
|
-
After booting up,
|
43
|
-
full cache key.
|
69
|
+
After booting up, AssetRam sends a message like this _once_ to the log for each usage:
|
44
70
|
|
45
71
|
```
|
46
|
-
Caching ["/
|
47
|
-
Caching ["/Users/robb/src/PublicLaw/public-law-website/app/views/application/_favicon.haml", 8]
|
48
|
-
Caching ["/Users/robb/src/PublicLaw/public-law-website/app/views/application/_favicon.haml", 11]
|
72
|
+
Caching ["/website/app/views/application/_favicon.haml", 8]
|
49
73
|
```
|
50
74
|
|
51
|
-
|
75
|
+
It outputs this when the asset link is generated. It shows the full cache key (filename
|
76
|
+
and line number)
|
77
|
+
so we can see what it's caching. This is the line of code that, without AssetRam,
|
78
|
+
would be exectued on every request.
|
79
|
+
|
80
|
+
|
81
|
+
I use it in my footer for social icons as well. I **used to** have this: (HAML syntax)
|
52
82
|
|
53
83
|
```ruby
|
54
|
-
= link_to
|
55
|
-
= link_to
|
56
|
-
= link_to
|
57
|
-
= link_to
|
84
|
+
= link_to AssetRam.cache { image_tag("social/instagram-logo.svg", alt: 'Instagram', loading: 'lazy', decoding: 'async') }, "https://www.instagram.com/law.is.code/"
|
85
|
+
= link_to AssetRam.cache { image_tag("social/facebook-logo-button.svg", alt: 'Facebook', loading: 'lazy', decoding: 'async') }, "https://www.facebook.com/PublicDotLaw"
|
86
|
+
= link_to AssetRam.cache { image_tag("social/twitter-logo-button.svg", alt: 'Twitter', loading: 'lazy', decoding: 'async') }, "https://twitter.com/law_is_code"
|
87
|
+
= link_to AssetRam.cache { image_tag("social/github-logo.svg", alt: 'Our GitHub Page', loading: 'lazy', decoding: 'async') }, "https://www.github.com/public-law/"
|
58
88
|
```
|
59
89
|
|
60
90
|
|
91
|
+
But my whole footer partial is static. So now I just do this instead in my layout:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
= AssetRam.cache { render 'footer_for_screen' }
|
95
|
+
```
|
61
96
|
|
62
97
|
|
63
98
|
### In some cases, the cache key can't be inferred.
|
64
99
|
|
65
|
-
|
100
|
+
AssetRam creates the cache key automatically using the view's source filename and line number.
|
66
101
|
This works for most uses.
|
67
102
|
|
68
|
-
Some of my app's views are an exception, however. It's multi-tenant and the views serve content
|
69
|
-
for
|
70
|
-
In my HTML
|
103
|
+
Some of my app's views are an exception, however. It's **multi-tenant** and the views serve content
|
104
|
+
for many sub-domains. To handle this, the call to `#cache` allows extra key info to be passed.
|
105
|
+
In my HTML `head` view, I already had a `site` variable for choosing the CSS file for the domain. So I reuse that as extra cache key info:
|
71
106
|
|
72
107
|
```ruby
|
73
|
-
<%= AssetRam
|
108
|
+
<%= AssetRam.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) } %>
|
74
109
|
```
|
75
110
|
|
76
111
|
## Background: I was looking for ways to reduce allocations in my Rails app
|
@@ -78,10 +113,13 @@ In my HTML HEAD view, I have a `site` variable for choosing the CSS file for the
|
|
78
113
|
In an effort to help my app run in a small 512MB virtual server, I looked through every view
|
79
114
|
invocation in the logs. After I optimized a bunch of my code, I realized that the asset helpers
|
80
115
|
create a relatively large amount of objects. The code is pretty complex too implying some amount
|
81
|
-
of CPU overhead. Moreover, this work is
|
116
|
+
of CPU overhead. Moreover, this work is **repeated on every request**.
|
117
|
+
|
118
|
+
These asset fingerprints are potentially re-generated on every deploy. Maybe I edit an image, but
|
119
|
+
I haven't modified any ActiveRecord models. This means that **the asset links cannot be stored in
|
120
|
+
the standard Rails cache.** (If the Rails cache had a lifetime option of, "until next boot", that would solve the problem.)
|
82
121
|
|
83
|
-
|
84
|
-
the usual Rails cache. I realized that storing the computed paths in a simple hash (in RAM only)
|
122
|
+
I realized that storing the computed paths in a simple hash (in RAM only)
|
85
123
|
would be fast and never return stale data: The RAM cache goes away on a deploy/restart, which is
|
86
124
|
when asset fingerprints could change.
|
87
125
|
|
@@ -97,29 +135,10 @@ To make it as easy as possible to use, the lib finds the view's source filename
|
|
97
135
|
the code being cached. This has been working well and in production for four months in a large Rails app.
|
98
136
|
|
99
137
|
|
100
|
-
|
101
|
-
## Installation
|
102
|
-
|
103
|
-
Add this line to your application's Gemfile:
|
104
|
-
|
105
|
-
```ruby
|
106
|
-
gem 'asset_ram'
|
107
|
-
```
|
108
|
-
|
109
|
-
And then execute:
|
110
|
-
|
111
|
-
$ bundle install
|
112
|
-
|
113
|
-
Or install it yourself as:
|
114
|
-
|
115
|
-
$ gem install asset_ram
|
116
|
-
|
117
|
-
|
118
138
|
## Development
|
119
139
|
|
120
140
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
121
141
|
|
122
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
123
142
|
|
124
143
|
## License
|
125
144
|
|
data/lib/asset_ram/version.rb
CHANGED
data/lib/asset_ram.rb
CHANGED
@@ -5,9 +5,9 @@ require_relative "asset_ram/version"
|
|
5
5
|
#
|
6
6
|
# Use in views to cache the asset path computation.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# Preferred usage (since v1.1.0):
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# = AssetRam.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') }
|
11
11
|
#
|
12
12
|
# The calculated asset paths are keyed by source file name and line number.
|
13
13
|
# The results are stored in RAM.
|
@@ -15,7 +15,11 @@ require_relative "asset_ram/version"
|
|
15
15
|
# Sometimes, a key is needed if the code is run in different contexts, like
|
16
16
|
# a multi-tenant site:
|
17
17
|
#
|
18
|
-
#
|
18
|
+
# = AssetRam.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) }
|
19
|
+
#
|
20
|
+
# For compatibility, you can still use:
|
21
|
+
#
|
22
|
+
# = AssetRam::Helper.cache { ... }
|
19
23
|
#
|
20
24
|
# To test and compare if this lib actually improves performance,
|
21
25
|
# set the ASSET_RAM_DISABLE env var and it will transparently never cache.
|
@@ -24,21 +28,34 @@ require_relative "asset_ram/version"
|
|
24
28
|
module AssetRam
|
25
29
|
class Error < StandardError; end
|
26
30
|
|
31
|
+
##
|
32
|
+
# The simpler API: AssetRam.cache { ... }
|
33
|
+
#
|
34
|
+
def self.cache(key: '', &blk)
|
35
|
+
Helper.cache(key: key, &blk)
|
36
|
+
end
|
37
|
+
|
27
38
|
##
|
28
39
|
# Our own asset helper which memoizes Rails' asset helper calls.
|
40
|
+
#
|
29
41
|
class Helper
|
30
|
-
|
31
42
|
@@_cache = {}
|
32
43
|
|
33
|
-
def self.cache(key: '', &blk)
|
34
|
-
return yield if ENV['ASSET_RAM_DISABLE']
|
35
44
|
|
45
|
+
def self.cache(key: '', &blk)
|
36
46
|
cache_key = blk.source_location
|
37
|
-
cache_key << key if key.
|
47
|
+
cache_key << key if !key.to_s.empty?
|
48
|
+
|
49
|
+
cache_by_key(cache_key, &blk)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def self.cache_by_key(cache_key, &blk)
|
54
|
+
return yield if ENV['ASSET_RAM_DISABLE']
|
38
55
|
|
39
56
|
if !@@_cache.has_key?(cache_key)
|
40
|
-
# Using WARN level because it should only
|
41
|
-
# once during any Rails run. If
|
57
|
+
# Using WARN level because it should only output
|
58
|
+
# once during any Rails run. If it's output multiple
|
42
59
|
# times, then caching isn't working correctly.
|
43
60
|
Rails.logger.warn("Caching #{cache_key}")
|
44
61
|
@@_cache[cache_key] = yield
|
data/test-data.png
ADDED
Binary file
|
metadata
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asset_ram
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- Robert Shecter
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
|
-
description:
|
14
12
|
email:
|
15
|
-
-
|
13
|
+
- robert@public.law
|
16
14
|
executables: []
|
17
15
|
extensions: []
|
18
16
|
extra_rdoc_files: []
|
@@ -20,21 +18,21 @@ files:
|
|
20
18
|
- ".rspec"
|
21
19
|
- CHANGELOG.md
|
22
20
|
- Gemfile
|
21
|
+
- Gemfile.lock
|
23
22
|
- LICENSE.txt
|
24
23
|
- README.md
|
25
24
|
- Rakefile
|
26
|
-
- asset_ram.gemspec
|
27
25
|
- bin/console
|
28
26
|
- bin/setup
|
29
27
|
- lib/asset_ram.rb
|
30
28
|
- lib/asset_ram/version.rb
|
31
|
-
|
29
|
+
- test-data.png
|
30
|
+
homepage: https://github.com/public-law/asset_ram
|
32
31
|
licenses:
|
33
32
|
- MIT
|
34
33
|
metadata:
|
35
|
-
homepage_uri: https://github.com/
|
36
|
-
source_code_uri: https://github.com/
|
37
|
-
post_install_message:
|
34
|
+
homepage_uri: https://github.com/public-law/asset_ram
|
35
|
+
source_code_uri: https://github.com/public-law/asset_ram
|
38
36
|
rdoc_options: []
|
39
37
|
require_paths:
|
40
38
|
- lib
|
@@ -49,8 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
47
|
- !ruby/object:Gem::Version
|
50
48
|
version: '0'
|
51
49
|
requirements: []
|
52
|
-
rubygems_version: 3.
|
53
|
-
signing_key:
|
50
|
+
rubygems_version: 3.6.9
|
54
51
|
specification_version: 4
|
55
52
|
summary: Improves Rails performance by caching asset path calculations
|
56
53
|
test_files: []
|
data/asset_ram.gemspec
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/asset_ram/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "asset_ram"
|
7
|
-
spec.version = AssetRam::VERSION
|
8
|
-
spec.authors = ["Robb Shecter"]
|
9
|
-
spec.email = ["robb@public.law"]
|
10
|
-
|
11
|
-
spec.summary = "Improves Rails performance by caching asset path calculations"
|
12
|
-
spec.homepage = "https://github.com/dogweather/asset_ram"
|
13
|
-
spec.license = "MIT"
|
14
|
-
spec.required_ruby_version = ">= 2.4.0"
|
15
|
-
|
16
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
-
spec.metadata["source_code_uri"] = "https://github.com/dogweather/asset_ram"
|
18
|
-
|
19
|
-
# Specify which files should be added to the gem when it is released.
|
20
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
24
|
-
end
|
25
|
-
end
|
26
|
-
spec.bindir = "exe"
|
27
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
-
spec.require_paths = ["lib"]
|
29
|
-
|
30
|
-
# Uncomment to register a new dependency of your gem
|
31
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
32
|
-
|
33
|
-
# For more information and examples about making a new gem, checkout our
|
34
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
35
|
-
end
|