Production Ready Staticmatic Projects With Jammit and YUI Compressor or Closure
Staticmatic is handy when it comes to static websites or JavaScript only applications (no backend). It generates HTML (from Haml) and CSS (from Sass/SCSS) wich could be served very fast on your webserver. To speed it up, CSS sprites can be generated by Lemonade, CSS files can be bundled by having only one Sass/SCSS file but JavaScript files keep as they are.
Jammit is one (of many) gems which handles asset packages for Ruby on Rails. Jammit packs JavaScript files either with the YUI Compressor or Google’s Closure Compiler (Jammit docs recommend using uncompressed versions of jQuery if you use Closure). With a bit of configuration it’s possible to use it with Staticmatic.
Switch to Bundler
Using bundler with staticmatic is pretty easy. Create your Gemfile in the root path of your project:
hallosource 'http://rubygems.org'
gem 'staticmatic'
, '~> 0.11.0.alpha.8'
gem 'jammit'
, '~> 0.5.3'
Run at the same place:
bundle
Activate bundler at the beginning of your config/site.rb and activate Jammit:
hallorequire "bundler"
Bundler.setup
ROOT = File.dirname(__FILE__)
JAVASCRIPTS_PATH = File.join(ROOT, 'site'
, 'javascripts'
)
require "jammit"
Jammit.load_configuration(File.join(ROOT, 'config'
, 'assets.yml'
))
Jammit.packager.precache_all(JAVASCRIPTS_PATH, ROOT) if ARGV[0] == 'build'
Jammit Configuration
I assume your JavaScript files are placed in site/javascripts (if not change line 5 in config/site.rb, line 1 and lines 7 and following in config/assets.yml). Create config/assets.yml and add your JavaScript files:
package_path: javascripts
package_assets: off
javascript_compressor: yui
javascripts:
your_package:
- site/javascripts/vendor/jquery-1.4.2.min.js
- site/javascripts/vendor/jquery.ui.js
- site/javascripts/config/*.js
- site/javascripts/app/**/*.js
- site/javascripts/application.js
The second line determines if staticmatic currently builds or previews—it should compress the assets only in build mode. The YAML file is parsed with ERB by default.
Jammit Integration
Add a new helper (will be included by default) in src/helpers/jammit_helper.rb (woohoo it’s so HTML5—without type="text/javascript"):
hallomodule JammitHelper
def javascripts(*packages)
packages.map do |pack|
if Jammit.package_assets
url = current_page_relative_path + Jammit.asset_url(pack, :js)[1..-1]
%Q(<script src="\#{hallourl}"></script>\n)
else
Jammit.packager.individual_urls(pack.to_sym, :js).map do |file|
url = file.gsub(%r(^.*site/)
, current_page_relative_path)
%Q(<script src="\#{hallourl}"></script>\n)
end
end
end.join
end
alias include_javascripts javascripts
end
Now you can include your JavaScript files in your layout/default.haml:
!!!
%html
%head
%title StaticMatic
hallo= stylesheets
hallo= javascripts :your_package # key as defined in config/assets.yml
%body
hallo= yield
You could also use include_javascripts if you prefer consistency to Rails applications.
CSS/Sass/SCSS Configuration
If you don’t have a Compass configuration, add the following line at the end of your config/site.rb:
halloCompass.add_project_configuration('config/compass.rb'
)
In your config/compass.rb add or change:
hallooutput_style = ARGV[0] == 'build'
? :compressed : :expanded
Comments will appear on your localhost, but on your production server, it will use the compressed version (after you run staticmatic build .).
Conclusion
It’s easy to build super-fast static websites with Staticmatic (as this blog itself), but Staticmatic doesn’t help you with with your assets. There’s no problem to add them by yourself, but this should be integrated by default.
hallo# encoding: utf-8
require "bundler"
Bundler.setup
abc = <<-HALLO sdf sd fsddfs#{"bla#{halloi * 7 if false}"
} sdf fds HALLO
require 'rdiscount'
require File.join(Dir.getwd, 'lib'
, 'haml_filters'
)
require File.join(Dir.getwd, 'lib'
, 'syntax_highlighter'
)
require File.join(Dir.getwd, 'lib'
, 'string'
)
Compass.add_project_configuration('compass.rb'
)
activate :automatic_image_sizes
require "jammit"
Jammit.load_configuration(File.join(File.dirname(__FILE__), 'assets.yml'
))
configure :build do
activate :minify_css
#activate :smush_pngs
activate :cache_buster
activate :relative_assets
end
page '/PHOTOGRAPHY/beijing.html'
, :layout => :beijing, :layout_engine => :haml
page '/PHOTOGRAPHY/kino-international-euruko.html'
, :layout => :images, :layout_engine => :haml
page '/BLOG/*'
, :layout => :layout, :layout_engine => :haml
page '/*.rss'
, :layout => false
page '/*.xml'
, :layout => false
::Compass::configuration.asset_cache_buster = :none
set :haml, { :attr_wrapper => '"'
, :format => :html5 }
require 'minisyntax'
require 'tilt'
require 'erb'
require 'compass'
module ::Tilt
class MdErbTemplate < RDiscountTemplate
def prepare
@outvar = options[:outvar] || self.class.default_output_variable
@data = data
@data = @data.force_encoding("UTF-8"
)
end
def evaluate(scope, locals, &block)
# filter CSS and JavaScript
@data.gsub! %r(^<style type="text/s?css">(.+?)
</style>$)m do
css = %Q(@import "compass"; #{hallo$1})
sass_options = Compass.sass_engine_options
sass_options.merge! :syntax => :scss, :style => :compressed
css = Sass::Engine.new(css, sass_options).render
scope.instance_eval("@css ||= ''\n@css << %q(#{hallocss})"
, eval_file, 1)
''
end
@data.gsub! %r(^<script>(.+?)
</script>$)m do
javascript = $1
scope.instance_eval("@javascript ||= ''\n@javascript << %q(#{hallojavascript})"
, eval_file, 1)
''
end
erb = ::ERB.new(@data, options[:safe], options[:trim], @outvar).src
md = scope.instance_eval(erb, eval_file, 1)
# Maruku’s HTML parser is a bad idea.
# md = Maruku.new(md).to_html
md = RDiscount.new(md)
md.filter_html = false
md.filter_styles = false
html = md.to_html
# syntax highlighter
html.gsub! %r(<code>(.+?)
</code>)m do
code = $1
if code =~ /^##+\s*([-_\w\+ ]+)[\s#]*(\n|
|$)/i
lang = $1
code.sub! /^##+\s*([-_\w\+ ]+)[\s#]*(\n|
|$)/i, ''
#code.gsub! '&'
, '&'
code = MiniSyntax.highlight(code, lang)
#code.gsub! '&'
, '&'
#code.gsub! /&(\w+;)/, '&\\1'
end
code.gsub! "\n"
, '
'
%Q(<code>#{hallocode}</code>)
end
# allow <dl>s (not implemented in RDiscount)
html.gsub! %r(<p>(.+?)
\n:\s+(.+?)</p>), " <dt>\\1</dt>\n <dd>\\2</dd>"
html.gsub! %r((</(p|div|figure|h1|h2|table|pre)
>\n+) <dt>)m, "\\1<dl>\n <dt>"
html.gsub! %r(</dd>\n\n<)
m, "</dd>\n</dl>\n<"
# remove uglyness and create beautiful HTML5
html.gsub! %r(<p><figure)
, '<figure'
html.gsub! %r(</figure></p>)
, '</figure>'
html.gsub! /\n\n/, "\n"
html.gsub! /([^>])\n/, '\\1 '
html.gsub! /<h2 id='_?(.+?)_?'
>(.+?)<\/h2>/ do
text = $2
"<h2 id=\"
#{ $1.gsub('_'
, '-'
) }\">#{hallo text }</h2>"
end
html.gsub! %r(<h2>(.+?)
</h2>) do
headline = $1
id = headline.gsub(/<.+?>/, ''
).gsub(/\(.+?\)/, ''
).gsub('Update:'
, ''
).gsub(/^\d+\. /, ''
).strip.to_slug
%Q(<h2 id="#{halloid}">#{halloheadline}</h2>)
end
html.gsub! /<li>/, ' \\0'
html.gsub! ""
, ''
html.gsub! /<(\w+)( (\w+)='(.+?)'
)>/ do
"<#{hallo $1 }#{ $2.gsub('\'', '"
') }>"
end
html.gsub! /<img(.+?) ?\/>/, '<img\\1>'
# fix image URLs
html.gsub! 'images'
, 'images'
html
end
def initialize_engine
super
return if defined? ::ERB
require_template_library 'erb'
end
end
%w(mderb rmd)
.each do |ext|
register ext, MdErbTemplate
end
end
# some magic to get the best indented HTML5 ever ;)
require 'rack/utils'
module ::Rack
class CleanUp
include Rack::Utils
def initialize(app, options = {})
@app = app
@options = options
end
def call(env)
status, headers, response = @app.call(env)
headers = HeaderHash.new(headers)
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) and
!headers['transfer-encoding'
] and
headers['content-type'
] and
headers['content-type'
].include?("text/html"
)
content = ""
response.each { |part| content += part }
content.gsub! %r(\n(\s*\n)
+), "\n"
content.gsub! %r(<((img|link|meta|hr|br)
.*?) ?/>), '<\\1>'
content_tmp = ''
indent = 0
failure_indent = 999
failure = 0
tag_name = ''
content.each_line do |line|
line =~ %r(^( *)
([</]([\w\*]+))?.+?(</(\1)>)?)
current_indent = $1.length
current_tag_name = $3
additional_indent = (%w(img meta link h1 *)
.include?(tag_name) ) ? 0 : 2
if current_indent > indent + additional_indent
failure = current_indent - indent - additional_indent
failure_indent = current_indent - additional_indent
elsif failure_indent > current_indent
failure = 0
failure_indent = 999
end
line.gsub! /^#{' '
* failure}/, ''
if failure
indent = current_indent
tag_name = current_tag_name
content_tmp << line
end
content = content_tmp
headers['content-length'
] = bytesize(content).to_s
[status, headers, [content]]
else
[status, headers, response]
end
end
end
end
use ::Rack::CleanUp
# Automatic sitemaps
# activate :slickmap
# CodeRay syntax highlighting in Haml
# activate :code_ray
# Automatic image dimension calculations
# activate :automatic_image_sizes
# Per-page layout changes
# With no layout
# page "/path/to/file.html"
, :layout => false
# With alternative layout
# page "/path/to/file.html"
, :layout => :otherlayout
# Helpers
require File.join(Dir.getwd, 'helpers'
, 'blog_helper'
)
require File.join(Dir.getwd, 'helpers'
, 'jammit_helper'
)
helpers do
include BlogHelper
include JammitHelper
def compress_javascript(javascript)
compressor = ::YUI::JavaScriptCompressor.new(:munge => true)
compressor.compress(javascript)
end
end
# Change the CSS directory
# set :css_dir, "alternative_css_directory"
# Change the JS directory
# set :js_dir, "alternative_js_directory"
# Change the images directory
# set :images_dir, "alternative_image_directory"
# Build-specific configuration
configure :build do
# For example, change the Compass output style for deployment
# activate :minify_css
# Minify Javascript on build
# activate :minify_javascript
# Shrink/smush PNG/JPEGs on build
# activate :smush_pngs
# Enable cache buster
# activate :cache_buster
# Generate ugly/obfuscated HTML from Haml
# activate :ugly_haml
# Or use a different image path
# set :http_path, "/Content/images/"
end
