How to bundle assets in a Rails engine
All you need to know about Rails Engines

I'ma self-taught developer on my journey to become a successful indie dev.
For more content follow me on Twitter @adrianthedev
Search for a command to run...
All you need to know about Rails Engines

I'ma self-taught developer on my journey to become a successful indie dev.
For more content follow me on Twitter @adrianthedev
No comments yet. Be the first to comment.
I want to share my experience regarding AI-driven development.I know it's not a new and shiny thing, and it's been around for a while now, but some things should need some time to take it through its

I think it's important that the Ruby community knows what just happened in San Francisco last week. 400+ Rubyists got together and shared ideas and visions. They exchanged opinions and presented the past, present, and future of Ruby. I went to very ...

Tl;DR; We're taking a break with Friendly.rb. We had three beautiful editions, incredible fun, and learned so much in the process. This happening changed us, the hosts in ways we could not imagine. We're taking a break because things have happened in...

What do you need in your digital brain? That's how I think about Avo now. It's not some closeted admin panel where only five people dare to go because touching the data wrong might crash the app. It's the place where you invite your whole team to run...
Here's something that's been on my mind lately about the Rails ecosystem. For the past 10 years, there's been massive investment in dev tooling, especially for JavaScript and TypeScript companies. They take some of that funding and deploy it to marke...
Own it - A blog from Adrian Marin
24 posts
Sharing my lessons on becoming a thriving #indiedev.
๐ Launching Avo into the world! - ๐ฅ A beautiful, easy-to-use Ruby on Rails admin panel framework
On this page
During Rails' lifetime, we had a lot of ways to load, parse, and process assets. The "recommended" way is to hook into the asset pipeline (whichever that is) and, on deployment, let the assets:precompile task to take over and... compile them so they can be used in your app.
But, if you built an engine that you want to distribute to others, there might be a few things that might get in the way.
Let's say you're using jsbundling-rails and/or tailwindcss-rails with your distributable engine. To you, it's common to have all you need to compile those assets. You'll have Node.js and all other external dependencies installed and ready to compile everything together, but the parent app that's using your gem might not. They might use importmaps, and they might not have all that Node.js infrastructure.
When the user pushes the Rails app with your gem installed, they might get the gem without any assets (because they haven't been built yet), or they might even get a crash that sprockets (or propshaft) can't find those assets.
But there's a better way!
One approach we had with avo-hq/avo was to precompile the assets before we publish a new version and serve them as static assets to the parent app.
The overview is this:
Set up build commands
Provide a way for the parent app to reach those assets
Run all the compilation commands
Package the gem up with those static assets
You first install your asset handlers as you need them for your project. They can be anything from rails/jsbundling-rails and rails/tailwindcss-rails to webpacker or something custom.
Add some commands to build up those assets. We have the following scripts inside the package.json file:
"scripts": {
"prod:build:js": "esbuild app/javascript/*.js --bundle --sourcemap --minify --outdir=public/avo-assets",
"prod:build:css": "tailwindcss -i ./app/assets/stylesheets/avo.base.css -o ./public/avo-assets/avo.base.css --postcss",
}
They take the source JS and CSS files, compile them, and move them to a public/assets directory.
When you ship your gem, that public directory will not be public at all. It will be hosted somewhere hidden on that machine, so the browser will not have a way to reach them.
Let's use a Rack::Static middleware to the engine.rb file.
module Avo
class Engine < ::Rails::Engine
config.app_middleware.use(
Rack::Static,
urls: ["/avo-assets"],
root: Avo::Engine.root.join("public")
)
end
end
Now, the parent app will re-route all the traffic from /avo-assets to that "hidden" directory where our compiled assets are.
When we're ready to ship our gem to RubyGems (or any other gems server), we should run the compilation commands to ensure all the JS and CSS files are compiled, minified, and packaged up how we need them to be in production.
$ yarn prod:build:js
$ yarn prod:build:css
We need to instruct the gem utility to add the compiled files to the packaged gem.
Gem::Specification.new do |spec|
spec.name = "avo"
spec.version = Avo::VERSION
# more spec properties
spec.files = Dir["{bin,app,config,db,lib,public}/**/*", "MIT-LICENSE", "Rakefile", "README.md", "avo.gemspec", "Gemfile", "Gemfile.lock"]
spec.add_dependency "activerecord", ">= 6.0"
# other dependencies here
end
The spec.files property knows which files should be bundled up. Finally, you'll see the Dir["{public}/**/*"] will add the whole public directory to the gem, including the avo-assets one.
Run bundle exec rails build to package everything up and profit ๐
This post is part of a series I'm writing to share some of my learnings while building my own distributed engine, Avo.