assets_booster 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Rakefile +4 -0
- data/asset_booster.gemspec +6 -1
- data/lib/assets_booster/compiler/closure.rb +8 -6
- data/lib/assets_booster/compiler/dummy.rb +2 -5
- data/lib/assets_booster/compiler/jsmin.rb +138 -148
- data/lib/assets_booster/compiler/rainpress.rb +3 -3
- data/lib/assets_booster/compiler/uglify.rb +6 -4
- data/lib/assets_booster/merger/base.rb +26 -0
- data/lib/assets_booster/merger/css.rb +38 -43
- data/lib/assets_booster/merger/simple.rb +7 -11
- data/lib/assets_booster/package/base.rb +24 -14
- data/lib/assets_booster/package/javascript.rb +3 -7
- data/lib/assets_booster/package/stylesheet.rb +3 -7
- data/lib/assets_booster/packager.rb +102 -11
- data/lib/assets_booster/railtie.rb +5 -3
- data/lib/assets_booster/tasks/tasks.rake +9 -11
- data/lib/assets_booster/version.rb +1 -1
- data/lib/assets_booster/view_helper.rb +3 -6
- data/spec/compiler/base.rb +15 -0
- data/spec/compiler/closure_spec.rb +16 -0
- data/spec/compiler/dummy_spec.rb +16 -0
- data/spec/compiler/jsmin_spec.rb +16 -0
- data/spec/compiler/rainpress_spec.rb +16 -0
- data/spec/compiler/uglify_spec.rb +16 -0
- data/spec/merger/base.rb +31 -0
- data/spec/merger/css_spec.rb +138 -0
- data/spec/merger/simple_spec.rb +18 -0
- data/spec/package/base.rb +26 -0
- data/spec/package/javascript_spec.rb +16 -0
- data/spec/package/stylesheet_spec.rb +16 -0
- data/spec/packager_spec.rb +87 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/view_helper_spec.rb +12 -0
- metadata +69 -7
- data/lib/assets_booster/configuration.rb +0 -104
data/.rspec
ADDED
data/Rakefile
CHANGED
data/asset_booster.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Corin Langosch"]
|
10
10
|
s.email = ["info@netskin.com"]
|
11
|
-
s.homepage = ""
|
11
|
+
s.homepage = "http://github.com/gucki/assets_booster"
|
12
12
|
s.summary = %q{Assets (javascripts, css) compression for rails applications}
|
13
13
|
s.description = %q{Instead of sending down a dozen JavaScript and CSS files full of formatting and comments, this gem makes it simple to merge and compress these into one or more files, increasing speed and saving bandwidth.}
|
14
14
|
|
@@ -18,4 +18,9 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency "rails", "~>3.0.5"
|
23
|
+
s.add_development_dependency "rspec", "~>2.5.0"
|
24
|
+
s.add_development_dependency "rainpress", "~>1.0"
|
21
25
|
end
|
26
|
+
|
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/http'
|
3
|
-
|
4
3
|
module AssetsBooster
|
5
4
|
module Compiler
|
6
5
|
class Closure
|
7
|
-
def
|
6
|
+
def name
|
8
7
|
"Google Closure Compiler"
|
9
8
|
end
|
10
9
|
|
11
|
-
def
|
10
|
+
def compile(code)
|
12
11
|
post_data = {
|
13
12
|
'js_code'=> code,
|
14
13
|
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
@@ -20,15 +19,18 @@ module AssetsBooster
|
|
20
19
|
case res
|
21
20
|
when Net::HTTPSuccess
|
22
21
|
data = res.body.strip
|
22
|
+
if data =~ /^Error\(22\): Too many compiles performed recently./
|
23
|
+
raise RuntimeError, "Google's Closure Compiler complained: "+data
|
24
|
+
end
|
23
25
|
if code.size > 0 && data.size < 1
|
24
26
|
post_data['output_info'] = 'errors'
|
25
27
|
res = Net::HTTP.post_form(uri, post_data)
|
26
|
-
raise
|
28
|
+
raise RuntimeError, "Google's Closure Compiler failed: "+res.body
|
27
29
|
end
|
28
30
|
data
|
29
31
|
else
|
30
|
-
raise
|
31
|
-
end
|
32
|
+
raise RuntimeError, "HTTP request TO Google's Closure Compiler failed: "+res.to_s
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
@@ -37,193 +37,183 @@ module AssetsBooster
|
|
37
37
|
class JSMin
|
38
38
|
|
39
39
|
# class variables
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
@EOF = -1
|
41
|
+
@theA = ""
|
42
|
+
@theB = ""
|
43
|
+
@input = ""
|
44
|
+
@output = ""
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def name
|
50
|
-
"Douglas Crockford's JSMin"
|
51
|
-
end
|
52
|
-
|
53
|
-
def compile(incoming)
|
54
|
-
@@output = StringIO.new("","w")
|
55
|
-
if incoming.is_a? String
|
56
|
-
@@input = StringIO.new(incoming,"r")
|
57
|
-
elsif incoming.kind_of? IO
|
58
|
-
@@input = incoming
|
59
|
-
else
|
60
|
-
raise CompileError.new("JSMin can only compress strings or files.")
|
61
|
-
end
|
62
|
-
jsmin
|
63
|
-
@@output.string
|
64
|
-
end
|
65
|
-
|
46
|
+
def name
|
47
|
+
"Douglas Crockford's JSMin"
|
48
|
+
end
|
66
49
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
|
74
|
-
c == '\\' || c[0].ord > 126)
|
75
|
-
end
|
50
|
+
def compile(code)
|
51
|
+
@input = StringIO.new(code, "r")
|
52
|
+
@output = StringIO.new("", "w")
|
53
|
+
jsmin
|
54
|
+
@output.string.strip
|
55
|
+
end
|
76
56
|
|
77
|
-
|
78
|
-
# the character is a control character, translate it to a space or linefeed.
|
79
|
-
def get()
|
80
|
-
c = @@input.getc
|
81
|
-
return @@EOF if(!c)
|
82
|
-
c = c.chr
|
83
|
-
return c if (c >= " " || c == "\n" || c.unpack("c") == @@EOF)
|
84
|
-
return "\n" if (c == "\r")
|
85
|
-
return " "
|
86
|
-
end
|
57
|
+
protected
|
87
58
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
59
|
+
# isAlphanum -- return true if the character is a letter, digit, underscore,
|
60
|
+
# dollar sign, or non-ASCII character
|
61
|
+
def isAlphanum(c)
|
62
|
+
return false if !c || c == @EOF
|
63
|
+
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
64
|
+
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
|
65
|
+
c == '\\' || c[0].ord > 126)
|
66
|
+
end
|
94
67
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
68
|
+
# get -- return the next character from stdin. Watch out for lookahead. If
|
69
|
+
# the character is a control character, translate it to a space or linefeed.
|
70
|
+
def get()
|
71
|
+
c = @input.getc
|
72
|
+
return @EOF if(!c)
|
73
|
+
c = c.chr
|
74
|
+
return c if (c >= " " || c == "\n" || c.unpack("c") == @EOF)
|
75
|
+
return "\n" if (c == "\r")
|
76
|
+
return " "
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the next character without getting it.
|
80
|
+
def peek()
|
81
|
+
lookaheadChar = @input.getc
|
82
|
+
@input.ungetc(lookaheadChar)
|
83
|
+
return lookaheadChar.chr
|
84
|
+
end
|
85
|
+
|
86
|
+
# mynext -- get the next character, excluding comments.
|
87
|
+
# peek() is used to see if a '/' is followed by a '/' or '*'.
|
88
|
+
def mynext()
|
89
|
+
c = get
|
90
|
+
if (c == "/")
|
91
|
+
if(peek == "/")
|
92
|
+
while(true)
|
93
|
+
c = get
|
94
|
+
if (c <= "\n")
|
95
|
+
return c
|
106
96
|
end
|
107
97
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
when @@EOF
|
118
|
-
raise "Unterminated comment"
|
98
|
+
end
|
99
|
+
if(peek == "*")
|
100
|
+
get
|
101
|
+
while(true)
|
102
|
+
case get
|
103
|
+
when "*"
|
104
|
+
if (peek == "/")
|
105
|
+
get
|
106
|
+
return " "
|
119
107
|
end
|
108
|
+
when @EOF
|
109
|
+
raise "Unterminated comment"
|
120
110
|
end
|
121
111
|
end
|
122
112
|
end
|
123
|
-
return c
|
124
113
|
end
|
114
|
+
return c
|
115
|
+
end
|
125
116
|
|
126
117
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
118
|
+
# action -- do something! What you do is determined by the argument: 1
|
119
|
+
# Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
|
120
|
+
# (Delete A). 3 Get the next B. (Delete B). action treats a string as a
|
121
|
+
# single character. Wow! action recognizes a regular expression if it is
|
122
|
+
# preceded by ( or , or =.
|
123
|
+
def action(a)
|
124
|
+
if(a==1)
|
125
|
+
@output.write $theA
|
126
|
+
end
|
127
|
+
if(a==1 || a==2)
|
128
|
+
$theA = $theB
|
129
|
+
if ($theA == "\'" || $theA == "\"")
|
130
|
+
while (true)
|
131
|
+
@output.write $theA
|
132
|
+
$theA = get
|
133
|
+
break if ($theA == $theB)
|
134
|
+
raise "Unterminated string literal" if ($theA <= "\n")
|
135
|
+
if ($theA == "\\")
|
136
|
+
@output.write $theA
|
141
137
|
$theA = get
|
142
|
-
break if ($theA == $theB)
|
143
|
-
raise "Unterminated string literal" if ($theA <= "\n")
|
144
|
-
if ($theA == "\\")
|
145
|
-
@@output.write $theA
|
146
|
-
$theA = get
|
147
|
-
end
|
148
138
|
end
|
149
139
|
end
|
150
140
|
end
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
141
|
+
end
|
142
|
+
if(a==1 || a==2 || a==3)
|
143
|
+
$theB = mynext
|
144
|
+
if ($theB == "/" && ($theA == "(" || $theA == "," || $theA == "=" ||
|
145
|
+
$theA == ":" || $theA == "[" || $theA == "!" ||
|
146
|
+
$theA == "&" || $theA == "|" || $theA == "?" ||
|
147
|
+
$theA == "{" || $theA == "}" || $theA == ";" ||
|
148
|
+
$theA == "\n"))
|
149
|
+
@output.write $theA
|
150
|
+
@output.write $theB
|
151
|
+
while (true)
|
152
|
+
$theA = get
|
153
|
+
if ($theA == "/")
|
154
|
+
break
|
155
|
+
elsif ($theA == "\\")
|
156
|
+
@output.write $theA
|
161
157
|
$theA = get
|
162
|
-
|
163
|
-
|
164
|
-
elsif ($theA == "\\")
|
165
|
-
@@output.write $theA
|
166
|
-
$theA = get
|
167
|
-
elsif ($theA <= "\n")
|
168
|
-
raise "Unterminated RegExp Literal"
|
169
|
-
end
|
170
|
-
@@output.write $theA
|
158
|
+
elsif ($theA <= "\n")
|
159
|
+
raise "Unterminated RegExp Literal"
|
171
160
|
end
|
172
|
-
$
|
161
|
+
@output.write $theA
|
173
162
|
end
|
163
|
+
$theB = mynext
|
174
164
|
end
|
175
165
|
end
|
166
|
+
end
|
176
167
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
168
|
+
# jsmin -- Copy the input to the output, deleting the characters which are
|
169
|
+
# insignificant to JavaScript. Comments will be removed. Tabs will be
|
170
|
+
# replaced with spaces. Carriage returns will be replaced with linefeeds.
|
171
|
+
# Most spaces and linefeeds will be removed.
|
172
|
+
def jsmin
|
173
|
+
$theA = "\n"
|
174
|
+
action(3)
|
175
|
+
while ($theA != @EOF)
|
176
|
+
case $theA
|
177
|
+
when " "
|
178
|
+
if (isAlphanum($theB))
|
179
|
+
action(1)
|
180
|
+
else
|
181
|
+
action(2)
|
182
|
+
end
|
183
|
+
when "\n"
|
184
|
+
case ($theB)
|
185
|
+
when "{","[","(","+","-"
|
186
|
+
action(1)
|
186
187
|
when " "
|
188
|
+
action(3)
|
189
|
+
else
|
187
190
|
if (isAlphanum($theB))
|
188
191
|
action(1)
|
189
192
|
else
|
190
193
|
action(2)
|
191
194
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
+
end
|
196
|
+
else
|
197
|
+
case ($theB)
|
198
|
+
when " "
|
199
|
+
if (isAlphanum($theA))
|
195
200
|
action(1)
|
196
|
-
when " "
|
197
|
-
action(3)
|
198
201
|
else
|
199
|
-
|
200
|
-
action(1)
|
201
|
-
else
|
202
|
-
action(2)
|
203
|
-
end
|
202
|
+
action(3)
|
204
203
|
end
|
205
|
-
|
206
|
-
case ($
|
207
|
-
when " "
|
204
|
+
when "\n"
|
205
|
+
case ($theA)
|
206
|
+
when "}","]",")","+","-","\"","\\", "'", '"'
|
207
|
+
action(1)
|
208
|
+
else
|
208
209
|
if (isAlphanum($theA))
|
209
210
|
action(1)
|
210
211
|
else
|
211
212
|
action(3)
|
212
213
|
end
|
213
|
-
when "\n"
|
214
|
-
case ($theA)
|
215
|
-
when "}","]",")","+","-","\"","\\", "'", '"'
|
216
|
-
action(1)
|
217
|
-
else
|
218
|
-
if (isAlphanum($theA))
|
219
|
-
action(1)
|
220
|
-
else
|
221
|
-
action(3)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
else
|
225
|
-
action(1)
|
226
214
|
end
|
215
|
+
else
|
216
|
+
action(1)
|
227
217
|
end
|
228
218
|
end
|
229
219
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module AssetsBooster
|
2
2
|
module Compiler
|
3
3
|
class Rainpress
|
4
|
-
def
|
4
|
+
def name
|
5
5
|
'Rainpress'
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def compile(css)
|
9
9
|
require 'rainpress'
|
10
10
|
::Rainpress.compress(css)
|
11
11
|
rescue LoadError => e
|
12
|
-
raise "To use the Rainpress CSS Compressor, please install the rainpress gem first"
|
12
|
+
raise LoadError, "To use the Rainpress CSS Compressor, please install the rainpress gem first"
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module AssetsBooster
|
2
2
|
module Compiler
|
3
3
|
class Uglify
|
4
|
-
def
|
4
|
+
def name
|
5
5
|
'UglifyJS running on Node.js'
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
8
|
+
def compile(code)
|
9
|
+
nodejs = %x[which node].strip
|
10
|
+
raise LoadError, "You need to install node.js in order to compile using UglifyJS." unless nodejs.length > 1
|
11
|
+
np_path = Pathname.new(File.join(File.dirname(__FILE__), 'node-js')).realpath
|
12
|
+
IO.popen("#{nodejs} #{np_path}/uglify.js", "r+") do |io|
|
11
13
|
io.write(code)
|
12
14
|
io.close_write
|
13
15
|
io.read
|