sinatra-assetpack-flexible-compression 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +2 -0
- data/HISTORY.md +131 -0
- data/README.md +664 -0
- data/Rakefile +31 -0
- data/docsrc/style.css +32 -0
- data/examples/basic/.gitignore +1 -0
- data/examples/basic/Rakefile +7 -0
- data/examples/basic/app.rb +39 -0
- data/examples/basic/app/css/test.sass +11 -0
- data/examples/basic/app/images/icon.png +0 -0
- data/examples/basic/app/js/app.js +4 -0
- data/examples/basic/app/js/vendor/jquery.js +2 -0
- data/examples/basic/app/js/vendor/jquery.plugin.js +2 -0
- data/examples/basic/app/js/vendor/underscore.js +2 -0
- data/examples/basic/views/index.erb +26 -0
- data/examples/classic/app.rb +24 -0
- data/examples/classic/css/screen.scss +1 -0
- data/examples/compass/.gitignore +1 -0
- data/examples/compass/Rakefile +7 -0
- data/examples/compass/app.rb +45 -0
- data/examples/compass/app/css/main.scss +64 -0
- data/examples/compass/app/images/icon-scfd8d7d404.png +0 -0
- data/examples/compass/app/images/icon/mail.png +0 -0
- data/examples/compass/app/images/icon/refresh.png +0 -0
- data/examples/compass/app/images/junk/mail.png +0 -0
- data/examples/compass/app/images/junk/refresh.png +0 -0
- data/examples/compass/app/js/app.js +3 -0
- data/examples/compass/app/js/vendor/jquery.js +2 -0
- data/examples/compass/app/js/vendor/jquery.plugin.js +2 -0
- data/examples/compass/app/js/vendor/underscore.js +2 -0
- data/examples/compass/config.ru +3 -0
- data/examples/compass/views/index.erb +15 -0
- data/lib/sinatra/assetpack.rb +61 -0
- data/lib/sinatra/assetpack/buster_helpers.rb +36 -0
- data/lib/sinatra/assetpack/class_methods.rb +82 -0
- data/lib/sinatra/assetpack/compressor.rb +54 -0
- data/lib/sinatra/assetpack/configurator.rb +21 -0
- data/lib/sinatra/assetpack/css.rb +36 -0
- data/lib/sinatra/assetpack/engine.rb +20 -0
- data/lib/sinatra/assetpack/engines/closure.rb +19 -0
- data/lib/sinatra/assetpack/engines/jsmin.rb +10 -0
- data/lib/sinatra/assetpack/engines/sass.rb +11 -0
- data/lib/sinatra/assetpack/engines/simple.rb +11 -0
- data/lib/sinatra/assetpack/engines/sqwish.rb +21 -0
- data/lib/sinatra/assetpack/engines/uglify.rb +12 -0
- data/lib/sinatra/assetpack/engines/yui.rb +22 -0
- data/lib/sinatra/assetpack/hasharray.rb +66 -0
- data/lib/sinatra/assetpack/helpers.rb +61 -0
- data/lib/sinatra/assetpack/html_helpers.rb +17 -0
- data/lib/sinatra/assetpack/image.rb +59 -0
- data/lib/sinatra/assetpack/options.rb +326 -0
- data/lib/sinatra/assetpack/package.rb +116 -0
- data/lib/sinatra/assetpack/rake.rb +29 -0
- data/lib/sinatra/assetpack/version.rb +7 -0
- data/test/app/.gitignore +1 -0
- data/test/app/Rakefile +7 -0
- data/test/app/app.rb +62 -0
- data/test/app/app/css/behavior.htc +1 -0
- data/test/app/app/css/js2c.css +494 -0
- data/test/app/app/css/screen.sass +9 -0
- data/test/app/app/css/sqwishable.css +7 -0
- data/test/app/app/css/style.css +3 -0
- data/test/app/app/css/stylus.styl +3 -0
- data/test/app/app/css_stylus/stylus.styl +3 -0
- data/test/app/app/images/background.jpg +1 -0
- data/test/app/app/images/email.png +0 -0
- data/test/app/app/js/_ignoreme.js +1 -0
- data/test/app/app/js/hello.js +1 -0
- data/test/app/app/js/hi.coffee +2 -0
- data/test/app/app/js/ugly.js +7 -0
- data/test/app/app/js_glob/a/b/c1/hello.js +1 -0
- data/test/app/app/js_glob/a/b/c2/hi.js +1 -0
- data/test/app/app/js_glob/a/b/c2/hola.js +1 -0
- data/test/app/app/views/index.haml +1 -0
- data/test/arity_test.rb +26 -0
- data/test/build_test.rb +31 -0
- data/test/cache_test.rb +9 -0
- data/test/compressed_test.rb +30 -0
- data/test/glob_test.rb +42 -0
- data/test/helpers_test.rb +30 -0
- data/test/ignore_test.rb +30 -0
- data/test/img_test.rb +31 -0
- data/test/local_file_test.rb +21 -0
- data/test/mime_type_test.rb +33 -0
- data/test/non_existent_test.rb +45 -0
- data/test/options_test.rb +21 -0
- data/test/order_test.rb +20 -0
- data/test/preproc_test.rb +28 -0
- data/test/redundant_test.rb +11 -0
- data/test/simplecss_test.rb +16 -0
- data/test/sqwish_test.rb +31 -0
- data/test/stylus_test.rb +23 -0
- data/test/template_cache_test.rb +29 -0
- data/test/test_helper.rb +46 -0
- data/test/tilt_test.rb +11 -0
- data/test/uglifier_test.rb +23 -0
- data/test/unit_test.rb +109 -0
- data/test/yui_test.rb +22 -0
- metadata +276 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
# Class methods that will be given to the Sinatra application.
|
4
|
+
module ClassMethods
|
5
|
+
# Sets asset options, or gets them
|
6
|
+
def assets(&blk)
|
7
|
+
@options ||= Options.new(self, &blk)
|
8
|
+
self.assets_initialize! if block_given?
|
9
|
+
|
10
|
+
@options
|
11
|
+
end
|
12
|
+
|
13
|
+
def assets_initialize!
|
14
|
+
add_compressed_routes!
|
15
|
+
add_individual_routes!
|
16
|
+
|
17
|
+
# Cache the built files.
|
18
|
+
if assets.prebuild && !reload_templates
|
19
|
+
assets.cache! { |file| $stderr.write "** Building #{file}...\n" }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add routes for the compressed versions
|
24
|
+
def add_compressed_routes!
|
25
|
+
assets.packages.each do |name, package|
|
26
|
+
get package.route_regex do
|
27
|
+
mtime, contents = @template_cache.fetch(package.path) {
|
28
|
+
[ package.mtime, package.minify ]
|
29
|
+
}
|
30
|
+
|
31
|
+
content_type package.type
|
32
|
+
last_modified mtime
|
33
|
+
contents
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add the routes for the individual files.
|
39
|
+
def add_individual_routes!
|
40
|
+
assets.served.each do |path, from|
|
41
|
+
get %r{#{"^/#{path}/".squeeze('/')}(.*)$} do |file|
|
42
|
+
fmt = File.extname(file)[1..-1]
|
43
|
+
|
44
|
+
# Sanity checks
|
45
|
+
pass unless AssetPack.supported_formats.include?(fmt)
|
46
|
+
fn = asset_path_for(file, from) or pass
|
47
|
+
|
48
|
+
pass if settings.assets.ignored?("#{path}/#{file}")
|
49
|
+
|
50
|
+
# Send headers
|
51
|
+
content_type fmt.to_sym
|
52
|
+
last_modified File.mtime(fn).to_i
|
53
|
+
expires 86400*30, :public
|
54
|
+
|
55
|
+
format = File.extname(fn)[1..-1]
|
56
|
+
|
57
|
+
if AssetPack.supported_formats.include?(format)
|
58
|
+
# It's a raw file, just send it
|
59
|
+
not_found unless format == fmt
|
60
|
+
|
61
|
+
if fmt == 'css'
|
62
|
+
@template_cache.fetch(fn) { asset_filter_css File.read(fn) }
|
63
|
+
else
|
64
|
+
send_file fn
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# Dynamic file
|
68
|
+
not_found unless AssetPack.tilt_formats[format] == fmt
|
69
|
+
|
70
|
+
@template_cache.fetch(fn) {
|
71
|
+
out = render format.to_sym, File.read(fn)
|
72
|
+
out = asset_filter_css(out) if fmt == 'css'
|
73
|
+
out
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
module Compressor
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Compresses a given string.
|
7
|
+
#
|
8
|
+
# compress File.read('x.js'), :js, :jsmin
|
9
|
+
#
|
10
|
+
def compress(str, type, engine=nil, options={})
|
11
|
+
# Use defaults if no engine is given.
|
12
|
+
return fallback(str, type, options) if engine.nil?
|
13
|
+
|
14
|
+
# Ensure that the engine exists.
|
15
|
+
klass = compressors[[type, engine]]
|
16
|
+
raise Error, "Engine #{engine} (#{type}) doesn't exist." unless klass
|
17
|
+
|
18
|
+
# Ensure that the engine can support that type.
|
19
|
+
engine = klass.new
|
20
|
+
raise Error, "#{klass} does not support #{type.upcase} compression." unless engine.respond_to?(type)
|
21
|
+
|
22
|
+
# Build it using the engine, and fallback to defaults if it fails.
|
23
|
+
output = engine.send type, str, options
|
24
|
+
output ||= fallback(str, type, options) unless options[:no_fallback]
|
25
|
+
output
|
26
|
+
end
|
27
|
+
|
28
|
+
# Compresses a given string using the default engines.
|
29
|
+
def fallback(str, type, options)
|
30
|
+
if type == :js
|
31
|
+
compress str, :js, :jsmin, :no_fallback => true
|
32
|
+
elsif type == :css
|
33
|
+
compress str, :css, :simple, :no_fallback => true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def compressors
|
38
|
+
@compressors ||= Hash.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def register(type, engine, meth)
|
42
|
+
compressors[[type, engine]] = meth
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/simple"
|
47
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/jsmin"
|
48
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/yui"
|
49
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/sass"
|
50
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/sqwish"
|
51
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/closure"
|
52
|
+
require "#{AssetPack::PREFIX}/assetpack/engines/uglify"
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
module Configurator
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def attrib(name)
|
10
|
+
define_method(:"#{name}") { |*a|
|
11
|
+
value = a.first
|
12
|
+
self.instance_variable_set :"@#{name}", value unless value.nil?
|
13
|
+
self.instance_variable_get :"@#{name}"
|
14
|
+
}
|
15
|
+
|
16
|
+
alias_method(:"#{name}=", :"#{name}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
module Css
|
4
|
+
def self.preproc(str, assets)
|
5
|
+
str.gsub(/url\(["']?(.*?)["']?\)/) { |url|
|
6
|
+
path = $1
|
7
|
+
file, options = path.split('?')
|
8
|
+
local = assets.local_file_for file
|
9
|
+
|
10
|
+
url = if local
|
11
|
+
if options.to_s.include?('embed')
|
12
|
+
to_data_uri(local)
|
13
|
+
else
|
14
|
+
BusterHelpers.add_cache_buster(file, local)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
path
|
18
|
+
end
|
19
|
+
|
20
|
+
"url(#{url})"
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.to_data_uri(file)
|
25
|
+
require 'base64'
|
26
|
+
|
27
|
+
data = File.read(file)
|
28
|
+
ext = File.extname(file)
|
29
|
+
mime = Sinatra::Base.mime_type(ext)
|
30
|
+
b64 = Base64.encode64(data).gsub("\n", '')
|
31
|
+
|
32
|
+
"data:#{mime};base64,#{b64}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
# The base class for all CSS/JS compression engines.
|
4
|
+
class Engine
|
5
|
+
# Helper for system files.
|
6
|
+
# Usage: sys('css', string, "sqwish %f")
|
7
|
+
# Returns stdout.
|
8
|
+
def sys(type, str, cmd)
|
9
|
+
t = Tempfile.new ['', ".#{type}"]
|
10
|
+
t.write(str)
|
11
|
+
t.close
|
12
|
+
|
13
|
+
output = `#{cmd.gsub('%f', t.path)}`
|
14
|
+
FileUtils.rm t
|
15
|
+
|
16
|
+
[output, t.path]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sinatra::AssetPack
|
2
|
+
class ClosureEngine < Engine
|
3
|
+
def js(str, options={})
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
response = Net::HTTP.post_form(URI.parse('http://closure-compiler.appspot.com/compile'), {
|
8
|
+
'js_code' => str,
|
9
|
+
'compilation_level' => options[:level] || "ADVANCED_OPTIMIZATIONS",
|
10
|
+
'output_format' => 'text',
|
11
|
+
'output_info' => 'compiled_code'
|
12
|
+
})
|
13
|
+
|
14
|
+
response.body
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Compressor.register :js, :closure, ClosureEngine
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sinatra::AssetPack
|
2
|
+
class SqwishEngine < Engine
|
3
|
+
def css(str, options={})
|
4
|
+
cmd = "#{sqwish_bin} %f "
|
5
|
+
cmd += "--strict" if options[:strict]
|
6
|
+
|
7
|
+
_, input = sys :css, str, cmd
|
8
|
+
output = input.gsub(/\.css/, '.min.css')
|
9
|
+
|
10
|
+
File.read(output)
|
11
|
+
rescue => e
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def sqwish_bin
|
16
|
+
ENV['SQWISH_PATH'] || "sqwish"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Compressor.register :css, :sqwish, SqwishEngine
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sinatra::AssetPack
|
2
|
+
class YuiEngine < Engine
|
3
|
+
def initialize
|
4
|
+
require 'yui/compressor'
|
5
|
+
end
|
6
|
+
|
7
|
+
def js(str, options={})
|
8
|
+
YUI::JavaScriptCompressor.new(options).compress(str)
|
9
|
+
rescue LoadError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def css(str, options={})
|
14
|
+
YUI::CssCompressor.new.compress(str)
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Compressor.register :js, :yui, YuiEngine
|
21
|
+
Compressor.register :css, :yui, YuiEngine
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
# Class: HashArray
|
4
|
+
# A stopgap solution to Ruby 1.8's lack of ordered hashes.
|
5
|
+
#
|
6
|
+
# A HashArray, for all intents and purposes, acts like an array. However, the
|
7
|
+
# common stuff are overloaded to work with hashes.
|
8
|
+
#
|
9
|
+
# ## Basic usage
|
10
|
+
#
|
11
|
+
# #### Creating
|
12
|
+
# You can create a HashArray by passing it an array.
|
13
|
+
#
|
14
|
+
# dict = HashArray.new([
|
15
|
+
# { :good_morning => "Bonjour" },
|
16
|
+
# { :goodbye => "Au revoir" },
|
17
|
+
# { :good_evening => "Bon nuit" }
|
18
|
+
# ])
|
19
|
+
#
|
20
|
+
# #### Converting
|
21
|
+
# You may also use it like so:
|
22
|
+
#
|
23
|
+
# letters = [ { :a => "Aye"}, { :b => "Bee" } ].to_hash_array
|
24
|
+
#
|
25
|
+
# #### Iterating
|
26
|
+
# Now you can use the typical enumerator functions:
|
27
|
+
#
|
28
|
+
# dict.each do |(key, value)|
|
29
|
+
# puts "#{key} is #{value}"
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# #=> :good_morning is "Bonjour"
|
33
|
+
# # :goodbye is "Au revoir"
|
34
|
+
# # :good_evening is "Bon nuit"
|
35
|
+
#
|
36
|
+
class HashArray < Array
|
37
|
+
def self.[](*arr)
|
38
|
+
new arr.each_slice(2).map { |(k, v)| Hash[k, v] }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Works like Hash#values.
|
42
|
+
def values
|
43
|
+
inject([]) { |a, (k, v)| a << v; a }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns everything as a hash.
|
47
|
+
def to_hash
|
48
|
+
inject({}) { |hash, (k, v)| hash[k] = v; hash }
|
49
|
+
end
|
50
|
+
|
51
|
+
def keys
|
52
|
+
inject([]) { |a, (k, v)| a << k; a }
|
53
|
+
end
|
54
|
+
|
55
|
+
[:each, :map, :map!, :reject, :reject!, :select, :select!].each do |meth|
|
56
|
+
send(:define_method, meth) { |*a, &block|
|
57
|
+
if block.respond_to?(:call)
|
58
|
+
super(*a) { |hash| block.call *hash.to_a }
|
59
|
+
else
|
60
|
+
super(*a)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module AssetPack
|
3
|
+
module Helpers
|
4
|
+
def css(*args)
|
5
|
+
show_asset_pack :css, *args
|
6
|
+
end
|
7
|
+
|
8
|
+
def js(*args)
|
9
|
+
show_asset_pack :js, *args
|
10
|
+
end
|
11
|
+
|
12
|
+
def img(src, options={})
|
13
|
+
attrs = { :src => src }.merge(options)
|
14
|
+
|
15
|
+
local = settings.assets.local_file_for src
|
16
|
+
if local
|
17
|
+
i = Image[local]
|
18
|
+
attrs[:src] = BusterHelpers.add_cache_buster(src, local)
|
19
|
+
if i.dimensions?
|
20
|
+
attrs[:width] ||= i.width
|
21
|
+
attrs[:height] ||= i.height
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
"<img#{HtmlHelpers.kv attrs} />"
|
26
|
+
end
|
27
|
+
|
28
|
+
def show_asset_pack(type, *args)
|
29
|
+
names = Array.new
|
30
|
+
while args.first.is_a?(Symbol)
|
31
|
+
names << args.shift
|
32
|
+
end
|
33
|
+
|
34
|
+
options = args.shift if args.first.is_a?(Hash)
|
35
|
+
|
36
|
+
names.map { |name|
|
37
|
+
show_one_asset_pack type, name, (options || Hash.new)
|
38
|
+
}.join "\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
def show_one_asset_pack(type, name, options={})
|
42
|
+
pack = settings.assets.packages["#{name}.#{type}"]
|
43
|
+
return "" unless pack
|
44
|
+
|
45
|
+
if (settings.respond_to? 'compressed_env') ? settings.compressed_env : settings.production?
|
46
|
+
pack.to_production_html options
|
47
|
+
else
|
48
|
+
pack.to_development_html options
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def asset_filter_css(str)
|
53
|
+
Css.preproc str, settings.assets
|
54
|
+
end
|
55
|
+
|
56
|
+
def asset_path_for(file, from)
|
57
|
+
settings.assets.dyn_local_file_for file, from
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|