asset_ram 0.1.0 → 0.2.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: 201c96bf0cd23662ec2b7b3f68ebc17935a38a2ade97c1d3c025e7efe1492f28
4
- data.tar.gz: 0640c99917e1d5bd6d064f34ecffee34d34e9459ae529c787c89ea02c1b1d449
3
+ metadata.gz: 05464224fe91ef9aeb8784b87a9331802f9f1625f13e75969f672dccd4548dcd
4
+ data.tar.gz: 1c5c4845548975b31204a71da7cdd8410b69c4806b191adc8c2047669a6cfd56
5
5
  SHA512:
6
- metadata.gz: 6e259782f7f9b0bbc2129cf453b43d7e3889f35fe0ca6f5b98ed737977ccd21a46f773fdfaf8abdab62f67b2218557ee211038c22aca08454e9b378fcf4e0482
7
- data.tar.gz: a7a2c855608200a56146ab8aed9f8c0994e01a452c41b72e45073bfa0bcd9b955bb2f6d32e748aa41632c58d61c29ee6c6765e5cc9826ea6c09c750421023aaf
6
+ metadata.gz: 553e142c91b2dc4a5d4f1ff379524c65be3400d17498bba2cd549f81d4970077f037e4d329a3ec1d327e67e18d39f61b0ae946bf3e7e053c4f15ecef1c38f384
7
+ data.tar.gz: cd10e4447bdbaeeb29d795f7862b33dea15320a5fc240a165850785be2d53da04da1d96979eb628edfecb01bd5e692f26c6dacc10230083a0e1401a83f4970a4
data/README.md CHANGED
@@ -1,8 +1,102 @@
1
1
  # AssetRam
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/asset_ram`. To experiment with that code, run `bin/console` for an interactive prompt.
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:
4
+
5
+ * 71% reduction in execution time
6
+ * 83% reduction in allocations
7
+
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.
9
+
10
+
11
+ ## Example stats for [the page](https://texas.public.law/statutes/tex._fam._code_section_1.001)
12
+
13
+ Without AssetRam:
14
+
15
+ ```
16
+ Completed 200 OK in 38ms (Views: 34.2ms | ActiveRecord: 0.9ms | Allocations: 30332)
17
+ ```
18
+
19
+ With AssetRam:
20
+
21
+ ```
22
+ Completed 200 OK in 11ms (Views: 5.1ms | ActiveRecord: 1.3ms | Allocations: 5208)
23
+ ```
24
+
25
+
26
+ ## Usage
27
+
28
+ Wrap every asset helper call with `#cache`, like this:
29
+
30
+ ### Before
31
+
32
+ ```ruby
33
+ <%= favicon_link_tag('favicon/favicon.ico', rel: 'icon') %>
34
+ ```
35
+
36
+ ### After
37
+
38
+ ```ruby
39
+ <%= AssetRam::Helper.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') } %>
40
+ ```
41
+
42
+ After booting up, messages like these will appear _once_ in the log. They show the
43
+ full cache key.
44
+
45
+ ```
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]
49
+ ```
50
+
51
+ I use it in my footer for social icons as well: (HAML syntax)
52
+
53
+ ```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/"
58
+ ```
59
+
60
+
61
+
62
+
63
+ ### In some cases, the cache key can't be inferred.
64
+
65
+ RamCache creates the cache key automatically using the view source filename and line number.
66
+ This works for most uses.
67
+
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:
71
+
72
+ ```ruby
73
+ <%= AssetRam::Helper.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) } %>
74
+ ```
75
+
76
+ ## Background: I was looking for ways to reduce allocations in my Rails app
77
+
78
+ In an effort to help my app run in a small 512MB virtual server, I looked through every view
79
+ invocation in the logs. After I optimized a bunch of my code, I realized that the asset helpers
80
+ 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**.
82
+
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)
85
+ would be fast and never return stale data: The RAM cache goes away on a deploy/restart, which is
86
+ when asset fingerprints could change.
87
+
88
+ And so one-by-one I started storing the computed asset paths in a hash, and saw pretty dramatic results.
89
+
90
+ ## How it works: Block-based code executed in the view's context and inferred cache keys
91
+
92
+ Rails has some magic around when the asset helpers are able to create the fingerprint path. I found
93
+ that the caching needs to be done within the context of a view. This is why the lib's API looks
94
+ the way it does.
95
+
96
+ To make it as easy as possible to use, the lib finds the view's source filename and the line number of
97
+ the code being cached. This has been working well and in production for four months in a large Rails app.
98
+
4
99
 
5
- TODO: Delete this and the text above, and describe your gem
6
100
 
7
101
  ## Installation
8
102
 
@@ -20,9 +114,6 @@ Or install it yourself as:
20
114
 
21
115
  $ gem install asset_ram
22
116
 
23
- ## Usage
24
-
25
- TODO: Write usage instructions here
26
117
 
27
118
  ## Development
28
119
 
@@ -30,10 +121,6 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
30
121
 
31
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).
32
123
 
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/asset_ram.
36
-
37
124
  ## License
38
125
 
39
126
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AssetRam
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/asset_ram.rb CHANGED
@@ -5,7 +5,7 @@ require_relative "asset_ram/version"
5
5
  #
6
6
  # Use in views to cache the asset path computation.
7
7
  #
8
- # For example, in an ERB file:
8
+ # For example, in a HAML file:
9
9
  #
10
10
  # = AssetRam::Helper.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') }
11
11
  #
@@ -17,6 +17,9 @@ require_relative "asset_ram/version"
17
17
  #
18
18
  # = AssetRam::Helper.cache(key: site) { stylesheet_link_tag("themes/#{site}", media: nil) }
19
19
  #
20
+ # To test and compare if this lib actually improves performance,
21
+ # set the ASSET_RAM_DISABLE env var and it will transparently never cache.
22
+ #
20
23
  #
21
24
  module AssetRam
22
25
  class Error < StandardError; end
@@ -25,16 +28,10 @@ module AssetRam
25
28
  # Our own asset helper which memoizes Rails' asset helper calls.
26
29
  class Helper
27
30
 
28
- ##
29
- # For testing the gains from caching with this library.
30
- # Set to true to disable caching.
31
- NO_CACHE = false
32
-
33
31
  @@_cache = {}
34
32
 
35
-
36
33
  def self.cache(key: '', &blk)
37
- return yield if NO_CACHE
34
+ return yield if ENV['ASSET_RAM_DISABLE']
38
35
 
39
36
  cache_key = blk.source_location
40
37
  cache_key << key if key.present?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asset_ram
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter