Class: Jammit::Packager

Inherits:
Object
  • Object
show all
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.

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

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.



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/jammit/packager.rb', line 16

def initialize
  @force          = false
  @package_names  = nil
  @config = {
    :css => (Jammit.configuration[:stylesheets] || {}),
    :js  => (Jammit.configuration[:javascripts] || {})
  }
  @packages = {
    :css => create_packages(@config[:css]),
    :js  => create_packages(@config[:js])
  }
end

Instance 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.



11
12
13
# File 'lib/jammit/packager.rb', line 11

def force
  @force
end

- (Object) package_names

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.



11
12
13
# File 'lib/jammit/packager.rb', line 11

def package_names
  @package_names
end

Instance Method Details

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

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.

Raises:



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/jammit/packager.rb', line 54

def cache(package, extension, contents, output_dir, suffix=nil, mtime=nil)
  FileUtils.mkdir_p(output_dir) unless File.exists?(output_dir)
  raise OutputNotWritable, "Jammit doesn't have permission to write to \"#{output_dir}\"" unless File.writable?(output_dir)
  mtime ||= latest_mtime package_for(package, extension.to_sym)[:paths]
  files = []
  files << file_name = File.join(output_dir, Jammit.filename(package, extension, suffix))
  File.open(file_name, 'wb+') {|f| f.write(contents) }
  if Jammit.gzip_assets
    files << zip_name = "#{file_name}.gz"
    Zlib::GzipWriter.open(zip_name, Zlib::BEST_COMPRESSION) {|f| f.write(contents) }
  end
  File.utime(mtime, mtime, *files)
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 that are missing or whose source files have changed since the last package build.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/jammit/packager.rb', line 125

def cacheable(extension, output_dir)
  names = @packages[extension].keys
  names = names.select {|n| @package_names.include? n } if @package_names
  config_mtime = File.mtime(Jammit.config_path)
  return names if @force
  return names.select do |name|
    pack        = package_for(name, extension)
    cached      = [Jammit.filename(name, extension)]
    if extension == :css
      cached.push Jammit.filename(name, extension, :datauri) if Jammit.embed_assets
      cached.push Jammit.filename(name, extension, :mhtml) if Jammit.mhtml_enabled
    end
    cached.map! {|file| File.join(output_dir, file) }
    if cached.any? {|file| !File.exists?(file) }
      true
    else
      since = cached.map {|file| File.mtime(file) }.min
      config_mtime > since || pack[:paths].any? {|src| File.mtime(src) > since }
    end
  end
end

- (Object) compressor



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

def compressor
  @compressor ||= Compressor.new
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. If there are JST files in this package we need to add an extra path for when package_assets is off (e.g. in a dev environment). This package (e.g. /assets/package-name.jst) will never exist as an actual file but will be dynamically generated by Jammit on every request.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/jammit/packager.rb', line 154

def create_packages(config)
  packages = {}
  return packages if !config
  config.each do |name, globs|
    globs                  ||= []
    packages[name]         = {}
    paths                  = globs.flatten.uniq.map {|glob| glob_files(glob) }.flatten.uniq
    packages[name][:paths] = paths
    if !paths.grep(Jammit.template_extension_matcher).empty?
      packages[name][:urls] = paths.grep(JS_EXTENSION).map {|path| path.sub(path_to_url, '') }
      packages[name][:urls] += [Jammit.asset_url(name, Jammit.template_extension)]
    else
      packages[name][:urls] = paths.map {|path| path.sub(path_to_url, '') }
    end
  end
  packages
end

- (Object) glob_files(glob) (private)

Absolute globs are absolute — relative globs are relative to ASSET_ROOT. Print a warning if no files were found that match the glob.



104
105
106
107
108
109
# File 'lib/jammit/packager.rb', line 104

def glob_files(glob)
  absolute = Pathname.new(glob).absolute?
  paths = Dir[absolute ? glob : File.join(ASSET_ROOT, glob)].sort
  Jammit.warn("No assets match '#{glob}'") if paths.empty?
  paths
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) latest_mtime(paths) (private)

Get the latest mtime of a list of files (plus the config path).



117
118
119
120
# File 'lib/jammit/packager.rb', line 117

def latest_mtime(paths)
  paths += [Jammit.config_path]
  paths.map {|p| File.mtime(p) }.max || Time.now
end

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

Raise a PackageNotFound exception for missing packages…

Raises:



173
174
175
# File 'lib/jammit/packager.rb', line 173

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

- (Object) pack_javascripts(package)

Return the compressed contents of a javascript package.



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

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.



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

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.



88
89
90
# File 'lib/jammit/packager.rb', line 88

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

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

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



97
98
99
100
# File 'lib/jammit/packager.rb', line 97

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

- (Object) path_to_url (private)

In Rails, the difference between a path and an asset URL is “public”.



112
113
114
# File 'lib/jammit/packager.rb', line 112

def path_to_url
  @path_to_url ||= /\A#{Regexp.escape(ASSET_ROOT)}(\/?#{Regexp.escape(Jammit.public_root.sub(ASSET_ROOT, ''))})?/
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.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/jammit/packager.rb', line 34

def precache_all(output_dir=nil, base_url=nil)
  output_dir ||= File.join(Jammit.public_root, Jammit.package_path)
  cacheable(:js, output_dir).each  {|p| cache(p, 'js',  pack_javascripts(p), output_dir) }
  cacheable(:css, output_dir).each do |p|
    cache(p, 'css', pack_stylesheets(p), output_dir)
    if Jammit.embed_assets
      cache(p, 'css', pack_stylesheets(p, :datauri), output_dir, :datauri)
      if Jammit.mhtml_enabled
        raise MissingConfiguration, "A --base-url option is required in order to generate MHTML." unless base_url
        mtime = latest_mtime package_for(p, :css)[:paths]
        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