{"id":13878145,"url":"https://github.com/dogweather/asset_ram","last_synced_at":"2025-04-05T17:06:51.290Z","repository":{"id":56842615,"uuid":"410458386","full_name":"dogweather/asset_ram","owner":"dogweather","description":"Reduce Rails allocations by 35%+ and gain a speed boost. Memoizes asset links.","archived":false,"fork":false,"pushed_at":"2024-03-29T05:46:39.000Z","size":213,"stargazers_count":178,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-29T16:06:15.105Z","etag":null,"topics":["caching","performance","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dogweather.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-26T05:26:30.000Z","updated_at":"2025-01-03T17:27:53.000Z","dependencies_parsed_at":"2024-10-30T09:14:41.707Z","dependency_job_id":"bbf1ac6c-e2d8-4529-b810-659efe659772","html_url":"https://github.com/dogweather/asset_ram","commit_stats":{"total_commits":45,"total_committers":1,"mean_commits":45.0,"dds":0.0,"last_synced_commit":"dc81f5944eb4529f834fddabafd5581bf2a16cc8"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dogweather%2Fasset_ram","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dogweather%2Fasset_ram/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dogweather%2Fasset_ram/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dogweather%2Fasset_ram/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dogweather","download_url":"https://codeload.github.com/dogweather/asset_ram/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369952,"owners_count":20927928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caching","performance","rails","ruby"],"created_at":"2024-08-06T08:01:41.021Z","updated_at":"2025-04-05T17:06:51.269Z","avatar_url":"https://github.com/dogweather.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# AssetRam\n\n**Rails 7 update: I measured a 35% reduction in allocations.** I tested \n[my app's home page](https://www.public.law) in production:\n\n* Rails 7.0.6\n* Ruby 3.2.2\n* Sprockets v3 and v4\n* JE_MALLOC\n* `--enable-yjit`\n\nThe page is simple with only five images. If you have more, you'll get \nan even bigger boost:\n\n\u003cimg src=\"https://github.com/dogweather/asset_ram/raw/master/test-data.png\" alt=\"Test Data\" style=\"width: 70%;\"\u003e\n\nThe savings come from avoiding asset calculations. The app is faster, too.\nBut it's hard for me to measure precisely: enabling AssetRam, this page time\ngoes down from ~9ms to ~6ms.\n\n\u003e Tip: Set env var `ASSET_RAM_DISABLE` to do this comparison in your own app.\n\n\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'asset_ram'\n```\n\nAnd then execute:\n\n```\n$ bundle install\n```\n\n\n## Usage\n\nWrap every asset helper call with `#cache`, like this:\n\n\n### Before\n\n```ruby\n\u003c%= favicon_link_tag('favicon/favicon.ico', rel: 'icon') %\u003e\n# ...\n\u003c%= javascript_include_tag('application.js') %\u003e\n```\n\n\n### After\n\n```ruby\n\u003c%= AssetRam::Helper.cache { favicon_link_tag('favicon/favicon.ico', rel: 'icon') } %\u003e\n# ...\n\u003c%= AssetRam::Helper.cache { javascript_include_tag('application.js') } %\u003e\n```\n\nAfter booting up, AssetRam sends a message like this _once_ to the log for each usage:\n\n```\nCaching [\"/website/app/views/application/_favicon.haml\", 8]\n```\n\nIt outputs this when the asset link is generated. It shows the full cache key (filename\nand line number)\nso we can see what it's caching. This is the line of code that, without AssetRam,\nwould be exectued on every request.\n\n\nI use it in my footer for social icons as well. I **used to** have this: (HAML syntax) \n\n```ruby\n- asset = AssetRam::Helper\n\n= link_to asset.cache { image_tag(\"social/instagram-logo.svg\", alt: 'Instagram', loading: 'lazy', decoding: 'async') },    \"https://www.instagram.com/law.is.code/\"\n= link_to asset.cache { image_tag(\"social/facebook-logo-button.svg\", alt: 'Facebook', loading: 'lazy', decoding: 'async') }, \"https://www.facebook.com/PublicDotLaw\"\n= link_to asset.cache { image_tag(\"social/twitter-logo-button.svg\", alt: 'Twitter', loading: 'lazy', decoding: 'async') },   \"https://twitter.com/law_is_code\"\n= link_to asset.cache { image_tag(\"social/github-logo.svg\", alt: 'Our GitHub Page', loading: 'lazy', decoding: 'async') },   \"https://www.github.com/public-law/\"\n```\n\n\nBut my whole footer partial is static. So now I just do this instead in my layout:\n\n```ruby\n= AssetRam::Helper.cache { render 'footer_for_screen' }\n```\n\n\n### In some cases, the cache key can't be inferred.\n\nAssetRam creates the cache key automatically using the view's source filename and line number.\nThis works for most uses. \n\nSome of my app's views are an exception, however. It's **multi-tenant** and the views serve content\nfor many sub-domains. To handle this, the call to `#cache` allows extra key info to be passed.\nIn 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:\n\n```ruby\n\u003c%= AssetRam::Helper.cache(key: site) { stylesheet_link_tag(\"themes/#{site}\", media: nil) } %\u003e\n```\n\n## Background: I was looking for ways to reduce allocations in my Rails app\n\nIn an effort to help my app run in a small 512MB virtual server, I looked through every view\ninvocation in the logs. After I optimized a bunch of my code, I realized that the asset helpers\ncreate a relatively large amount of objects. The code is pretty complex too implying some amount\nof CPU overhead. Moreover, this work is **repeated on every request**.\n\nThese asset fingerprints are potentially re-generated on every deploy. Maybe I edit an image, but\nI haven't modified any ActiveRecord models. This means that **the asset links cannot be stored in\nthe standard Rails cache.** (If the Rails cache had a lifetime option of, \"until next boot\", that would solve the problem.)\n\nI realized that storing the computed paths in a simple hash (in RAM only)\nwould be fast and never return stale data: The RAM cache goes away on a deploy/restart, which is\nwhen asset fingerprints could change.\n\nAnd so one-by-one I started storing the computed asset paths in a hash, and saw pretty dramatic results.\n\n## How it works: Block-based code executed in the view's context and inferred cache keys\n\nRails has some magic around when the asset helpers are able to create the fingerprint path. I found\nthat the caching needs to be done within the context of a view. This is why the lib's API looks\nthe way it does. \n\nTo make it as easy as possible to use, the lib finds the view's source filename and the line number of\nthe code being cached. This has been working well and in production for four months in a large Rails app.\n\n\n## Development\n\nAfter 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.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdogweather%2Fasset_ram","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdogweather%2Fasset_ram","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdogweather%2Fasset_ram/lists"}