cjohansen-juicer 0.2.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/History.txt +17 -5
  2. data/Manifest.txt +33 -15
  3. data/Rakefile +22 -1
  4. data/Readme.rdoc +68 -32
  5. data/bin/juicer +1 -0
  6. data/lib/juicer.rb +26 -1
  7. data/lib/juicer/binary.rb +173 -0
  8. data/lib/juicer/cache_buster.rb +45 -0
  9. data/lib/juicer/chainable.rb +1 -0
  10. data/lib/juicer/cli.rb +13 -8
  11. data/lib/juicer/command/install.rb +59 -0
  12. data/lib/juicer/command/list.rb +50 -0
  13. data/lib/juicer/command/merge.rb +130 -31
  14. data/lib/juicer/command/util.rb +32 -0
  15. data/lib/juicer/command/verify.rb +60 -0
  16. data/lib/juicer/core.rb +61 -0
  17. data/lib/juicer/css_cache_buster.rb +106 -0
  18. data/lib/juicer/install/base.rb +186 -0
  19. data/lib/juicer/install/jslint_installer.rb +51 -0
  20. data/lib/juicer/install/rhino_installer.rb +52 -0
  21. data/lib/juicer/install/yui_compressor_installer.rb +66 -0
  22. data/lib/juicer/jslint.rb +90 -0
  23. data/lib/juicer/merger/base.rb +74 -72
  24. data/lib/juicer/merger/dependency_resolver.rb +34 -16
  25. data/lib/juicer/merger/stylesheet_merger.rb +71 -1
  26. data/lib/juicer/minifyer/yui_compressor.rb +20 -43
  27. data/tasks/test/setup.rake +35 -0
  28. data/test/juicer/command/test_install.rb +53 -0
  29. data/test/juicer/command/test_list.rb +69 -0
  30. data/test/juicer/command/test_merge.rb +160 -0
  31. data/test/juicer/command/test_util.rb +54 -0
  32. data/test/juicer/command/test_verify.rb +33 -0
  33. data/test/juicer/install/test_installer_base.rb +195 -0
  34. data/test/juicer/install/test_jslint_installer.rb +54 -0
  35. data/test/juicer/install/test_rhino_installer.rb +57 -0
  36. data/test/juicer/install/test_yui_compressor_installer.rb +56 -0
  37. data/test/juicer/merger/test_base.rb +2 -3
  38. data/test/juicer/merger/test_css_dependency_resolver.rb +8 -4
  39. data/test/juicer/merger/test_javascript_dependency_resolver.rb +6 -7
  40. data/test/juicer/merger/test_javascript_merger.rb +1 -2
  41. data/test/juicer/merger/test_stylesheet_merger.rb +118 -2
  42. data/test/juicer/minifyer/test_yui_compressor.rb +109 -29
  43. data/test/juicer/test_cache_buster.rb +58 -0
  44. data/test/juicer/test_chainable.rb +7 -0
  45. data/test/juicer/test_core.rb +47 -0
  46. data/test/juicer/test_css_cache_buster.rb +91 -0
  47. data/test/juicer/test_jslint.rb +33 -0
  48. data/test/test_helper.rb +65 -196
  49. metadata +77 -26
  50. data/.gitignore +0 -2
  51. data/juicer.gemspec +0 -38
  52. data/lib/juicer/minifyer/compressor.rb +0 -125
  53. data/test/juicer/minifyer/test_compressor.rb +0 -36
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'tempfile'
3
- require File.expand_path(File.join(File.dirname(__FILE__), 'compressor')) unless defined?(Juicer::Minifyer::Compressor)
3
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'binary')) unless defined?(Juicer::Shell::Binary)
4
4
 
5
5
  module Juicer
6
6
  module Minifyer
7
7
 
8
8
  # Provides an interface to the YUI compressor library using
9
- # Juicer::Minify::Compressor. The YUI compressor library is implemented
9
+ # Juicer::Shell::Binary. The YUI compressor library is implemented
10
10
  # using Java, and as such Java is required when running this code. Also, the
11
11
  # YUI jar file has to be provided.
12
12
  #
@@ -28,14 +28,23 @@ module Juicer
28
28
  # License:: MIT
29
29
  #
30
30
  # = Usage example =
31
- # yuic = Juicer::Minifyer::YuiCompressor.new({ :bin_path => '/home/user/java/yui/' })
32
- # yuic.compress('lib.js', 'lib.compressed.js')
31
+ # yuic = Juicer::Minifyer::YuiCompressor.new
32
+ # yuic.java = "/usr/local/bin/java" # If 'java' is not on path
33
+ # yuic.path << "/home/user/java/yui_compressor/"
34
+ # yuic.save("", "")
33
35
  #
