sinatra-minify 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +14 -0
- data/VERSION +1 -1
- data/lib/sinatra/minify.rb +6 -2
- data/lib/sinatra/minify/compressor.rb +54 -0
- data/lib/sinatra/minify/config.rb +34 -0
- data/lib/sinatra/minify/helpers.rb +4 -5
- data/lib/sinatra/minify/package.rb +107 -0
- data/lib/tasks.rake +2 -1
- data/sinatra-minify.gemspec +5 -2
- data/test/fixtures/control/style-default-compressed.css +1 -0
- data/test/fixtures/exampleapp/app.rb +1 -1
- data/test/fixtures/exampleapp/public/js/script-2.js +1 -1
- data/test/test_minify.rb +51 -8
- metadata +7 -4
- data/lib/sinatra/minify/builder.rb +0 -224
data/Rakefile
CHANGED
@@ -50,3 +50,17 @@ Rake::RDocTask.new do |rdoc|
|
|
50
50
|
rdoc.rdoc_files.include('README*')
|
51
51
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
52
|
end
|
53
|
+
|
54
|
+
namespace :minify do
|
55
|
+
desc "Builds the example files in test/fixtures/exampleapp"
|
56
|
+
task :build_example do
|
57
|
+
$:.unshift File.dirname(__FILE__) + '/lib'
|
58
|
+
|
59
|
+
require 'test/fixtures/exampleapp/app'
|
60
|
+
puts "Building..."
|
61
|
+
|
62
|
+
files = Sinatra::Minify::Package.build(App)
|
63
|
+
files.each { |f| puts " * #{File.basename f}" }
|
64
|
+
puts "Construction complete!"
|
65
|
+
end
|
66
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/sinatra/minify.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
+
require 'yaml'
|
3
|
+
require 'forwardable'
|
2
4
|
|
3
5
|
begin
|
4
6
|
require 'jsmin'
|
@@ -8,8 +10,10 @@ end
|
|
8
10
|
|
9
11
|
module Sinatra
|
10
12
|
module Minify
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
+
autoload :Config, 'sinatra/minify/config'
|
14
|
+
autoload :Package, 'sinatra/minify/package'
|
15
|
+
autoload :Compressor, 'sinatra/minify/compressor'
|
16
|
+
autoload :Helpers, 'sinatra/minify/helpers'
|
13
17
|
|
14
18
|
def self.registered( app )
|
15
19
|
app.helpers Helpers
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Minify
|
3
|
+
class Compressor
|
4
|
+
attr :type, :package, :file
|
5
|
+
|
6
|
+
def initialize(type, file, package)
|
7
|
+
@type = type.to_s
|
8
|
+
@file = file
|
9
|
+
@package = package
|
10
|
+
@command = :"minify_#{@type}"
|
11
|
+
|
12
|
+
raise ArgumentError if not ['js', 'css'].include?(type)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Rebuilds the minified .min.* files.
|
16
|
+
def build
|
17
|
+
File.open(file, 'w') { |f| f.write minify(concatenated) }
|
18
|
+
file
|
19
|
+
end
|
20
|
+
|
21
|
+
# Deletes all minified files.
|
22
|
+
def clean
|
23
|
+
File.unlink(file) if File.exists?(file)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
# TODO: decouple this further?
|
28
|
+
def concatenated
|
29
|
+
package.files.map { |f| File.read(f) }.join("\n").strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def minify(src)
|
33
|
+
send @command, src
|
34
|
+
end
|
35
|
+
|
36
|
+
def minify_css(src)
|
37
|
+
src.gsub!(/\s+/, " ")
|
38
|
+
src.gsub!(/\/\*(.*?)\*\//, "")
|
39
|
+
src.gsub!(/\} /, "}\n")
|
40
|
+
src.gsub!(/\n$/, "")
|
41
|
+
src.gsub!(/[ \t]*\{[ \t]*/, "{")
|
42
|
+
src.gsub!(/;[ \t]*\}/, "}")
|
43
|
+
|
44
|
+
src.gsub!(/[ \t]*([,|{|}|>|:|;])[ \t]*/,"\\1") # Tersify
|
45
|
+
src.gsub!(/[ \t]*\n[ \t]*/, "") # Hardcore mode (no NLs)
|
46
|
+
src.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
def minify_js(src)
|
50
|
+
JSMin.minify(src).strip
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Minify
|
3
|
+
class Config
|
4
|
+
def initialize(type, app_class = ::Main)
|
5
|
+
@settings = app_class
|
6
|
+
@type = type.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def js_url
|
10
|
+
@settings.js_url
|
11
|
+
end
|
12
|
+
|
13
|
+
def css_url
|
14
|
+
@settings.css_url
|
15
|
+
end
|
16
|
+
|
17
|
+
def public_dir(*args)
|
18
|
+
root_path(@settings.send("#{@type}_path"), *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def public_url(path)
|
22
|
+
File.join(@settings.send("#{@type}_url"), path).squeeze('/')
|
23
|
+
end
|
24
|
+
|
25
|
+
def root_path(*args)
|
26
|
+
File.join(File.dirname(@settings.app_file), *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def sets
|
30
|
+
YAML.load_file(root_path("config/assets.yml"))[@type]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
module Sinatra
|
2
2
|
module Minify
|
3
3
|
module Helpers
|
4
|
-
def js_assets(
|
5
|
-
|
4
|
+
def js_assets(set)
|
5
|
+
Package.new(:js, set, self.class).html
|
6
6
|
end
|
7
7
|
|
8
|
-
def css_assets(
|
9
|
-
|
8
|
+
def css_assets(set)
|
9
|
+
Package.new(:css, set, self.class).html
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Minify
|
3
|
+
class Package
|
4
|
+
GlobNoMatchError = Class.new(StandardError)
|
5
|
+
|
6
|
+
attr :type, :set, :compressor, :filename
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Deletes all the different packaged and minified files
|
10
|
+
# You may pass in a different application object
|
11
|
+
# e.g.
|
12
|
+
#
|
13
|
+
# Sinatra::Minify::Package.clean(HelloWorld)
|
14
|
+
#
|
15
|
+
# as long as the appication has registered Sinatra::Minify
|
16
|
+
#
|
17
|
+
# The files will be based on config/assets.yml
|
18
|
+
#
|
19
|
+
# See test/fixtures/exampleapp/config/assets.yml for an example
|
20
|
+
def clean(app_class = ::Main)
|
21
|
+
all(:js, app_class).each { |p| p.compressor.clean }
|
22
|
+
all(:css, app_class).each { |p| p.compressor.clean }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Packages and minifies all of the files declared on config/assets.yml
|
26
|
+
#
|
27
|
+
# Returns all of the minified files
|
28
|
+
def build(app_class = ::Main)
|
29
|
+
ret = []
|
30
|
+
ret << all(:js, app_class).map { |p| p.compressor.build }
|
31
|
+
ret << all(:css, app_class).map { |p| p.compressor.build }
|
32
|
+
ret.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def all(type, app_class = ::Main)
|
37
|
+
config = Config.new(type, app_class)
|
38
|
+
config.sets.keys.map do |set|
|
39
|
+
Package.new(type, set, app_class)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
extend Forwardable
|
45
|
+
def_delegators :@config, :public_url, :public_dir, :sets, :js_url, :css_url
|
46
|
+
|
47
|
+
def initialize(type, set, app_class = ::Main)
|
48
|
+
@type = type.to_s
|
49
|
+
@app_class = app_class
|
50
|
+
@set = set
|
51
|
+
@config = Config.new(@type, app_class)
|
52
|
+
@filename = "#{set}.min.#{type}"
|
53
|
+
@compressor = Compressor.new(@type, public_dir(@filename), self)
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def html
|
58
|
+
if @app_class.minify?
|
59
|
+
file = public_dir(filename)
|
60
|
+
@compressor.build unless File.exists?(file)
|
61
|
+
mtime = File.mtime(file).to_i
|
62
|
+
|
63
|
+
asset_include_tag public_url(filename), mtime
|
64
|
+
else
|
65
|
+
enumerate_all_assets
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def files
|
70
|
+
specs = sets[set]
|
71
|
+
globs = Array(specs).map { |s| public_dir(s) }
|
72
|
+
|
73
|
+
globs.map { |glob|
|
74
|
+
list = Dir[glob]
|
75
|
+
|
76
|
+
if list.empty? and glob.include?('*')
|
77
|
+
raise GlobNoMatchError, "The spec `#{glob}` does not match any files."
|
78
|
+
end
|
79
|
+
|
80
|
+
list.empty? ? glob : list
|
81
|
+
}.flatten.uniq
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def enumerate_all_assets
|
86
|
+
files.map { |file|
|
87
|
+
mtime = File.mtime(file).to_i if File.exist?(file)
|
88
|
+
|
89
|
+
asset_include_tag(
|
90
|
+
public_url(file.gsub(/^#{Regexp.escape(public_dir)}/, '')), mtime
|
91
|
+
)
|
92
|
+
}.join("\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
def asset_include_tag(path, mtime)
|
96
|
+
case type
|
97
|
+
when 'js'
|
98
|
+
"<script src='#{js_url}/#{path}?#{mtime}' type='text/javascript'></script>"
|
99
|
+
when 'css'
|
100
|
+
"<link rel='stylesheet' href='#{css_url}/#{path}?#{mtime}' media='screen' />"
|
101
|
+
else
|
102
|
+
raise ArgumentError, "only js/css are supported"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/tasks.rake
CHANGED
data/sinatra-minify.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{sinatra-minify}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["sinefunc"]
|
@@ -22,10 +22,13 @@ Gem::Specification.new do |s|
|
|
22
22
|
"Rakefile",
|
23
23
|
"VERSION",
|
24
24
|
"lib/sinatra/minify.rb",
|
25
|
-
"lib/sinatra/minify/
|
25
|
+
"lib/sinatra/minify/compressor.rb",
|
26
|
+
"lib/sinatra/minify/config.rb",
|
26
27
|
"lib/sinatra/minify/helpers.rb",
|
28
|
+
"lib/sinatra/minify/package.rb",
|
27
29
|
"lib/tasks.rake",
|
28
30
|
"sinatra-minify.gemspec",
|
31
|
+
"test/fixtures/control/style-default-compressed.css",
|
29
32
|
"test/fixtures/exampleapp/app.rb",
|
30
33
|
"test/fixtures/exampleapp/config/assets.yml",
|
31
34
|
"test/fixtures/exampleapp/public/css/style-default.css",
|
@@ -0,0 +1 @@
|
|
1
|
+
div,dl,dt,dd,ul,ol,li,form,fieldset,input,th,td{margin:0;padding:0}pre,p,blockquote,h1,h2,h3,h4,h5,h6{margin:1em 0 1em 0}blockquote{border-left:solid 2px #ccc;padding:0 1em;margin-left:0.2em;color:#888}h1{font-size:1.4em}h2{font-size:1.3em}h3{font-size:1.2em}h4{font-size:1.1em}h5{font-size:0.9em}h6{font-size:0.9em}ol,ul{margin-left:2em}html,body{margin:0;padding:0}body,td,input,textarea{font-family:arial,helvetica,sans-serif;font-size:1em}body{font-size:0.85em;line-height:1.6em;background:#6ba241;color:#333}#all{margin:0 auto;width:auto;position:relative}a{color:#393;text-decoration:none;font-weight:bold;padding:1px}a:visited{color:#393}a:hover{color:#393;text-decoration:underline}a img{border:0}#what-we-do,#who-we-are,#content{overflow:hidden}#header>.c,#what-we-do>.c,#who-we-are>.c,#footer>.c{width:940px;margin:0 auto;overflow:hidden}#header{background:#f8fbf4 url(bg-header.png) center bottom repeat-x;padding:10px 0 130px 0}#what-we-do{background:#f0f6e9;padding:30px 0 0 0}#who-we-are{background:#6ba241 url(bg-stripe.png) center top repeat-x;padding:90px 0 30px 0;color:#d3e3c6;text-shadow:1px 1px #5c843d}#footer{background:#444;padding:50px 0 50px 0;color:#bbb}#header{overflow:hidden}#header #logo{float:left;margin-right:75px;margin-left:75px}#header h1{background:url(text.png) 0 0 no-repeat;width:511px;height:95px;text-indent:-9999px;margin:0;padding:0;display:block;float:left}#contact-header{border-bottom:solid 1px #ccc;margin-bottom:90px;padding:0 0 4px 0;text-align:right;color:#aaa;font-size:0.9em}#contact-header em{margin:0 12px 0 10px;color:#ccc}#contact-header a{color:#888;text-decoration:underline}#contact-header a:hover{color:#6b6}#content h2{float:left;width:240px}#content ul,#content li{margin:0;padding:0;display:block}#what-we-do>.c ul{float:left;width:700px;overflow:hidden}#what-we-do li{float:left;width:340px}#what-we-do li.left{margin-right:20px}#what-we-do li h3{font-size:1.4em}#what-we-do li h4{font-family:georgia,times,serif;font-style:italic;color:#777;font-weight:normal;font-size:1.2em;line-height:1.4em;margin:0.8em 0 0 0}#what-we-do li p{margin:3px 0 2em 0}#what-we-do #do-build,#what-we-do #do-scale{padding-bottom:1.5em}#what-we-do h2{background:url(text.png) 0 -400px no-repeat;height:18px;text-indent:-9999px;margin:0;padding:0;display:block}#what-we-do h3{background:url(text.png) 0 0 no-repeat;height:21px;text-indent:-9999px;margin:0;padding:0;display:block}#do-build h3{background-position:0 -200px}#do-scale h3{background-position:0 -250px}#do-craft h3{background-position:0 -300px}#do-train h3{background-position:0 -350px}#who-we-are>.c div{float:left;width:560px;overflow:hidden}#who-we-are h3{font-size:1.4em;line-height:1.5em;color:white;background:url(text.png) 0 -550px no-repeat;width:521px;height:73px;text-indent:-9999px;margin:0;padding:0;display:block;padding-bottom:7px}#who-we-are h2{background:url(text.png) 0 -450px no-repeat;height:18px;text-indent:-9999px;margin:0;padding:0;display:block}#footer{text-align:center}#footer a{font-size:1.3em;color:white;font-weight:normal}#footer span{display:block;font-size:1.2em;color:#888;margin-bottom:3px;font-family:georgia;font-style:italic}
|
@@ -1 +1 @@
|
|
1
|
-
aoeu =
|
1
|
+
aoeu = 456 /* Test */;
|
data/test/test_minify.rb
CHANGED
@@ -9,10 +9,6 @@ class TestMinify < Test::Unit::TestCase
|
|
9
9
|
last_response.body
|
10
10
|
end
|
11
11
|
|
12
|
-
def builder
|
13
|
-
Sinatra::Minify::Builder.new app
|
14
|
-
end
|
15
|
-
|
16
12
|
should "rock pants off" do
|
17
13
|
get '/'
|
18
14
|
assert_match "Hello", output
|
@@ -23,6 +19,34 @@ class TestMinify < Test::Unit::TestCase
|
|
23
19
|
assert_match /\/js\/script-1.js\?/, output
|
24
20
|
assert_match /\/js\/script-2.js\?/, output
|
25
21
|
end
|
22
|
+
|
23
|
+
describe "Package.all(:js)" do
|
24
|
+
setup do
|
25
|
+
@packages = Sinatra::Minify::Package.send(:all, :js, App)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "have only one package" do
|
29
|
+
assert_equal 1, @packages.size
|
30
|
+
end
|
31
|
+
|
32
|
+
should "return base as the only package" do
|
33
|
+
assert_equal 'base', @packages.first.set
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Package.all(:css)" do
|
38
|
+
setup do
|
39
|
+
@packages = Sinatra::Minify::Package.send(:all, :css, App)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "have only one package" do
|
43
|
+
assert_equal 1, @packages.size
|
44
|
+
end
|
45
|
+
|
46
|
+
should "return base as the only package" do
|
47
|
+
assert_equal 'base', @packages.first.set
|
48
|
+
end
|
49
|
+
end
|
26
50
|
|
27
51
|
describe "In a production environment" do
|
28
52
|
def setup
|
@@ -41,12 +65,31 @@ class TestMinify < Test::Unit::TestCase
|
|
41
65
|
|
42
66
|
describe "Building files" do
|
43
67
|
def setup
|
44
|
-
|
45
|
-
|
68
|
+
Sinatra::Minify::Package.clean(App)
|
69
|
+
Sinatra::Minify::Package.build(App)
|
70
|
+
end
|
71
|
+
|
72
|
+
should "at least create the files" do
|
73
|
+
assert File.exist?(File.dirname(App.app_file) + '/public/js/base.min.js')
|
74
|
+
assert File.exist?(File.dirname(App.app_file) + '/public/css/base.min.css')
|
75
|
+
end
|
76
|
+
|
77
|
+
should "include the css file in base.min.css" do
|
78
|
+
control = File.read('test/fixtures/control/style-default-compressed.css')
|
79
|
+
unknown = File.read(File.dirname(App.app_file) + '/public/css/base.min.css')
|
80
|
+
|
81
|
+
assert unknown.include?(control)
|
46
82
|
end
|
47
83
|
|
48
|
-
should "
|
49
|
-
|
84
|
+
should "include the script-1 file first in base.min.js" do
|
85
|
+
unknown = File.read(File.dirname(App.app_file) + '/public/js/base.min.js')
|
86
|
+
assert_equal 0, unknown.index('aoeu=234;')
|
87
|
+
end
|
88
|
+
|
89
|
+
should "include the script-2 file second in base.min.js" do
|
90
|
+
unknown = File.read(File.dirname(App.app_file) + '/public/js/base.min.js')
|
91
|
+
|
92
|
+
assert_equal 9, unknown.index('aoeu=456;')
|
50
93
|
end
|
51
94
|
end
|
52
95
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- sinefunc
|
@@ -44,10 +44,13 @@ files:
|
|
44
44
|
- Rakefile
|
45
45
|
- VERSION
|
46
46
|
- lib/sinatra/minify.rb
|
47
|
-
- lib/sinatra/minify/
|
47
|
+
- lib/sinatra/minify/compressor.rb
|
48
|
+
- lib/sinatra/minify/config.rb
|
48
49
|
- lib/sinatra/minify/helpers.rb
|
50
|
+
- lib/sinatra/minify/package.rb
|
49
51
|
- lib/tasks.rake
|
50
52
|
- sinatra-minify.gemspec
|
53
|
+
- test/fixtures/control/style-default-compressed.css
|
51
54
|
- test/fixtures/exampleapp/app.rb
|
52
55
|
- test/fixtures/exampleapp/config/assets.yml
|
53
56
|
- test/fixtures/exampleapp/public/css/style-default.css
|
@@ -1,224 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
module Sinatra
|
4
|
-
module Minify
|
5
|
-
class Builder
|
6
|
-
# Returns the root path of the main Sinatra application.
|
7
|
-
# Mimins the root_path functionality of Monk.`
|
8
|
-
def root_path(*args)
|
9
|
-
File.join(File.dirname(settings.app_file), *args)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Deletes all minified files.
|
13
|
-
def clean
|
14
|
-
[:js, :css].each do |type|
|
15
|
-
assets_config(type).keys.each do |set|
|
16
|
-
prefix = type == :js ? settings.js_path : settings.css_path
|
17
|
-
path = root_path File.join(prefix, "#{set}.min.#{type}")
|
18
|
-
File.unlink path if File.exists? path
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Rebuilds the minified .min.js and .min.css files.
|
24
|
-
def build
|
25
|
-
out = []
|
26
|
-
[:js, :css].each do |type|
|
27
|
-
assets_config(type).keys.each do |set|
|
28
|
-
prefix = type == :js ? settings.js_path : settings.css_path
|
29
|
-
path = root_path(prefix, "#{set}.min.#{type}")
|
30
|
-
File.open(path, 'w') { |f| f.write compress(type, set) }
|
31
|
-
out << path
|
32
|
-
end
|
33
|
-
end
|
34
|
-
out
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(app_class = ::Main)
|
38
|
-
@app_class = app_class
|
39
|
-
end
|
40
|
-
|
41
|
-
def settings
|
42
|
-
@app_class
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns the file sets for a given type as defined in the `assets.yml` config file.
|
46
|
-
#
|
47
|
-
# Params:
|
48
|
-
# - `type` (Symbol/string) - Can be either `:js` or `:css`
|
49
|
-
#
|
50
|
-
def assets_config(type)
|
51
|
-
YAML.load_file(root_path("config/assets.yml"))[type.to_s]
|
52
|
-
end
|
53
|
-
|
54
|
-
# Returns HTML code with `<script>` tags to include the scripts in a given `set`.
|
55
|
-
# In a production environment, this tries to include the minified version of the
|
56
|
-
# files. If it doesn't exist, it falls back to the original files.
|
57
|
-
#
|
58
|
-
# Params:
|
59
|
-
# - `set` (String) - The set name, as defined in `config/assets.yml`.
|
60
|
-
#
|
61
|
-
# Example:
|
62
|
-
#
|
63
|
-
# <%= js_assets 'base' %>
|
64
|
-
#
|
65
|
-
def js_assets( set )
|
66
|
-
min_file = root_path(settings.js_path, "#{set}.min.js")
|
67
|
-
min_path = "/#{settings.js_url}/#{set}.min.js".squeeze("/")
|
68
|
-
if settings.minify? and File.exists? min_file
|
69
|
-
mtime = File.mtime(min_file).to_i
|
70
|
-
"<script src='#{min_path}?#{mtime}' type='text/javascript'></script>\n"
|
71
|
-
else
|
72
|
-
js_assets_all set
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def js_assets_all(set)
|
77
|
-
assets(:js, set).map { |script|
|
78
|
-
"<script src='#{script[:url]}' type='text/javascript'></script>"
|
79
|
-
}.join("\n")
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns HTML code with `<link>` tags to include the stylesheets in a given `set`.
|
83
|
-
# In a production environment, this tries to include the minified version of the
|
84
|
-
# files. If it doesn't exist, it falls back to the original files.
|
85
|
-
#
|
86
|
-
# Params:
|
87
|
-
# - `set` (String) - The set name, as defined in `config/assets.yml`.
|
88
|
-
#
|
89
|
-
# Example:
|
90
|
-
#
|
91
|
-
# <%= css_assets 'base' %>
|
92
|
-
#
|
93
|
-
def css_assets( set )
|
94
|
-
min_file = root_path settings.css_path, "#{set}.min.css"
|
95
|
-
min_path = "/#{settings.css_url}/#{set}.min.css".squeeze("/")
|
96
|
-
if settings.minify? and File.exists? min_file
|
97
|
-
mtime = File.mtime(file).to_i
|
98
|
-
"<link rel='stylesheet' href='#{min_path}?#{mtime}' media='screen' />\n"
|
99
|
-
else
|
100
|
-
css_assets_all set
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def css_assets_all(set)
|
105
|
-
assets(:css, set).map { |sheet|
|
106
|
-
"<link rel='stylesheet' href='#{sheet[:url]}' media='screen' />"
|
107
|
-
}.join("\n")
|
108
|
-
end
|
109
|
-
|
110
|
-
# Returns the raw consolidated CSS/JS contents of a given type/set
|
111
|
-
def combine(type, set)
|
112
|
-
assets(type, set).map { |asset| File.open(asset[:path]).read }.join("\n").strip
|
113
|
-
end
|
114
|
-
|
115
|
-
# Returns compressed code
|
116
|
-
def compress(type, set)
|
117
|
-
code = combine(type, set)
|
118
|
-
if type == :js
|
119
|
-
minify_js code
|
120
|
-
elsif type == :css
|
121
|
-
minify_css code
|
122
|
-
else
|
123
|
-
raise ArgumentError, "type should be one of :js or :css"
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def minify_css( src )
|
128
|
-
src.gsub! /\s+/, " "
|
129
|
-
src.gsub! /\/\*(.*?)\*\//, ""
|
130
|
-
src.gsub! /\} /, "}\n"
|
131
|
-
src.gsub! /\n$/, ""
|
132
|
-
src.gsub! /[ \t]*\{[ \t]*/, "{"
|
133
|
-
src.gsub! /;[ \t]*\}/, "}"
|
134
|
-
src.gsub! /[ \t]*([,|{|}|>|:|;])[ \t]*/,"\\1" # Tersify
|
135
|
-
src.gsub! /[ \t]*\n[ \t]*/, "" # Hardcore mode (no NLs)
|
136
|
-
src.strip
|
137
|
-
end
|
138
|
-
|
139
|
-
def minify_js(src)
|
140
|
-
JSMin.minify src
|
141
|
-
end
|
142
|
-
|
143
|
-
|
144
|
-
# Returns the file path of where assets of a certain type are stored.
|
145
|
-
#
|
146
|
-
# Params:
|
147
|
-
# - `type` (Symbol) - Either `:js` or `:css`.
|
148
|
-
#
|
149
|
-
# Example:
|
150
|
-
# get_path :js
|
151
|
-
# # Possible value: "/home/rsc/myproject/public/js"
|
152
|
-
#
|
153
|
-
def get_path(type)
|
154
|
-
path = (type == :js) ? settings.js_path : settings.css_path
|
155
|
-
root_path path.squeeze('/')
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns the URL for a given filename and a type.
|
159
|
-
#
|
160
|
-
# Params:
|
161
|
-
# - `type` (Symbol) - Either `:js` or `:css`.
|
162
|
-
#
|
163
|
-
# Example:
|
164
|
-
# get_url :js, '/path/to/file.js'
|
165
|
-
#
|
166
|
-
def get_url(type, filename)
|
167
|
-
prefix = (type == :js) ? settings.js_url : settings.css_url
|
168
|
-
path = filename.gsub(/^#{Regexp.escape(get_path(type))}/, '')
|
169
|
-
File.join(prefix, path).squeeze('/')
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns a list of assets of a given type for a given set.
|
173
|
-
#
|
174
|
-
# Params:
|
175
|
-
# - `type` (Symbol) - Either `:js` or `:css`.
|
176
|
-
# - `set` (String) - The set name, as defined in `config/assets.yml`.
|
177
|
-
#
|
178
|
-
# Returns:
|
179
|
-
# An array of objects.
|
180
|
-
#
|
181
|
-
# Example:
|
182
|
-
#
|
183
|
-
# puts assets(:js, 'base').to_json
|
184
|
-
# # Possible output:
|
185
|
-
# # [ { 'url': '/js/app.js', 'path': '/home/rsc/projects/assets/public/js/app.js' },
|
186
|
-
# # { 'url': '/js/main.js', 'path': '/home/rsc/projects/assets/public/js/main.js' },
|
187
|
-
# # ...
|
188
|
-
# # ]
|
189
|
-
#
|
190
|
-
# See also:
|
191
|
-
# - js_assets
|
192
|
-
#
|
193
|
-
def assets(type, set)
|
194
|
-
# type is either :js or :css
|
195
|
-
specs = assets_config(type)[set]
|
196
|
-
path = get_path(type)
|
197
|
-
done = []
|
198
|
-
# `specs` will be a list of filespecs. Find all files that
|
199
|
-
# match all specs.
|
200
|
-
[specs].flatten.inject([]) do |ret, spec|
|
201
|
-
filepath = "#{path}/#{spec}"
|
202
|
-
files = Dir[filepath]
|
203
|
-
|
204
|
-
# Add it anyway if it doesn't match anything
|
205
|
-
unless files.any? || done.include?(filepath) || filepath.include?('*')
|
206
|
-
ret << { :url => get_url(type, filepath), :path => filepath }
|
207
|
-
done << filepath
|
208
|
-
end
|
209
|
-
|
210
|
-
files.each do |filename|
|
211
|
-
unless done.include? filename
|
212
|
-
ret << {
|
213
|
-
:url => [get_url(type, filename), File.mtime(filename).to_i].join('?'),
|
214
|
-
:path => filename
|
215
|
-
}
|
216
|
-
done << filename
|
217
|
-
end
|
218
|
-
end
|
219
|
-
ret
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|