Class: Jammit::Packager

Inherits:
Object
Defined in:
lib/jammit/packager.rb

Overview

The Jammit::Packager resolves the configuration file into lists of real assets that get merged into individual asset packages. Given the compiled contents of an asset package, the Packager knows how to cache that package with the correct timestamps.

Constant Summary

PATH_TO_URL = In Rails, the difference between a path and an asset URL is “public”.
/\A#{ASSET_ROOT}(\/public)?/

Attribute Summary

Method Summary

Constructor Details

- (Packager) initialize

Creating a new Packager will rebuild the list of assets from the Jammit.configuration. When assets.yml is being changed on the fly, create a new Packager.

Returns:

  • (Packager) — a new instance of Packager


19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/jammit/packager.rb', line 19

def initialize
  @compressor = Compressor.new
  @force = false
  @config = {
    :css => (Jammit.configuration[:stylesheets] || {}).symbolize_keys,
    :js  => (Jammit.configuration[:javascripts] || {}).symbolize_keys,
    :jst => (Jammit.configuration[:templates]   || {}).symbolize_keys
  }
  @packages = {
    :css => create_packages(@config[:css]),
    :js  => create_packages(@config[:js]),
    :jst => create_packages(@config[:jst])
  }
end

Attribute Details

- (Object) force

Set force to false to allow packages to only be rebuilt when their source files have changed since the last time their package was built.



14
15
16
# File 'lib/jammit/packager.rb', line 14

def force
  @force
end

Method Details

- (Object) cache(package, extension, contents, output_dir, suffix = nil, mtime = Time.now)

Caches a single prebuilt asset package and gzips it at the highest compression level. Ensures that the modification time of both both variants is identical, for web server caching modules, as well as MHTML.



59
60
61
62
63
64
65
66
# File 'lib/jammit/packager.rb', line 59

def cache(package, extension, contents, output_dir, suffix=nil, mtime=Time.now)
  FileUtils.mkdir_p(output_dir) unless File.exists?(output_dir)
  filename = File.join(output_dir, Jammit.filename(package, extension, suffix))
  zip_name = "#{filename}.gz"
  File.open(filename, 'wb+') {|f| f.write(contents) }
  Zlib::GzipWriter.open(zip_name, Zlib::BEST_COMPRESSION) {|f| f.write(contents) }
  File.utime(mtime, mtime, filename, zip_name)
end

- (Object) individual_urls(package, extension)

Get the list of individual assets for a package.



69
70
71
# File 'lib/jammit/packager.rb', line 69

def individual_urls(package, extension)
  package_for(package, extension)[:urls]
end

- (Object) pack_javascripts(package)

Return the compressed contents of a javascript package.



79
80
81
# File 'lib/jammit/packager.rb', line 79

def pack_javascripts(package)
  @compressor.compress_js(package_for(package, :js)[:paths])
end

- (Object) pack_stylesheets(package, variant = nil, asset_url = nil)

Return the compressed contents of a stylesheet package.



74
75
76
# File 'lib/jammit/packager.rb', line 74

def pack_stylesheets(package, variant=nil, asset_url=nil)
  @compressor.compress_css(package_for(package, :css)[:paths], variant, asset_url)
end

- (Object) pack_templates(package)

Return the compiled contents of a JST package.



84
85
86
# File 'lib/jammit/packager.rb', line 84

def pack_templates(package)
  @compressor.compile_jst(package_for(package, :jst)[:paths])
end

- (Object) precache_all(output_dir = nil, base_url = nil)

Ask the packager to precache all defined assets, along with their gzip’d versions. In order to prebuild the MHTML stylesheets, we need to know the base_url, because IE only supports MHTML with absolute references. Unless forced, will only rebuild assets whose source files have been changed since their last package build.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/jammit/packager.rb', line 39

def precache_all(output_dir=nil, base_url=nil)
  output_dir ||= File.join(PUBLIC_ROOT, Jammit.package_path)
  cacheable(:js, output_dir).each  {|p| cache(p, 'js',  pack_javascripts(p), output_dir) }
  cacheable(:jst, output_dir).each {|p| cache(p, 'jst', pack_templates(p),  output_dir) }
  cacheable(:css, output_dir).each do |p|
    cache(p, 'css', pack_stylesheets(p), output_dir)
    if Jammit.embed_images
      cache(p, 'css', pack_stylesheets(p, :datauri), output_dir, :datauri)
      if Jammit.mhtml_enabled && base_url
        mtime = Time.now
        asset_url = "#{base_url}#{Jammit.asset_url(p, :css, :mhtml, mtime)}"
        cache(p, 'css', pack_stylesheets(p, :mhtml, asset_url), output_dir, :mhtml, mtime)
      end
    end
  end
end

- (Object) cacheable(extension, output_dir) (private)

Return a list of all of the packages that should be cached. If “force” is true, this is all of them — otherwise only the packages whose source files have changed since the last package build.



107
108
109
110
111
112
113
114
115
116
# File 'lib/jammit/packager.rb', line 107

def cacheable(extension, output_dir)
  names = @packages[extension].keys
  return names if @force
  return names.select do |name|
    pack    = package_for(name, extension)
    cached  = File.join(output_dir, Jammit.filename(name, extension))
    since   = File.exists?(cached) && File.mtime(cached)
    !since || pack[:paths].any? {|src| File.mtime(src) > since }
  end
end

- (Object) create_packages(config) (private)

Compiles the list of assets that goes into each package. Runs an ordered list of Dir.globs, taking the merged unique result.



120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/jammit/packager.rb', line 120

def create_packages(config)
  packages = {}
  return packages if !config
  config.each do |name, globs|
    globs                  ||= []
    packages[name]         = {}
    paths                  = globs.map {|glob| glob_files(glob) }.flatten.uniq
    packages[name][:paths] = paths
    packages[name][:urls]  = paths.map {|path| path.sub(PATH_TO_URL, '') }
  end
  packages
end

- (Object) glob_files(glob) (private)

Absolute globs are absolute — relative globs are relative to ASSET_ROOT.



99
100
101
102
# File 'lib/jammit/packager.rb', line 99

def glob_files(glob)
  absolute = Pathname.new(glob).absolute?
  Dir[absolute ? glob : File.join(ASSET_ROOT, glob)]
end

- (Object) not_found(package, extension) (private)

Raise a PackageNotFound exception for missing packages…

Raises:



134
135
136
# File 'lib/jammit/packager.rb', line 134

def not_found(package, extension)
  raise PackageNotFound, "assets.yml does not contain a \"#{package}\" #{extension.to_s.upcase} package"
end

- (Object) package_for(package, extension) (private)

Look up a package asset list by name, raising an exception if the package has gone missing.



93
94
95
96
# File 'lib/jammit/packager.rb', line 93

def package_for(package, extension)
  pack = @packages[extension] && @packages[extension][package]
  pack || not_found(package, extension)
end