psyho_juicer 1.0.0 → 1.0.7

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.
Files changed (49) hide show
  1. data/Gemfile +11 -0
  2. data/History.txt +33 -0
  3. data/Readme.rdoc +16 -4
  4. data/VERSION +1 -1
  5. data/lib/juicer/asset/path.rb +6 -10
  6. data/lib/juicer/cache_buster.rb +53 -13
  7. data/lib/juicer/command/merge.rb +14 -6
  8. data/lib/juicer/css_cache_buster.rb +6 -4
  9. data/lib/juicer/dependency_resolver/css_dependency_resolver.rb +1 -1
  10. data/lib/juicer/image_embed.rb +18 -20
  11. data/lib/juicer/install/yui_compressor_installer.rb +2 -1
  12. data/lib/juicer/merger/stylesheet_merger.rb +5 -5
  13. data/lib/juicer/minifyer/closure_compiler.rb +1 -1
  14. data/lib/juicer.rb +1 -1
  15. data/lib/psyho_juicer.rb +1 -0
  16. data/test/data/Changelog.txt +10 -0
  17. data/test/data/a.css +3 -0
  18. data/test/data/a.js +5 -0
  19. data/test/data/a1.css +5 -0
  20. data/test/data/b.css +1 -0
  21. data/test/data/b.js +5 -0
  22. data/test/data/b1.css +5 -0
  23. data/test/data/b2.css +5 -0
  24. data/test/data/c1.css +3 -0
  25. data/test/data/css/2.gif +1 -0
  26. data/test/data/css/test.css +15 -0
  27. data/test/data/css/test2.css +1 -0
  28. data/test/data/css/test3.css +12 -0
  29. data/test/data/d1.css +3 -0
  30. data/test/data/images/1.png +1 -0
  31. data/test/data/my_app.js +2 -0
  32. data/test/data/not-ok.js +2 -0
  33. data/test/data/ok.js +3 -0
  34. data/test/data/path_test.css +5 -0
  35. data/test/data/path_test2.css +14 -0
  36. data/test/data/pkg/module/moda.js +2 -0
  37. data/test/data/pkg/module/modb.js +3 -0
  38. data/test/data/pkg/pkg.js +1 -0
  39. data/test/fixtures/git-tracked-file.txt +1 -0
  40. data/test/fixtures/yui-download.html +103 -243
  41. data/test/test_helper.rb +27 -1
  42. data/test/unit/juicer/asset/path_test.rb +7 -1
  43. data/test/unit/juicer/cache_buster_test.rb +95 -5
  44. data/test/unit/juicer/command/util_test.rb +1 -1
  45. data/test/unit/juicer/css_cache_buster_test.rb +127 -32
  46. data/test/unit/juicer/image_embed_test.rb +35 -0
  47. data/test/unit/juicer/merger/stylesheet_merger_test.rb +175 -109
  48. data/test/unit/juicer/minifyer/closure_compressor_test.rb +3 -3
  49. metadata +68 -21
@@ -26,10 +26,17 @@ class CacheBusterTest < Test::Unit::TestCase
26
26
  assert_equal @filename, Juicer::CacheBuster.clean("#@filename?1234567890", nil)
27
27
  end
28
28
 
29
+ should "not destroy numeric file names with no cache buster name" do
30
+ @numeric_filename = "815.gif"
31
+ File.open(@numeric_filename, "w") { |f| f.puts "Testing" }
32
+ assert_equal @numeric_filename, Juicer::CacheBuster.clean("#@numeric_filename?1234567890", nil)
33
+ File.delete(@numeric_filename)
34
+ end
35
+
29
36
  should "remove cache buster query parameters with custom name" do
30
37
  assert_equal @filename, Juicer::CacheBuster.clean("#@filename?cb=1234567890", :cb)
31
38
  end
32
-
39
+
33
40
  should "remove hard cache buster" do
34
41
  assert_equal @filename, Juicer::CacheBuster.clean(@filename.sub(/(\.txt)/, '-jcb1234567890\1'))
35
42
  end
