yui-compressor 0.9.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.
@@ -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
+