34
- class YuiCompressor < Compressor
36
+ #
37
+ class YuiCompressor
38
+ include Juicer::Binary
39
+ include Juicer::Chainable
40
+
35
41
  def initialize(options = {})
36
- super
42
+ bin = options.delete(:java) || "java"
43
+ bin_path = options.delete(:bin_path) || nil
37
44
  @jar = nil
38
- @command = nil
45
+
46
+ super(bin, options)
47
+ path << bin_path if bin_path
39
48
  end
40
49
 
41
50
  # Compresses a file using the YUI Compressor. Note that the :bin_path
@@ -50,16 +59,13 @@ module Juicer
50
59
  # is guessed from the suffix on the input file name
51
60
  def save(file, output = nil, type = nil)
52
61
  type = type.nil? ? file.split('.')[-1].to_sym : type
53
- cmd = @command = @command.nil? || @opt_set || type != @type ? command(type) : @command
54
62
 
55
63
  output ||= file
56
64
  use_tmp = !output.is_a?(String)
57
65
  output = File.join(Dir::tmpdir, File.basename(file) + '.min.tmp.' + type.to_s) if use_tmp
58
66
  FileUtils.mkdir_p(File.dirname(output))
59
67
 
60
- cmd += ' -o "' + output + '" "' + file + '"'
61
- compressor = IO.popen(cmd, 'r')
62
- result = compressor.gets
68
+ result = execute(%Q{-jar "#{locate_jar}" -o "#{output}" "#{file}"})
63
69
 
64
70
  if use_tmp # If no output file is provided, YUI compressor will
65
71
  output.puts IO.read(output) # compress to a temp file. This file should be cleared
@@ -70,22 +76,6 @@ module Juicer
70
76
  chain_method :save
71
77
 
72
78
  private
73
- # Constructs the command to use
74
- def command(type)
75
- @opt_set = false
76
- @type = type
77
- @jar = locate_jar unless @jar
78
- raise 'Unable to locate YUI Compressor Jar' if @jar.nil?
79
- cmd = "#{@options[:java]} -jar #{@jar} --type #{@type}"
80
-
81
- @options.each do |k, v|
82
- v = '' if v == true
83
- v = " #{v}" unless v == '' || v.nil?
84
- cmd += " --#{k.to_s.gsub('_', '-')}#{v}" unless v.nil? || [:bin_path, :java].include?(k)
85
- end
86
-
87
- return cmd
88
- end
89
79
 
90
80
  # Returns a map of options accepted by YUI Compressor, currently:
91
81
  #
@@ -100,8 +90,7 @@ module Juicer
100
90
  # :java (Java command, defaults to 'java')
101
91
  def default_options
102
92
  { :charset => nil, :line_break => nil, :no_munge => nil,
103
- :preserve_semi => nil, :preserve_strings => nil,
104
- :bin_path => nil, :java => 'java' }
93
+ :preserve_semi => nil, :preserve_strings => nil }
105
94
  end
106
95
 
107
96
  # Locates the Jar file by searching directories.
@@ -117,20 +106,8 @@ module Juicer
117
106
  # This means that higher version numbers will be preferred with the default
118
107
  # naming for the YUI Compressor Jars
119
108
  def locate_jar
120
- paths = @options[:bin_path].nil? ? [] : [@options[:bin_path]]
121
- jar = nil
122
-
123
- if ENV.key?('YUIC_HOME') && File.exist?(ENV['YUIC_HOME'])
124
- paths << ENV['YUIC_HOME']
125
- end
126
-
127
- (paths << Dir.pwd).each do |path|
128
- files = Dir.glob(File.join(path, 'yuicompressor*.jar'))
129
- jar = files.sort.last unless files.empty?
130
- break unless jar.nil?
131
- end
132
-
133
- jar.nil? ? nil : File.expand_path(jar)
109
+ files = locate("yuicompressor*.jar", "YUIC_HOME")
110
+ !files || files.empty? ? nil : files.sort.last
134
111
  end
135
112
  end
136
113
 
