yui-compressor 0.9.0

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