@@ -63,12 +70,47 @@ class CacheBusterTest < Test::Unit::TestCase
63
70
 
64
71
  should "include only mtime when parameter name is nil" do
65
72
  mtime = File.mtime(@filename).to_i
66
- assert_equal "#@filename?#{mtime}", Juicer::CacheBuster.soft(@filename, nil)
73
+ assert_equal "#@filename?#{mtime}", Juicer::CacheBuster.soft(@filename, :parameter => nil)
67
74
  end
68
75
 
69
76
  should "include custom parameter name" do
70
77
  mtime = File.mtime(@filename).to_i
71
- assert_equal "#@filename?juicer=#{mtime}", Juicer::CacheBuster.soft(@filename, :juicer)
78
+ assert_equal "#@filename?juicer=#{mtime}", Juicer::CacheBuster.soft(@filename, :parameter => :juicer)
79
+ end
80
+
81
+ should "use git cache buster if passed as a parameter" do
82
+ revision = revision_of_tracked_file
83
+ file_name = git_tracked_file
84
+ assert_equal "#{file_name}?jcb=#{revision}", Juicer::CacheBuster.soft(file_name, :revision_type => :git)
85
+ end
86
+
87
+ should "use mtime cache buster if passed as a parameter" do
88
+ file_name = git_tracked_file
89
+ revision = File.mtime(file_name).to_i
90
+ assert_equal "#{file_name}?jcb=#{revision}", Juicer::CacheBuster.soft(file_name, :revision_type => :mtime)
91
+ end
92
+ end
93
+
94
+ context "creating rails-style cache busters" do
95
+ should "clean file before adding new cache buster" do
96
+ cache_buster = "1234567890"
97
+ assert_no_match /#{cache_buster}/, Juicer::CacheBuster.rails("#@filename?#{cache_buster}")
98
+ end
99
+
100
+ should "append no cache buster when parameters exist" do
101
+ parameters = "id=1"
102
+ assert_match /#{parameters}/, Juicer::CacheBuster.rails("#@filename?#{parameters}")
103
+ end
104
+
105
+ should "include only mtime as query parameter" do
106
+ mtime = File.mtime(@filename).to_i
107
+ assert_equal "#@filename?#{mtime}", Juicer::CacheBuster.rails(@filename)
108
+ end
109
+
110
+ should "always use mtime cache busters" do
111
+ file_name = git_tracked_file
112
+ revision = Juicer::CacheBuster::Revision.mtime(file_name)
113
+ assert_equal "#{file_name}?#{revision}", Juicer::CacheBuster.rails(file_name, :revision_type => :git)
72
114
  end
73
115
  end
74
116
 
@@ -93,12 +135,60 @@ class CacheBusterTest < Test::Unit::TestCase
93
135
 
94
136
  should "include only mtime when parameter name is nil" do
95
137
  mtime = File.mtime("#@filename.txt").to_i
96
- assert_equal "#@filename-#{mtime}.txt", Juicer::CacheBuster.hard("#@filename.txt", nil)
138
+ assert_equal "#@filename-#{mtime}.txt", Juicer::CacheBuster.hard("#@filename.txt", :parameter => nil)
97
139
  end
98
140
 
99
141
  should "include custom parameter name" do
100
142
  mtime = File.mtime("#@filename.txt").to_i
101
- assert_equal "#@filename-juicer#{mtime}.txt", Juicer::CacheBuster.hard("#@filename.txt", :juicer)
143
+ assert_equal "#@filename-juicer#{mtime}.txt", Juicer::CacheBuster.hard("#@filename.txt", :parameter => :juicer)
144
+ end
145
+
146
+ should "use git cache buster if passed as a paramater" do
147
+ revision = revision_of_tracked_file
148
+ file_name = git_tracked_file
149
+ assert_equal "#{file_name.gsub('.txt', '')}-jcb#{revision}.txt", Juicer::CacheBuster.hard(file_name, :revision_type => :git)
150
+ end
151
+
152
+ should "use mtime cache buster if passed as a parameter" do
153
+ file_name = git_tracked_file
154
+ revision = File.mtime(file_name).to_i
155
+ assert_equal "#{file_name.gsub('.txt', '')}-jcb#{revision}.txt", Juicer::CacheBuster.hard(file_name, :revision_type => :mtime)
102
156
  end
