There are a lot of tools that accomplish very similar things in this space. Pinion is meant to be a very simple and lightweight solution. It is driven by these core goals (bold goals are implemented):
- Simple configuration and usage.
- No added syntax to your assets (e.g. no
//= require my_other_asset)
- Recompile all compiled assets when they change (or dependencies change) in development and set mtimes
- Recompile asynchronously from requests (no polling allowed)
- Compile assets one time in production
$ gem install pinion
You should add pinion to your project's Gemfile.
The easiest way to use Pinion is to map your desired asset mount point to a
Pinion::Server instance in your
In your app, you will use pinion's helper methods to construct urls:
<head> <title>My App</title> <link type="text/css" rel="stylesheet" href="<%= pinion.asset_url("/assets/style.css") %>" /> <%# Shorthand equivalent %> <%= pinion.css_url("style.css") %> </head>
In production, you may wish to concatenate and minify your assets before you serve them. This is done through
using asset bundles. Pinion provides a predefined bundle type,
:concatenate_and_uglify_js, for your
You can bundle files by putting this in your app:
<%= @pinion.js_bundle(:concatenate_and_uglify_js, "main-bundle", "app.js", "helpers.js", "util.js", "jquery.js" ) %>
In development, the individual
<script> tags for each asset will be emitted; in production, a single asset
main-bundle.js) will be produced.
:concatenate_and_uglify_js bundle type simply concatenates JS files and runs them through
Uglifier. No default CSS bundle type is provided (but the built-in Sass
conversion type emits minified code in production, and typically you'll let Sass/Less/Stylus handle
concatenation for you).
You can define your own bundle types and their behavior if you like:
# The block is passed an array of `Pinion::Asset`s; it should return the content of the bundled files. Pinion::BundleType.create(:concatenate_js_only) do |assets| # Demo code only; you need to be a bit more careful in reality. See the definition of # :concatenate_and_uglify_js for hints. assets.map(&:contents).join("\n") end
Note that in production mode, asset URLs will have the md5sum of the asset inserted into them:
<link type="text/css" rel="stylesheet" href="/assets/style-698f462d2f43890597ae78df8286d03f.css" /> <script src="/assets/test-bundle-cd94852076ffa13c006cf575dfff9e35.js"></script>
and these assets are served with long (1-year) expiry, for good cacheability.
- Currently, Pinion sidesteps the dependency question by invalidating its cache of each file of a particular
type (say, all
.scssfiles) when any such source file is changed.
- The order that paths are added to the watch list is a priority order in case of conflicting assets. (For
foo/bazare both on the watch list, and both of the files
foo/bar/style.scsswill be used if a request occurs for
You can see an example app using Pinion and Sinatra in the
example/ directory. Run
bundle install in that
directory to get the necessary gems, then run it:
rackup config.ru # Development mode RACK_ENV=production rackup config.ru # Production mode
- Alek Storm (alekstorm)
Pinion is released under the MIT License.