sbader-yui-compressor 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "rake/gempackagetask"
3
+ require "rake/rdoctask"
4
+ require "rake/testtask"
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs += ["lib", "test"]
10
+ t.test_files = FileList["test/*_test.rb"]
11
+ t.verbose = true
12
+ end
13
+
14
+ Rake::RDocTask.new do |t|
15
+ t.rdoc_files.include("README.rdoc", "lib/**/*.rb")
16
+ end
17
+
18
+ Rake::GemPackageTask.new(eval(IO.read(File.join(File.dirname(__FILE__), "yui-compressor.gemspec")))) do |pkg|
19
+ pkg.need_zip = true
20
+ pkg.need_tar = true
21
+ end
@@ -0,0 +1,216 @@
1
+ require "popen4"
2
+ require "shellwords"
3
+ require "stringio"
4
+
5
+ module YUI #:nodoc:
6
+ class Compressor
7
+ VERSION = "0.9.6"
8
+
9
+ class Error < StandardError; end
10
+ class OptionError < Error; end
11
+ class RuntimeError < Error; end
12
+
13
+ attr_reader :options
14
+
15
+ def self.default_options #:nodoc:
16
+ { :charset => "utf-8", :line_break => nil }
17
+ end
18
+
19
+ def self.compressor_type #:nodoc:
20
+ raise Error, "create a CssCompressor or JavaScriptCompressor instead"
21
+ end
22
+
23
+ def initialize(options = {}) #:nodoc:
24
+ @options = self.class.default_options.merge(options)
25
+ @command = [path_to_java, "-jar", path_to_jar_file, *(command_option_for_type + command_options)]
26
+ end
27
+
28
+ def command #:nodoc:
29
+ @command.map { |word| Shellwords.escape(word) }.join(" ")
30
+ end
31
+
32
+ # Compress a stream or string of code with YUI Compressor. (A stream is
33
+ # any object that responds to +read+ and +close+ like an IO.) If a block
34
+ # is given, you can read the compressed code from the block's argument.
35
+ # Otherwise, +compress+ returns a string of compressed code.
36
+ #
37
+ # ==== Example: Compress CSS
38
+ # compressor = YUI::CssCompressor.new
39
+ # compressor.compress(<<-END_CSS)
40
+ # div.error {
41
+ # color: red;
42
+ # }
43
+ # div.warning {
44
+ # display: none;
45
+ # }
46
+ # END_CSS
47
+ # # => "div.error{color:red;}div.warning{display:none;}"
48
+ #
49
+ # ==== Example: Compress JavaScript
50
+ # compressor = YUI::JavaScriptCompressor.new
51
+ # compressor.compress('(function () { var foo = {}; foo["bar"] = "baz"; })()')
52
+ # # => "(function(){var foo={};foo.bar=\"baz\"})();"
53
+ #
54
+ # ==== Example: Compress and gzip a file on disk
55
+ # File.open("my.js", "r") do |source|
56
+ # Zlib::GzipWriter.open("my.js.gz", "w") do |gzip|
57
+ # compressor.compress(source) do |compressed|
58
+ # while buffer = compressed.read(4096)
59
+ # gzip.write(buffer)
60
+ # end
61
+ # end
62
+ # end
63
+ # end
64
+ #
65
+ def compress(stream_or_string)
66
+ streamify(stream_or_string) do |stream|
67
+ output = true
68
+ status = POpen4.popen4(command, "b") do |stdout, stderr, stdin, pid|
69
+ begin
70
+ stdin.binmode
71
+ transfer(stream, stdin)
72
+
73
+ if block_given?
74
+ yield stdout
75
+ else
76
+ output = stdout.read
77
+ end
78
+
79
+ rescue Exception => e
80
+ raise RuntimeError, "compression failed"
81
+ end
82
+ end
83
+
84
+ if status.exitstatus.zero?
85
+ output
86
+ else
87
+ raise RuntimeError, "compression failed"
88
+ end
89
+ end
90
+ end
91
+
92
+ private
93
+ def command_options
94
+ options.inject([]) do |command_options, (name, argument)|
95
+ method = begin
96
+ method(:"command_option_for_#{name}")
97
+ rescue NameError
98
+ raise OptionError, "undefined option #{name.inspect}"
99
+ end
100
+
101
+ command_options.concat(method.call(argument))
102
+ end
103
+ end
104
+
105
+ def path_to_java
106
+ options.delete(:java) || "java"
107
+ end
108
+
109
+ def path_to_jar_file
110
+ options.delete(:jar_file) || File.join(File.dirname(__FILE__), *%w".. yuicompressor-2.4.7.jar")
111
+ end
112
+
113
+ def streamify(stream_or_string)
114
+ if stream_or_string.respond_to?(:read)
115
+ yield stream_or_string
116
+ else
117
+ yield StringIO.new(stream_or_string.to_s)
118
+ end
119
+ end
120
+
121
+ def transfer(from_stream, to_stream)
122
+ while buffer = from_stream.read(4096)
123
+ to_stream.write(buffer)
124
+ end
125
+ from_stream.close
126
+ to_stream.close
127
+ end
128
+
129
+ def command_option_for_type
130
+ ["--type", self.class.compressor_type.to_s]
131
+ end
132
+
133
+ def command_option_for_charset(charset)
134
+ ["--charset", charset.to_s]
135
+ end
136
+
137
+ def command_option_for_line_break(line_break)
138
+ line_break ? ["--line-break", line_break.to_s] : []
139
+ end
140
+ end
141
+
142
+ class CssCompressor < Compressor
143
+ def self.compressor_type #:nodoc:
144
+ "css"
145
+ end
146
+
147
+ # Creates a new YUI::CssCompressor for minifying CSS code.
148
+ #
149
+ # Options are:
150
+ #
151
+ # <tt>:charset</tt>:: Specifies the character encoding to use. Defaults to
152
+ # <tt>"utf-8"</tt>.
153
+ # <tt>:line_break</tt>:: By default, CSS will be compressed onto a single
154
+ # line. Use this option to specify the maximum
155
+ # number of characters in each line before a newline
156
+ # is added. If <tt>:line_break</tt> is 0, a newline
157
+ # is added after each CSS rule.
158
+ #
159
+ def initialize(options = {})
160
+ super
161
+ end
162
+ end
163
+
164
+ class JavaScriptCompressor < Compressor
165
+ def self.compressor_type #:nodoc:
166
+ "js"
167
+ end
168
+
169
+ def self.default_options #:nodoc:
170
+ super.merge(
171
+ :munge => false,
172
+ :optimize => true,
173
+ :preserve_semicolons => false
174
+ )
175
+ end
176
+
177
+ # Creates a new YUI::JavaScriptCompressor for minifying JavaScript code.
178
+ #
179
+ # Options are:
180
+ #
181
+ # <tt>:charset</tt>:: Specifies the character encoding to use. Defaults to
182
+ # <tt>"utf-8"</tt>.
183
+ # <tt>:line_break</tt>:: By default, JavaScript will be compressed onto a
184
+ # single line. Use this option to specify the
185
+ # maximum number of characters in each line before a
186
+ # newline is added. If <tt>:line_break</tt> is 0, a
187
+ # newline is added after each JavaScript statement.
188
+ # <tt>:munge</tt>:: Specifies whether YUI Compressor should shorten local
189
+ # variable names when possible. Defaults to +false+.
190
+ # <tt>:optimize</tt>:: Specifies whether YUI Compressor should optimize
191
+ # JavaScript object property access and object literal
192
+ # declarations to use as few characters as possible.
193
+ # Defaults to +true+.
194
+ # <tt>:preserve_semicolons</tt>:: Defaults to +false+. If +true+, YUI
195
+ # Compressor will ensure semicolons exist
196
+ # after each statement to appease tools like
197
+ # JSLint.
198
+ #
199
+ def initialize(options = {})
200
+ super
201
+ end
202
+
203
+ private
204
+ def command_option_for_munge(munge)
205
+ munge ? [] : ["--nomunge"]
206
+ end
207
+
208
+ def command_option_for_optimize(optimize)
209
+ optimize ? [] : ["--disable-optimizations"]
210
+ end
211
+
212
+ def command_option_for_preserve_semicolons(preserve_semicolons)
213
+ preserve_semicolons ? ["--preserve-semi"] : []
214
+ end
215
+ end
216
+ end
Binary file
@@ -0,0 +1,114 @@
1
+ require "test/unit"
2
+ require "yui/compressor"
3
+
4
+ module YUI
5
+ class CompressorTest < Test::Unit::TestCase
6
+ FIXTURE_CSS = <<-END_CSS
7
+ div.warning {
8
+ display: none;
9
+ }
10
+
11
+ div.error {
12
+ background: red;
13
+ color: white;
14
+ }
15
+
16
+ @media screen and (max-device-width: 640px) {
17
+ body { font-size: 90%; }
18
+ }
19
+ END_CSS
20
+
21
+ FIXTURE_JS = <<-END_JS
22
+ // here's a comment
23
+ var Foo = { "a": 1 };
24
+ Foo["bar"] = (function(baz) {
25
+ /* here's a
26
+ multiline comment */
27
+ if (false) {
28
+ doSomething();
29
+ } else {
30
+ for (var index = 0; index < baz.length; index++) {
31
+ doSomething(baz[index]);
32
+ }
33
+ }
34
+ })("hello");
35
+ END_JS
36
+
37
+ FIXTURE_ERROR_JS = "var x = {class: 'name'};"
38
+
39
+ def test_compressor_should_raise_when_instantiated
40
+ assert_raises YUI::Compressor::Error do
41
+ YUI::Compressor.new
42
+ end
43
+ end
44
+
45
+ def test_css_should_be_compressed
46
+ @compressor = YUI::CssCompressor.new
47
+ assert_equal "div.warning{display:none}div.error{background:red;color:white}@media screen and (max-device-width:640px){body{font-size:90%}}", @compressor.compress(FIXTURE_CSS)
48
+ end
49
+
50
+ def test_js_should_be_compressed
51
+ @compressor = YUI::JavaScriptCompressor.new
52
+ assert_equal "var Foo={a:1};Foo.bar=(function(baz){if(false){doSomething()}else{for(var index=0;index<baz.length;index++){doSomething(baz[index])}}})(\"hello\");", @compressor.compress(FIXTURE_JS)
53
+ end
54
+
55
+ def test_large_js_should_be_compressed
56
+ assert_nothing_raised do
57
+ @compressor = YUI::JavaScriptCompressor.new
58
+ @compressor.compress(FIXTURE_JS * 200)
59
+ end
60
+ end
61
+
62
+ def test_compress_should_raise_when_an_unknown_option_is_specified
63
+ assert_raises YUI::Compressor::OptionError do
64
+ @compressor = YUI::CssCompressor.new(:foo => "bar")
65
+ @compressor.compress(FIXTURE_JS)
66
+ end
67
+ end
68
+
69
+ def test_compress_should_accept_an_io_argument
70
+ @compressor = YUI::CssCompressor.new
71
+ assert_equal "div.warning{display:none}div.error{background:red;color:white}@media screen and (max-device-width:640px){body{font-size:90%}}", @compressor.compress(StringIO.new(FIXTURE_CSS))
72
+ end
73
+
74
+ def test_compress_should_accept_a_block_and_yield_an_io
75
+ @compressor = YUI::CssCompressor.new
76
+ @compressor.compress(FIXTURE_CSS) do |stream|
77
+ assert_kind_of IO, stream
78
+ assert_equal "div.warning{display:none}div.error{background:red;color:white}@media screen and (max-device-width:640px){body{font-size:90%}}", stream.read
79
+ end
80
+ end
81
+
82
+ def test_line_break_option_should_insert_line_breaks_in_css
83
+ @compressor = YUI::CssCompressor.new(:line_break => 0)
84
+ assert_equal "div.warning{display:none}\ndiv.error{background:red;color:white}\n@media screen and (max-device-width:640px){body{font-size:90%}\n}", @compressor.compress(FIXTURE_CSS)
85
+ end
86
+
87
+ def test_line_break_option_should_insert_line_breaks_in_js
88
+ @compressor = YUI::JavaScriptCompressor.new(:line_break => 0)
89
+ assert_equal "var Foo={a:1};\nFoo.bar=(function(baz){if(false){doSomething()\n}else{for(var index=0;\nindex<baz.length;\nindex++){doSomething(baz[index])\n}}})(\"hello\");", @compressor.compress(FIXTURE_JS)
90
+ end
91
+
92
+ def test_munge_option_should_munge_local_variable_names
93
+ @compressor = YUI::JavaScriptCompressor.new(:munge => true)
94
+ assert_equal "var Foo={a:1};Foo.bar=(function(b){if(false){doSomething()}else{for(var a=0;a<b.length;a++){doSomething(b[a])}}})(\"hello\");", @compressor.compress(FIXTURE_JS)
95
+ end
96
+
97
+ def test_optimize_option_should_not_modify_property_accesses_or_object_literal_keys_when_false
98
+ @compressor = YUI::JavaScriptCompressor.new(:optimize => false)
99
+ assert_equal "var Foo={\"a\":1};Foo[\"bar\"]=(function(baz){if(false){doSomething()}else{for(var index=0;index<baz.length;index++){doSomething(baz[index])}}})(\"hello\");", @compressor.compress(FIXTURE_JS)
100
+ end
101
+
102
+ def test_preserve_semicolons_option_should_preserve_semicolons
103
+ @compressor = YUI::JavaScriptCompressor.new(:preserve_semicolons => true)
104
+ assert_equal "var Foo={a:1};Foo.bar=(function(baz){if(false){doSomething();}else{for(var index=0;index<baz.length;index++){doSomething(baz[index]);}}})(\"hello\");", @compressor.compress(FIXTURE_JS)
105
+ end
106
+
107
+ def test_compress_should_raise_on_javascript_syntax_error
108
+ @compressor = YUI::JavaScriptCompressor.new
109
+ assert_raise YUI::Compressor::RuntimeError do
110
+ @compressor.compress(FIXTURE_ERROR_JS)
111
+ end
112
+ end
113
+ end
114
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sbader-yui-compressor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Stephenson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-03-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: POpen4
16
+ requirement: &70096501390620 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70096501390620
25
+ description: A Ruby interface to YUI Compressor for minifying JavaScript and CSS assets.
26
+ email: sbader2@gmail.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - Rakefile
32
+ - lib/yui/compressor.rb
33
+ - lib/yuicompressor-2.4.7.jar
34
+ - test/compressor_test.rb
35
+ homepage: http://github.com/sbader/ruby-yui-compressor/
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project: yui
55
+ rubygems_version: 1.8.11
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: JavaScript and CSS minification library
59
+ test_files:
60
+ - test/compressor_test.rb