103
157
  end
158
+
159
+ context "Revision" do
160
+ context "mtime" do
161
+ should "return the file modification time" do
162
+ assert_equal File.mtime(@filename).to_i, Juicer::CacheBuster::Revision.mtime(@filename)
163
+ end
164
+ end
165
+
166
+ context "git" do
167
+ should "return the last commit that modified the file if file is tracked by git" do
168
+ # given
169
+ expected_hash = revision_of_tracked_file
170
+ file_name = git_tracked_file
171
+
172
+ # then
173
+ assert_equal expected_hash, Juicer::CacheBuster::Revision.git(file_name)
174
+ end
175
+
176
+ should "return the last commit of the repository if file is not tracked by git" do
177
+ # given
178
+ last_commit = %x[git log -1 --pretty=oneline].split.first
179
+
180
+ # then
181
+ assert_equal last_commit, Juicer::CacheBuster::Revision.git(@filename)
182
+ end
183
+ end
184
+ end
185
+
186
+ def git_tracked_file
187
+ File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'git-tracked-file.txt'))
188
+ end
189
+
190
+ def revision_of_tracked_file
191
+ '98477054651e764b4ece65f417df3eb17eb5de83'
192
+ end
193
+
104
194
  end
@@ -6,7 +6,7 @@ end
6
6
 
7
7
  class TestCommandUtil < Test::Unit::TestCase
8
8
 
9
- CSS_FILES = %w{a.css a1.css b.css b1.css c1.css d1.css path_test.css path_test2.css}
9
+ CSS_FILES = %w{a.css a1.css b.css b1.css b2.css c1.css d1.css path_test.css path_test2.css}
10
10
  JS_FILES = %w{a.js}
11
11
  ALL_FILES = (CSS_FILES + JS_FILES).sort
12
12
 
@@ -11,61 +11,156 @@ class TestCssCacheBuster < Test::Unit::TestCase
11
11
  Juicer::Test::FileSetup.new.create
12
12
  end
13
13
 
14
- def test_find_urls
15
- urls = @buster.urls(path("css/test.css"))
16
- assert_equal 3, urls.length
17
- assert_equal "../a1.css../images/1.png2.gif", urls.collect { |a| a.path }.sort.join.gsub(path("/"), "")
14
+ context "finding urls" do
15
+ should "find all urls" do
16
+ urls = @buster.urls(path("css/test.css"))
17
+ assert_equal 4, urls.length
18
+ assert_equal "../a1.css../images/1.png2.gif2.gif", urls.collect { |a| a.path }.sort.join.gsub(path("/"), "")
19
+ end
18
20
  end
19
21
 
20
- def test_image_references_should_be_updated
21
- file = path("css/test.css")
22
- buster = Juicer::CssCacheBuster.new
23
- buster.save file
22
+ context "image references" do
23
+ should "update image urls" do
24
+ file = path("css/test.css")
25
+ buster = Juicer::CssCacheBuster.new
26
+ buster.save file
24
27
 
25
- File.read(file).scan(/url\(([^\)]*)\)/m).each do |path|
26
- assert_match(/[^\?]*\?jcb=\d+/, path.first)
28
+ File.read(file).scan(/url\(([^\)]*)\)/m).each do |path|
29
+ assert_match(/[^\?]*\?jcb=\d+/, path.first)
30
+ end
27
31
  end
28
- end
29
32
 
30
- def test_absolute_path_without_document_root_should_fail
31
- file = path("css/test2.css")
32
- buster = Juicer::CssCacheBuster.new
33
+ should "not add multiple cache busters" do
34
+ file = path("css/test.css")
35
+ buster = Juicer::CssCacheBuster.new
36
+ buster.save file
37
+
38
+ assert_no_match /2\.gif\?jcb=\d+\?jcb=/, File.read(file)
39
+ end
33
40
 
