This library suppose to be an alternative for assets processing for ruby web apps. First there was just dislike with how restrictive Rails feels to any javascript whatsoever. After digging deep into big and messy sprockets, I realized I'd rather make my own alternative then spend days trying to pack in my fancy features into sprockets.
gem install 'gluey'
Start up yard server and browse the documentation on local:
bundle exec yard server
As this is an 'alternative', it tries to mimic the sprockets library. For scripting, here also are headers on top of files, that specify how the parts will be compossed. Though apparent difference is that you can add your own headers/directives very easily and you have full control of the result.
Each type of assets have to be defined as a 'material':
GLUEY_ENV = Gluey::Workshop.new Rails.root.to_s, path_prefix: '/gluey_assets' do
# css material
register_material :css, Gluey::Glues.load('script') do |m|
m.paths << 'assets/stylesheets'
m.items.concat %w(pages admin)
end
end
For details refer to Worshop
and Material
classes in documentation, but important here is this:
- Workshop needs to know on what path will be assets served (default :'/assets'), that is in this case it will be on '/gluey_assets/css/pages.css'.
- Paths for materials are relative to Workshop's root, therefore css files will get looked up for at '/assets/stylesheets/pages.css'.
- There are only two items, that will be processed via gluey: 'admin.css' and 'pages.css'. Requesting anything else would give error Gluey::FileNotFound.
- Material needs to know HOW to process the files, and that's the role of 'glues'. You have to load
each glue, that you use, because they are not loaded automatically. That
Gluey::Glues.load('script')
will load generic script glue and return the appropriate class. Once loaded you can simply reference themGluey::Glues::Script
- Material's name gives part of path on which assets will be served. But you can specify what different
file_extension
files have or what differentasset_extension
should the resulting assets have.
Consider this:
register_material :my_css, Gluey::Glues.load('sass') do |m|
m.set asset_extension: 'css', file_extension: 'scss'
end
Suppose you have gluey environment (/workshop) set up as above, this is how to get the path on which assets will be served:
<link rel="stylesheet" type="text/css" href="<%= GLUEY_ENV.asset_url :css, 'pages' %>">
What material look up is a 'base file' in given destination. Items therefore simply can be under nested directories like 'pages/dependencies'. This code
GLUEY_ENV.asset_url :js, 'page/dependencies'
will look up for base file at: (given :js material's paths is 'assets' and file_extension has not been assigned)
- /assets/page/dependencies.js
- /assets/page/dependencies/index.js
- /assets/page/dependencies.js.erb
- /assets/page/dependencies/index.js.erb
Now, if the base file has .erb extenesion it will gou through ERB first (binded to gluey
environment - GLUEY_ENV
). Given glue for that material (supposedly Gluey::Glues::JsScript
)
will build the asset. Every other file (and base file) will be picked up and stored as dependency,
therefore any change you make in any of these files will impose the building process next time
asset is referenced.
Example how to reference other files from within some script:
//> append greeter.js
$(function () {
console.log(Greeter.english());
});
Every header/directive can work differently, but mostly it references a file on relative path to
file from which it was referenced. For more, each directive can be two step process - glue will
try to call it before and after of appending of the file's body. That can prove convenient in
some situation. Here are descriptions some of directives defined for Gluey::Gluey::Script
:
prepend
inserts a file before the rest of the file's bodyappend
inserts a file after the rest of the file's bodydepend_on
add a file dependency to the bundle
And here's additions for Gluey::Gluey::JsScript
:
enclose
wraps the whole body into js scope:"(function(){\n#{@output}\n}());"
. This one is utilized on the latter step, i.e. after all 'before-' directives have been run and the file's body was appended to@output
.replace
example://> replace GREETER_CLASS greeter.js
will look up for"%GREETER_CLASS%"
verbatim and replace it with content of referenced asset.
For JsScript
glue there are even addons, that can add some useful other common features. Role
of addons is simply to add some directives and are applied straight to the class. You simply
require
particular file or use this while defining material:
Gluey::Glues.load('js_script', 'handlebars') # returns Gluey::Glues::JsScript with handlebars support
That adds possibility to reference handlebars templates that will get precompiled and insert as a object into js code:
//> replace_with_handlebars TEMPALTES templates
var templates = "%TEMPLATES%";
Given that '/templates' is a folder relative to the base file and it contains 'menu.hb' file and 'header.hb' in 'body' subdirectory, this will be the result:
var templates = {
"menu": ['...'],
"body/header": ['..']
};
There is the Gluey::Glues::Copy
glues that doesnẗ process the files in any way, but utilizes
the gluey process for other purposes. (Of course you can always hard reference your graphics
assets in html)
On developement, use Gluey::Workshop
, that watches for changes in assets and enables you to
reference to built results. Attaching Gluey::RackMountable
to your routes will utilize
serving requested assets to client.
(or simply use #find_asset_file
to get path to the built asset, so you can
serve it to client.)
Rails.application.routes.prepend do
mount Gluey::RackMountable.new(GLUEY_ENV, Rails.logger) => GLUEY_ENV.path_prefix
end
GLUEY_APP = Gluey::RackMountable.new GLUEY_ENV, logger
get "/gluey_assets*" do |asset|
GLUEY_APP.dup.call env.merge!('PATH_INFO' => asset)
end
But for production you need to use Gluey::Warehouse
, this environment doesn't create assets
but serves only the role of finding the reference to them. Therefore:
GLUEY_ENV = if Rails.env.development?
Gluey::Workshop.new Nesselsburg.root, &define_materials_proc
else
Gluey::Warehouse.new Nesselsburg.root, mark_versions: true
end
The mark_versions
option serve the purpose of cache-busting for browsers, because assets get a stamp
to tell the version. Warehouse needs to have it's index file built to know how to reeference to
assets (look for warehouse.write_listing GLUEY_ENV
in example how to generate assets for
production)
You have to build your own way, how to generate assets fro production, because it hugely depends on
how you serve them. Either you can you the same Gluey::RackMountable
app as for development, or
use nginx. In both cases you need to put your assets into public directory. But then, you may
wanth to upload your assets to somewhere else, like AWS S3...
The library has some helpers in Gluey::Tools
that will help you anyway to put the process together.
Here is example how to generate assets into public dir using rake task:
desc 'creates assets into public dir'
task make: :environment do
require 'gluey/tools/local_make'
raise 'Only for development environment!' unless GLUEY_ENV.is_a? Gluey::Workshop
GLUEY_ENV.mark_versions = true
warehouse = Gluey::Warehouse.new GLUEY_ENV.root, path_prefix: GLUEY_ENV.path_prefix
# For the production build I wanth treat those assets differently
Gluey::Glues::Sass.engine_opts = {style: :compressed}
Gluey::Glues.load 'js_script', 'uglifier'
# makes a special index file for warehouse (!important!)
warehouse.write_listing GLUEY_ENV
created = Gluey::Tools.make_into_assets_dir GLUEY_ENV, warehouse
# this is purely to enjoy of my layzines (use with caution!)
#unless created.empty?
# puts 'adding new files into git ...'
# created.each{|file| %x(git add #{file})}
#end
end
One could build some process to clear out old versions of assets, but that is hugely dependent. Look for my attempts into 'lib/gluey/tools/local_make.rb' if you're interested.
(Any comments or support are welcome!) - the future is open
- Fork it ( https://github.com/[my-github-username]/gluey/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request