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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05464224fe91ef9aeb8784b87a9331802f9f1625f13e75969f672dccd4548dcd
4
- data.tar.gz: 1c5c4845548975b31204a71da7cdd8410b69c4806b191adc8c2047669a6cfd56
3
+ metadata.gz: e51caa4c493e188fe6522e16c6473c7ff3c723a8afb5926ea85353ad30e675ab
4
+ data.tar.gz: 66c580f13e0eea34e2e27e25b38ebd1c567e37e604999247dec453418ecaeacb
5
5
  SHA512:
6
- metadata.gz: 553e142c91b2dc4a5d4f1ff379524c65be3400d17498bba2cd549f81d4970077f037e4d329a3ec1d327e67e18d39f61b0ae946bf3e7e053c4f15ecef1c38f384
7
- data.tar.gz: cd10e4447bdbaeeb29d795f7862b33dea15320a5fc240a165850785be2d53da04da1d96979eb628edfecb01bd5e692f26c6dacc10230083a0e1401a83f4970a4
6
+ metadata.gz: b3c0b278d8a39ca23a0cd87cc640793572309f15c61c22eea267cec3ef40b77899c883d35947ac00589d645a5e808c9836861c9f760b3f8badb9e8c4a5902b77
7
+ data.tar.gz: 99dd95d7025ea7159c867559b21bcf05051cfc15449cffb1a91d70f71252901b8c6589218d0de6f922b1731260eccb9b83a3dc749e5f39884cedd67a6d2cf886
data/CHANGELOG.md CHANGED
@@ -1,4 +1,9 @@
1
- ## [Unreleased]
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
- In a nutshell, for [a typical dynamic page](https://texas.public.law/statutes/tex._fam._code_section_1.001) in my Rails app, I get:
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
- * 71% reduction in execution time
6
- * 83% reduction in allocations
6
+ * Rails 7.0.6
7
+ * Ruby 3.2.2
8
+ * Sprockets v3 and v4
9
+ * JE_MALLOC
10
+ * `--enable-yjit`
7
11
 
8
- My app has been amazingly efficient since using these: memory usage stays flat at just 50% usage (512MB) for 4 Puma workers. It's very quick, with production response times averaging 37ms on a Heroku 1GB Standard-2x Dyno.
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
- ## Example stats for [the page](https://texas.public.law/statutes/tex._fam._code_section_1.001)
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
- Completed 200 OK in 38ms (Views: 34.2ms | ActiveRecord: 0.9ms | Allocations: 30332)
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
- With AssetRam:
34
+ And then execute:
20
35
 
21
36
  ```
22
- Completed 200 OK in 11ms (Views: 5.1ms | ActiveRecord: 1.3ms | Allocations: 5208)
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
- ### After
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, messages like these will appear _once_ in the log. They show the
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 ["/Users/robb/src/PublicLaw/public-law-website/app/views/application/_html_head.haml", 16, "texas"]
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
- I use it in my footer for social icons as well: (HAML syntax)
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 asset.cache { image_tag("social/instagram-logo.svg", alt: 'Instagram', loading: 'lazy', decoding: 'async') }, "https://www.instagram.com/law.is.code/"
55
- = link_to asset.cache { image_tag("social/facebook-logo-button.svg", alt: 'Facebook', loading: 'lazy', decoding: 'async') }, "https://www.facebook.com/PublicDotLaw"
56
- = link_to asset.cache { image_tag("social/twitter-logo-button.svg", alt: 'Twitter', loading: 'lazy', decoding: 'async') }, "https://twitter.com/law_is_code"
57
- = link_to asset.cache { image_tag("social/github-logo.svg", alt: 'Our GitHub Page', loading: 'lazy', decoding: 'async') }, "https://www.github.com/public-law/"
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
- RamCache creates the cache key automatically using the view source filename and line number.
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 several sub-domains. And so the call to `#cache` allows extra key info to be passed.
70
- In my HTML HEAD view, I have a `site` variable for choosing the CSS file for the domain:
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::Helper.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) } %>
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 done over **on every request**.
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
- These asset fingerprints are potentially re-generated on every deploy. So they can't be stored in
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AssetRam
4
- VERSION = "0.2.0"
4
+ VERSION = "1.1.0"
5
5
  end
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
- # For example, in a HAML file:
8
+ # Preferred usage (since v1.1.0):
9
9
  #
10
- # = AssetRam::Helper.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') }
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
- # = AssetRam::Helper.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) }
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.present?
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 be output
41
- # once during any Rails run. If its output multiple
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: 0.2.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Robb Shecter
8
- autorequire:
7
+ - Robert Shecter
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2021-09-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
- - robb@public.law
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
- homepage: https://github.com/dogweather/asset_ram
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/dogweather/asset_ram
36
- source_code_uri: https://github.com/dogweather/asset_ram
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.2.28
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