34
- assert_raise FileNotFoundError do
41
+ should "not add multiple cache busters when asset host cycling is used" do
42
+ file = path("css/test3.css")
43
+ buster = Juicer::CssCacheBuster.new( { :hosts => [ 'http://assets1', 'http://assets2', 'http://assets3' ], :document_root => './test/data' } )
35
44
  buster.save file
45
+
46
+ assert_no_match /2\.gif\?jcb=\d+\?jcb=/, File.read(file)
36
47
  end
48
+
37
49
  end
38
50
 
39
- def test_absolute_path_should_be_resolved_when_document_root_known
40
- file = path("css/test.css")
41
- buster = Juicer::CssCacheBuster.new :document_root => path("")
51
+ context "absolute paths" do
52
+ # should "fail without document root" do
53
+ # file = path("css/test2.css")
54
+ # buster = Juicer::CssCacheBuster.new
42
55
 
43
- assert_nothing_raised do
44
- buster.save file
56
+ # assert_raise FileNotFoundError do
57
+ # buster.save file
58
+ # end
59
+ # end
60
+
61
+ should "resolve with document root" do
62
+ file = path("css/test.css")
63
+ buster = Juicer::CssCacheBuster.new :document_root => path("")
64
+
65
+ assert_nothing_raised do
66
+ buster.save file
67
+ end
68
+
69
+ File.read(file).scan(/url\(([^\)]*)\)/m).each do |path|
70
+ assert_match(/[^\?]*\?jcb=\d+/, path.first)
71
+ end
45
72
  end
73
+ end
46
74
 
47
- File.read(file).scan(/url\(([^\)]*)\)/m).each do |path|
48
- assert_match(/[^\?]*\?jcb=\d+/, path.first)
75
+ context "cache busters" do
76
+ should "add mtime to urls" do
77
+ File.open(path("a2.css"), "w") { |f| f.puts "" }
78
+ file = path("path_test2.css")
79
+ output = path("path_test3.css")
80
+ buster = Juicer::CssCacheBuster.new :document_root => path("")
81
+ buster.save file, output
82
+
83
+ buster.urls(output).each { |url| assert url !~ /(jcb=\d+).*(jcb=\d+)/, url }
49
84
  end
50
85
  end
51
86
 
52
- def test_urls_should_only_have_mtime_appended_once
53
- File.open(path("a2.css"), "w") { |f| f.puts "" }
54
- file = path("path_test2.css")
55
- output = path("path_test3.css")
56
- buster = Juicer::CssCacheBuster.new :document_root => path("")
57
- buster.save file, output
87
+ context "rails cache busters" do
88
+ should "should append mtime to urls" do
89
+ File.open(path("a2.css"), "w") { |f| f.puts "" }
90
+ file = path("path_test2.css")
91
+ output = path("path_test3.css")
92
+ buster = Juicer::CssCacheBuster.new :document_root => path(""), :type => :rails
93
+ buster.save file, output
58
94
 
59
- buster.urls(output).each { |url| assert url !~ /(jcb=\d+).*(jcb=\d+)/, url }
95
+ buster.urls(output).each { |asset| assert_match /\?\d+$/, asset.path }
96
+ end
60
97
  end
98
+
99
+ context "hard cache busters" do
100
+ should "should alter file name" do
101
+ File.open(path("a2.css"), "w") { |f| f.puts "" }
102
+ file = path("path_test2.css")
103
+ output = path("path_test3.css")
104
+ buster = Juicer::CssCacheBuster.new :document_root => path(""), :type => :hard
105
+ buster.save file, output
61
106
 
62
- def test_type_hard_should_produce_hard_buster_urls
107
+ buster.urls(output).each { |asset| assert_match /-jcb\d+\.[a-z]{3}$/, asset.path }
108
+ end
109
+ end
110
+
111
+ def given_a_buster_with_type_hard_and_format(format)
63
112
  File.open(path("a2.css"), "w") { |f| f.puts "" }
