sinatra-minify 0.1.1 → 0.2.0
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/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
|