@@ -0,0 +1,35 @@
1
+ require 'fileutils'
2
+ require 'open-uri'
3
+
4
+ namespace :test do
5
+ desc "Download third party libraries needed to successfully run tests"
6
+ task :setup do
7
+ root = File.join(File.dirname(__FILE__), "../../test/bin")
8
+ yui242 = File.join(root, "yuicompressor-2.4.2.zip")
9
+ jslint = File.join(root, "jslint.js")
10
+ rhino = File.join(root, "rhino1_7R2-RC1.zip")
11
+
12
+ download("http://www.julienlecomte.net/yuicompressor/yuicompressor-2.4.2.zip")
13
+ FileUtils.cp(File.join(root, "yuicompressor-2.4.2.zip"), File.join(root, "yuicompressor-2.3.5.zip"))
14
+ download("http://www.jslint.com/rhino/jslint.js")
15
+ download("ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R1.zip")
16
+ download("ftp://ftp.mozilla.org/pub/mozilla.org/js/rhino1_7R2-RC1.zip")
17
+ download("http://www.julienlecomte.net/yuicompressor/")
18
+ end
19
+ end
20
+
21
+ def download(url)
22
+ filename = File.expand_path(File.join(File.dirname(__FILE__), "../../test/bin", File.basename(url)))
23
+ return filename if File.exists?(filename)
24
+
25
+ puts "Downloading #{url} to #{filename}"
26
+ FileUtils.mkdir_p(File.dirname(filename))
27
+
28
+ File.open(filename, "wb") do |file|
29
+ webpage = open(url)
30
+ file.write(webpage.read)
31
+ webpage.close
32
+ end
33
+
34
+ filename
35
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. test_helper])) unless defined?(Juicer)
2
+
3
+ class TestInstallCommand < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @io = StringIO.new
7
+ @command = Juicer::Command::Install.new(Logger.new(@io))
8
+ FileUtils.rm_rf(path(".juicer/lib")) if File.exists?(path(".juicer/lib"))
9
+ end
10
+
11
+ def test_default_version_should_bring_up_latest_from_installer
12
+ assert_equal "1.0", @command.version(Juicer::Install::JSLintInstaller.new)
13
+ end
14
+
15
+ def test_explicit_version_should_not_be_overridden
16
+ @command.instance_eval { @version = "1.0.1" }
17
+ assert_equal "1.0.1", @command.version(Juicer::Install::JSLintInstaller.new)
18
+ end
19
+
20
+ def test_execute_should_require_atleast_one_argument
21
+ assert_raise ArgumentError do
22
+ @command.execute
23
+ end
24
+ end
25
+
26
+ def test_install_single_lib
27
+ installer = Juicer::Install::JSLintInstaller.new(path(".juicer"))
28
+ assert !installer.installed?
29
+
30
+ @command.instance_eval { @path = path(".juicer") }
31
+ @command.execute("jslint")
32
+ assert installer.installed?
33
+ end
34
+
35
+ def test_install_already_installed_lib
36
+ installer = Juicer::Install::JSLintInstaller.new(path(".juicer"))
37
+ installer.install
38
+ assert installer.installed?
39
+
40
+ @command.execute("jslint")
41
+ assert_match(/is already installed in/, @io.string)
42
+ end
43
+
44
+ def test_install_specific_version
45
+ installer = Juicer::Install::JSLintInstaller.new(path(".juicer"))
46
+ assert !installer.installed?("0.9")
47
+
48
+ @command.instance_eval { @path = path(".juicer") }
49
+ @command.instance_eval { @version = "0.9" }
50
+ @command.execute("jslint")
51
+ assert installer.installed?("0.9")
52
+ end
53
+ end
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. test_helper])) unless defined?(Juicer)
2
+
3
+ class TestListCommand < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @io = StringIO.new
7
+ @command = Juicer::Command::List.new(@io)
8
+ Juicer::Test::FileSetup.new.create
9
+ end
10
+
11
+ def test_list_nothing
12
+ assert_raise ArgumentError do
13
+ @command.execute
14
+ end
15
+
16
+ assert_raise ArgumentError do
17
+ @command.execute []
18
+ end
19
+ end
20
+
21
+ def test_list_css_file
22
+ @command.execute "test/data/a.css"
23
+
24
+ msg = <<-STDOUT
25
+ Dependency chain for test/data/a.css:
26
+ test/data/b.css
27
+ test/data/a.css
28
+
29
+ STDOUT
30
+
31
+ assert_equal msg, @io.string
32
+ end
33
+
34
+ def test_list_js_file
35
+ @command.execute "test/data/a.js"
36
+
37
+ msg = <<-STDOUT
38
+ Dependency chain for test/data/a.js:
39
+ test/data/b.js
40
+ test/data/a.js
41
+
42
+ STDOUT
43
+
44
+ assert_equal msg, @io.string
45
+ end
46
+
47
+ def test_list_several_files
48
+ @command.execute ["test/data/a.js", "test/data/b.js"]
49
+
50
+ msg = <<-STDOUT
51
+ Dependency chain for test/data/a.js:
52
+ test/data/b.js
53
+ test/data/a.js
54
+
55
+ Dependency chain for test/data/b.js:
56
+ test/data/a.js
57
+ test/data/b.js
58
+
59
+ STDOUT
60
+
61
+ assert_equal msg, @io.string
62
+ end
63
+
64
+ def test_list_files_unable_to_guess_type
65
+ assert_raise FileNotFoundError do
66
+ @command.execute("test/data/*.txt")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,160 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. test_helper])) unless defined?(Juicer)
2
+
3
+ class TestMergeCommand < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @io = StringIO.new
7
+ @merge = Juicer::Command::Merge.new(Logger.new(@io))
8
+
9
+ Juicer::Test::FileSetup.new.create
10
+
11
+ ["a.min.css", "not-ok.min.js"].each { |f| File.delete(path(f)) if File.exists?(path(f)) }
12
+
13
+ Juicer.home = path(".juicer")
14
+ installer = Juicer::Install::YuiCompressorInstaller.new(Juicer.home)
15
+ installer.install("2.4.2") unless installer.installed?("2.4.2")
16
+
17
+ installer = Juicer::Install::JSLintInstaller.new(Juicer.home)
18
+ installer.install unless installer.installed?
19
+ end
20
+
21
+ def test_get_minifier_from_nil_minifyer
22
+ @merge.instance_eval { @minifyer = nil }
23
+
24
+ Juicer::Command::Merge.publicize_methods do
25
+ assert_nil @merge.minifyer
26
+ end
27
+ end
28
+
29
+ def test_get_minifier_from_empty_minifyer
30
+ @merge.instance_eval { @minifyer = "" }
31
+
32
+ Juicer::Command::Merge.publicize_methods do
33
+ assert_nil @merge.minifyer
34
+ end
35
+ end
36
+
37
+ def test_get_minifier_from_none_minifyer
38
+ Juicer::Command::Merge.publicize_methods do
39
+ @merge.instance_eval { @minifyer = "none" }
40
+ assert_nil @merge.minifyer
41
+
42
+ @merge.instance_eval { @minifyer = "None" }
43
+ assert_nil @merge.minifyer
44
+
45
+ @merge.instance_eval { @minifyer = "NONE" }
46
+ assert_nil @merge.minifyer
47
+ end
48
+ end
49
+
50
+ def test_get_minifyer
51
+ Juicer::Command::Merge.publicize_methods do
52
+ assert @merge.minifyer.class == Juicer::Minifyer::YuiCompressor
53
+ end
54
+ end
55
+
56
+ def test_output_name_from_file_should_have_suffix_prepended_with_min
57
+ Juicer::Command::Merge.publicize_methods do
58
+ assert_equal File.expand_path("test.min.js"), @merge.output("test.js")
59
+ end
60
+ end
61
+
62
+ def test_output_name_from_nothing_should_be_timestamp
63
+ Juicer::Command::Merge.publicize_methods do
64
+ assert_match(/\d{10}\.min\.tmp/, @merge.output)
65
+ end
66
+ end
67
+
68
+ def test_output_name_instance_value
69
+ Juicer::Command::Merge.publicize_methods do
70
+ @merge.instance_eval { @output = "output.css" }
71
+ assert_equal File.expand_path("output.css"), @merge.output
72
+ assert_equal File.expand_path("output.css"), @merge.output("bleh.css")
73
+ end
74
+ end
75
+
76
+ def test_output_name_should_be_generated_when_output_is_directory
77
+ Juicer::Command::Merge.publicize_methods do
78
+ @merge.instance_eval { @output = path("css") }
79
+ assert_equal File.join(path("css"), "file.min.css"), @merge.output("file.css")
80
+ end
81
+ end
82
+
83
+ def test_merger_from_valid_type
84
+ Juicer::Command::Merge.publicize_methods do
85
+ assert_equal Juicer::Merger::JavaScriptMerger, @merge.merger("bleh.js")
86
+ end
87
+ end
88
+
89
+ def test_merger_from_invalid_type
90
+ Juicer::Command::Merge.publicize_methods do
91
+ assert_equal Juicer::Merger::JavaScriptMerger, @merge.merger("bleh.txt")
92
+ assert_match(/Unknown type 'txt', defaulting to 'js'/, @io.string)
93
+ end
94
+ end
95
+
96
+ def test_merger_from_preset_type
97
+ Juicer::Command::Merge.publicize_methods do
98
+ @merge.instance_eval { @type = "css" }
99
+ assert_equal Juicer::Merger::StylesheetMerger, @merge.merger
100
+ assert_equal Juicer::Merger::StylesheetMerger, @merge.merger("bleh.txt")
101
+ end
102
+ end
103
+
104
+ def test_merge_without_input
105
+ assert_raise SystemExit do
106
+ @merge.execute([])
107
+ end
108
+ end
109
+
110
+ def test_merge_with_bogus_input
111
+ assert_raise SystemExit do
112
+ @merge.execute(["*.css", "bleh/*.js"])
113
+ end
114
+ end
115
+
116
+ def test_unable_to_merge_on_existing_file
117
+ assert_raise SystemExit do
118
+ @merge.instance_eval { @output = path("a.css") }
119
+ @merge.execute(path("a.css"))
120
+ assert_match(/Run again with --force to overwrite/, @io.string)
121
+ end
122
+ end
123
+
124
+ def test_update_output_when_force
125
+ assert_nothing_raised do
126
+ @merge.instance_eval { @force = true }
127
+ @merge.execute(path("a.css"))
128
+ end
129
+ end
130
+
131
+ def test_merge_successful
132
+ begin
133
+ @merge.instance_eval { @output = path("a.min.css") }
134
+ assert @merge.execute(path("a1.css"))
135
+ assert_equal "h2{font-size:10px;}html{background:red;}h1{font-size:12px;}body{width:800px;}", IO.read(path("a.min.css"))
136
+ rescue Test::Unit::AssertionFailedError => err
137
+ raise err
138
+ rescue Exception => err
139
+ puts err.message
140
+ end
141
+ end
142
+
143
+ def test_fail_when_syntax_no_good
144
+ assert_raise SystemExit do
145
+ @merge.execute(path("not-ok.js"))
146
+ assert_match(/Problems were detected during verification/, @io.string)
147
+ assert_no_match(/Ignoring detected problems/, @io.string)
148
+ end
149
+ end
150
+
151
+ def test_ignore_problems
152
+ @merge.instance_eval { @ignore = true }
153
+
154
+ assert_nothing_raised do
155
+ @merge.execute(path("not-ok.js"))
156
+ assert_match(/Problems were detected during verification/, @io.string)
157
+ assert_match(/Ignoring detected problems/, @io.string)
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. test_helper])) unless defined?(Juicer)
2
+
3
+ class Dummy
4
+ include Juicer::Command::Util
5
+ end
6
+
7
+ class TestCommandUtil < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @impl = Dummy.new
11
+ Juicer::Test::FileSetup.new.create
12
+ Dir.glob("test/data/*.min.css").each { |file| File.delete(file) }
13
+ end
14
+
15
+ def test_files_from_single_file
16
+ files = @impl.files("test/data/a.css")
17
+ assert files.is_a?(Array)
18
+ assert_equal "test/data/a.css", files.sort.join
19
+ end
20
+
21
+ def test_files_from_single_glob_pattern
22
+ files = @impl.files("test/data/*.css")
23
+ assert files.is_a?(Array)
24
+ assert_equal %w{a.css a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}.collect { |f| "test/data/#{f}" }.join, files.sort.join
25
+ end
26
+
27
+ def test_files_from_mixed_arguments
28
+ files = @impl.files("test/data/*.css", "test/data/a.js")
29
+ assert files.is_a?(Array)
30
+ assert_equal %w{a.css a.js a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}.collect { |f| "test/data/#{f}" }.join, files.sort.join
31
+ end
32
+
33
+ def test_files_from_array
34
+ files = @impl.files(["test/data/*.css", "test/data/a.js"])
35
+ assert files.is_a?(Array)
36
+ assert_equal %w{a.css a.js a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}.collect { |f| "test/data/#{f}" }.join, files.sort.join
37
+ end
38
+
39
+ def test_relative_path_single_file
40
+ assert_equal "test/data/a.css", @impl.relative("test/data/a.css")
41
+ end
42
+
43
+ def test_relative_path_many_files
44
+ files = @impl.relative(Dir.glob("test/data/*.css"))
45
+ assert files.is_a?(Array)
46
+ assert_equal %w{a.css a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}.collect { |f| "test/data/#{f}" }.join, files.sort.join
47
+ end
48
+
49
+ def test_relative_path_many_files_explicit_reference
50
+ files = @impl.relative(Dir.glob("test/data/*.css"), "lib")
51
+ assert files.is_a?(Array)
52
+ assert_equal %w{a.css a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}.collect { |f| "../test/data/#{f}" }.join, files.sort.join
53
+ end
54
+ end