113
+ return Juicer::CssCacheBuster.new :document_root => path(""), :type => :hard, :format => format
114
+ end
115
+
116
+ def when_buster_saves_file(buster)
64
117
  file = path("path_test2.css")
65
118
  output = path("path_test3.css")
66
- buster = Juicer::CssCacheBuster.new :document_root => path(""), :type => :hard
67
119
  buster.save file, output
120
+ return output
121
+ end
68
122
 
69
- buster.urls(output).each { |asset| assert_match /-jcb\d+\.[a-z]{3}$/, asset.path }
123
+ def assert_all_urls_match(buster, output, pattern)
124
+ buster.urls(output).each { |asset| assert_match pattern, asset.path }
125
+ end
126
+
127
+ context "cache busters with git format" do
128
+ should "include last git commit touching that file in file name" do
129
+ # given
130
+ Juicer::CacheBuster::Revision.stubs(:git => 'GIT_LAST_COMMIT')
131
+ buster = given_a_buster_with_type_hard_and_format(:git)
132
+
133
+ # when
134
+ output = when_buster_saves_file(buster)
135
+
136
+ # then
137
+ assert_all_urls_match(buster, output, /-jcbGIT_LAST_COMMIT\.[a-z]{3}$/)
138
+ end
139
+ end
140
+
141
+ context "cache busters with mtime format" do
142
+ should "include file modification time in file name" do
143
+ # given
144
+ File.stubs(:mtime => 123)
145
+ buster = given_a_buster_with_type_hard_and_format(:mtime)
146
+
147
+ # when
148
+ output = when_buster_saves_file(buster)
149
+
150
+ # then
151
+ assert_all_urls_match(buster, output, /-jcb123\.[a-z]{3}$/)
152
+ end
153
+ end
154
+
155
+ context "non-existent urls" do
156
+ should "not raise" do
157
+ File.open(path("a2.css"), "w") { |f| f.puts "a { background: url(i/dont/exist.fck); }" }
158
+ file = path("a2.css")
159
+ buster = Juicer::CssCacheBuster.new :document_root => path("")
160
+
161
+ assert_nothing_raised do
162
+ buster.save file
163
+ end
164
+ end
70
165
  end
71
166
  end
@@ -73,6 +73,41 @@ class TestImageEmbed < Test::Unit::TestCase
73
73
  end
74
74
  end
75
75
 
76
+ context "non empty document root" do
77
+ setup do
78
+ @document_root = '/path/to/public/dir'
79
+ @another_embedder = Juicer::ImageEmbed.new(:type => :data_uri, :document_root => @document_root)
80
+ @files = [{ :path => "#{@document_root}/images/custom-file.png", :filename => '/images/custom-file.png', :content => "hello png!" }]
81
+ create_files(@files)
82
+ end
83
+
84
+ should "embed urls with embedder" do
85
+ stylesheets = [{ :path => "#{@document_root}/stylesheets/test_absolute_path.css", :content => "body: { background: url(#{@files.first[:filename]}?embed=true); }" }]
86
+ create_files(stylesheets)
87
+
88
+ @another_embedder.save stylesheets.first[:path]
89
+ css_contents = File.read(stylesheets.first[:path])
90
+
91
+ # encode the image
92
+ image_contents = File.read(@files.first[:path])
93
+ data_uri = Datafy::make_data_uri(image_contents, 'image/png')
94
+
95
+ # make sure the encoded data_uri is present in the stylesheet
96
+ assert css_contents.include?(data_uri)
97
+ end
98
+
99
+ should "not embed urls with embedder" do
100
+ stylesheets = [{ :path => "#{@document_root}/stylesheets/test_absolute_path.css", :content => "body: { background: url(#{@files.first[:filename]}?embed=false); }" }]
101
+ create_files(stylesheets)
102
+
103
+ @another_embedder.save stylesheets.first[:path]
104
+ css_contents = File.read(stylesheets.first[:path])
105
+
106
+ # encode the image
107
+ assert css_contents.include?(@files.first[:filename])
108
+ end
109
+ end
110
+
76
111
  context "with duplicated urls" do
77
112
  setup do
78
113
  @stylesheets = [{