juicer 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 1.0.4 / 2010-05-26
2
+ * Brought to you by Elliott Wood, two-fish.com
3
+ * Added "rails" cache-buster type for cache-buster stamps that are identical
4
+ to those generated by Rails'
5
+ * --all-hosts-local is no longer dependent upon command-line positioning.
6
+ * Ensured that URLs don't receive two cache busters, even when asset
7
+ host cycling is used.
8
+
1
9
  == 1.0.3 / 2010-04-15
2
10
  * Update YUI Compressor installer to find the download link in YUI's updated markup
3
11
  Pacthed by Olle Jonsson (http://ollehost.dk/blog/)
data/Readme.rdoc CHANGED
@@ -4,6 +4,7 @@ Official URL: http://github.com/cjohansen/juicer/tree/master
4
4
  Christian Johansen (http://www.cjohansen.no) and contributors:
5
5
 
6
6
  * Morgan Roderick (http://roderick.dk)
7
+ * Elliott Wood (http://two-fish.com)
7
8
  * Pavel Valodzka (http://github.com/valodzka)
8
9
  * Daniel Stockman (http://evocateur.org/)
9
10
  * Aaron Suggs (http://ktheory.com)
@@ -69,15 +70,19 @@ Juicer supports so-called cache busters. A cache buster is a pattern in a path
69
70
  whose only purpose is to "trick" browsers to redownload a file when it has
70
71
  changed in cases where a far future expires header is used.
71
72
 
72
- There are two types of cache busters; soft ones add a parameter to the URL, like
73
- so: http://assets/images/1.png?cb1234567890, ie the letters "cb" (as in cache
74
- buster) and then the timestamp of the files mtime.
73
+ There are three types of cache busters. Soft cache busters (the default) add a
74
+ parameter to the URL, like so: http://assets/images/1.png?jcb=1234567890, ie the
75
+ letters "jcb" (as in Juicer Cache Buster) and then the timestamp of the files mtime.
76
+
77
+ Rails cache busters are similar, except they leave out the parameter name. This
78
+ mimics the behavior of the image_tag helper in the Ruby on Rails framework, so that
79
+ images called from both CSS and from Rails views will have identical URLs.
75
80
 
76
81
  Unfortunately, the popular web proxy Squid shipped for some time with a default
77
82
  configuration which would not treat a URL as a "new" URL if the only thing changed
78
83
  was the GET parameters. For this reason Juicer provides hard cache busters.
79
84
 
80
- Hard cache busters result in URLs such as: http://assets/images/1-cb1234567890.png,
85
+ Hard cache busters result in URLs such as: http://assets/images/1-jcb1234567890.png,
81
86
  ie URLs that require either renaming of files, or (more conveniently) a web
82
87
  server configuration that will forward URLs to the right files anyway.
83
88
 
@@ -287,6 +292,9 @@ Will tell you about other dependencies that might be missing on your system.
287
292
  * Aaron Suggs (http://ktheory.com)
288
293
  Added support for depending on directories
289
294
  Fixed installers on 1.0 branch
295
+ * Elliott Wood (http://two-fish.com)
296
+ Added "rails" cache buster type
297
+ Fixed issues with asset host cycling
290
298
 
291
299
  == LICENSE:
292
300
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.3
1
+ 1.0.4
data/lib/juicer.rb CHANGED
@@ -3,7 +3,7 @@ require "logger"
3
3
  module Juicer
4
4
 
5
5
  # :stopdoc:
6
- VERSION = '1.0.3'
6
+ VERSION = '1.0.4'
7
7
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
8
8
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
9
9
  LOGGER = Logger.new(STDOUT)
@@ -265,7 +265,7 @@ module Juicer
265
265
  hosts.each do |host|
266
266
  return path if path !~ @@scheme_pattern
267
267
 
268
- path.sub!(%r{^#{host}}, '')
268
+ path = path.sub(%r{^#{host}}, '')
269
269
  end
270
270
 
271
271
  return path
@@ -80,15 +80,18 @@ module Juicer
80
80
  #
81
81
  def self.path(file, type = :soft, parameter = DEFAULT_PARAMETER)
82
82
  return file if file =~ /data:.*;base64/
83
+ type = [:soft, :hard, :rails].include?(type) ? type : :soft
84
+ parameter = nil if type == :rails
83
85
  file = self.clean(file, parameter)
84
86
  filename = file.split("?").first
85
87
  raise ArgumentError.new("#{file} could not be found") unless File.exists?(filename)
86
88
  mtime = File.mtime(filename).to_i
87
- type = [:soft, :hard].include?(type) ? type : :soft
88
89
 
89
90
  if type == :soft
90
91
  parameter = "#{parameter}=".sub(/^=$/, '')
91
92
  return "#{file}#{file.index('?') ? '&' : '?'}#{parameter}#{mtime}"
93
+ elsif type == :rails
94
+ return "#{file}#{file.index('?') ? '' : "?#{mtime}"}"
92
95
  end
93
96
 
94
97
  file.sub(/(\.[^\.]+$)/, "-#{parameter}#{mtime}" + '\1')
@@ -116,16 +119,29 @@ module Juicer
116
119
  self.path(file, :soft, parameter)
117
120
  end
118
121
 
122
+ #
123
+ # Add a Rails-style cache buster to a filename. Results in filenames of the
124
+ # form: <tt>file.suffix?[timestamp]</tt>, ie <tt>images/logo.png?1234567890</tt>
125
+ # which is the format used by Rails' image_tag helper.
126
+ #
127
+ def self.rails(file)
128
+ self.path(file, :rails)
129
+ end
130
+
119
131
  #
120
132
  # Remove cache buster from a URL for a given parameter name. Parameter name is
121
133
  # "cb" by default.
122
134
  #
123
135
  def self.clean(file, parameter = DEFAULT_PARAMETER)
124
- query_param = "#{parameter}".length == 0 ? "" : "#{parameter}="
125
- new_file = file.sub(/#{query_param}\d+&?/, "").sub(/(\?|&)$/, "")
126
- return new_file unless new_file == file
136
+ if "#{parameter}".length == 0
137
+ return file.sub(/\?\d+$/, '')
138
+ else
139
+ query_param = "#{parameter}="
140
+ new_file = file.sub(/#{query_param}\d+&?/, "").sub(/(\?|&)$/, "")
141
+ return new_file unless new_file == file
127
142
 
128
- file.sub(/-#{parameter}\d+(\.\w+)($|\?)/, '\1\2')
143
+ file.sub(/-#{parameter}\d+(\.\w+)($|\?)/, '\1\2')
144
+ end
129
145
  end
130
146
  end
131
147
  end
@@ -68,15 +68,17 @@ the compressor the path should be the path to where the jar file is found.
68
68
  opt.on("-l", "--local-hosts hosts", "Host names that are served from --document-root (can be given cache busters). Comma separated") do |hosts|
69
69
  @local_hosts = hosts.split(",")
70
70
  end
71
- opt.on("", "--all-hosts-local", "Treat all hosts as local (ie served from --document-root") { |t| @local_hosts = @hosts }
71
+ opt.on("", "--all-hosts-local", "Treat all hosts as local (ie served from --document-root)") { @all_hosts_local = true }
72
72
  opt.on("-r", "--relative-urls", "Convert all referenced URLs to relative URLs. Requires --document-root if\n" +
73
73
  (" " * 37) + "absolute URLs are used. Only valid for CSS files") { |t| @relative_urls = true }
74
74
  opt.on("-b", "--absolute-urls", "Convert all referenced URLs to absolute URLs. Requires --document-root.\n" +
75
75
  (" " * 37) + "Works with cycled asset hosts. Only valid for CSS files") { |t| @absolute_urls = true }
76
76
  opt.on("-d", "--document-root dir", "Path to resolve absolute URLs relative to") { |path| @document_root = path }
77
- opt.on("-c", "--cache-buster type", "none, soft or hard. Default is soft, which adds timestamps to referenced\n" +
78
- (" " * 37) + "URLs as query parameters. None leaves URLs untouched and hard alters file names") do |type|
79
- @cache_buster = [:soft, :hard].include?(type.to_sym) ? type.to_sym : nil
77
+ opt.on("-c", "--cache-buster type", "none, soft, rails, or hard. Default is soft, which adds timestamps to\n" +
78
+ (" " * 37) + "reference URLs as query parameters. None leaves URLs untouched, rails adds\n" +
79
+ (" " * 37) + "timestamps in the same format as Rails' image_tag helper, and hard alters\n" +
80
+ (" " * 37) + "file names") do |type|
81
+ @cache_buster = [:soft, :hard, :rails].include?(type.to_sym) ? type.to_sym : nil
80
82
  end
81
83
  opt.on("-e", "--embed-images type", "none or data_uri. Default is none. Data_uri embeds images using Base64 encoding\n" +
82
84
  (" " * 37) + "None leaves URLs untouched. Candiate images must be flagged with '?embed=true to be considered") do |embed|
@@ -92,6 +94,9 @@ the compressor the path should be the path to where the jar file is found.
92
94
  @log.fatal "Please provide atleast one input file"
93
95
  raise SystemExit.new("Please provide atleast one input file")
94
96
  end
97
+
98
+ # Copy hosts to local_hosts if --all-hosts-local was specified
99
+ @local_hosts = @hosts if @all_hosts_local
95
100
 
96
101
  # Figure out which file to output to
97
102
  output = output(files.first)
@@ -58,7 +58,6 @@ module Juicer
58
58
 
59
59
  content.scan(/url\([\s"']*([^\)"'\s]*)[\s"']*\)/m).uniq.collect do |url|
60
60
  url = url.first
61
- puts "URL: #{url}"
62
61
  path = resolve_path(url, dir)
63
62
  content.gsub!(/\([\s"']*#{url}[\s"']*\)/m, "(#{path})") unless path == url
64
63
  end
@@ -62,7 +62,7 @@ module Juicer
62
62
 
63
63
  out_dir = File.dirname(output)
64
64
  FileUtils.mkdir_p(out_dir) unless File.exists?(out_dir)
65
- result = execute(%Q{-jar "#{locate_jar}"#{jar_args} -js_output_file "#{output}" -js "#{file}"})
65
+ execute(%Q{-jar "#{locate_jar}"#{jar_args} --js_output_file "#{output}" --js "#{file}"})
66
66
 
67
67
  File.delete(file) if use_tmp
68
68
  end
@@ -0,0 +1,12 @@
1
+ body {
2
+ background: url(http://assets1/images/1.png);
3
+ }
4
+
5
+ h1 {
6
+ background: url(http://assets2/css/2.gif);
7
+ background: url(http://assets3/a1.css) 0 0 no-repeat;
8
+ }
9
+
10
+ h2 {
11
+ background: url(http://assets2/css/2.gif) no-repeat;
12
+ }
data/test/test_helper.rb CHANGED
@@ -147,6 +147,23 @@ p { background: url(/a2.css); }
147
147
 
148
148
  mkfile(@dir, 'path_test2.css', css)
149
149
 
150
+ css = <<-CSS
151
+ body {
152
+ background: url(http://assets1/images/1.png);
153
+ }
154
+
155
+ h1 {
156
+ background: url(http://assets2/css/2.gif);
157
+ background: url(http://assets3/a1.css) 0 0 no-repeat;
158
+ }
159
+
160
+ h2 {
161
+ background: url(http://assets2/css/2.gif) no-repeat;
162
+ }
163
+ CSS
164
+
165
+ mkfile(css_dir, 'test3.css', css)
166
+
150
167
  mkfile(@dir, 'Changelog.txt', "2008.02.09 | stb-base 1.29\n\nFEATURE: Core | Bla bla bla bla bla\nFEATURE: UI: | Bla bla bla bla bla\n\n\n2008.02.09 | stb-base 1.29\n\nFEATURE: Core | Bla bla bla bla bla\nFEATURE: UI: | Bla bla bla bla bla\n")
151
168
 
152
169
  # my_app.js depends on the pkg directory
@@ -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
@@ -72,6 +79,23 @@ class CacheBusterTest < Test::Unit::TestCase
72
79
  end
73
80
  end
74
81
 
82
+ context "creating rails-style cache busters" do
83
+ should "clean file before adding new cache buster" do
84
+ cache_buster = "1234567890"
85
+ assert_no_match /#{cache_buster}/, Juicer::CacheBuster.rails("#@filename?#{cache_buster}")
86
+ end
87
+
88
+ should "append no cache buster when parameters exist" do
89
+ parameters = "id=1"
90
+ assert_match /#{parameters}/, Juicer::CacheBuster.rails("#@filename?#{parameters}")
91
+ end
92
+
93
+ should "include only mtime as query parameter" do
94
+ mtime = File.mtime(@filename).to_i
95
+ assert_equal "#@filename?#{mtime}", Juicer::CacheBuster.rails(@filename)
96
+ end
97
+ end
98
+
75
99
  context "hard cache busters" do
76
100
  setup { @filename.sub!(/\.txt/, '') }
77
101
  teardown { @filename = "#@filename.txt" }
@@ -37,6 +37,15 @@ class TestCssCacheBuster < Test::Unit::TestCase
37
37
 
38
38
  assert_no_match /2\.gif\?jcb=\d+\?jcb=/, File.read(file)
39
39
  end
40
+
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' } )
44
+ buster.save file
45
+
46
+ assert_no_match /2\.gif\?jcb=\d+\?jcb=/, File.read(file)
47
+ end
48
+
40
49
  end
41
50
 
42
51
  context "absolute paths" do
@@ -75,6 +84,18 @@ class TestCssCacheBuster < Test::Unit::TestCase
75
84
  end
76
85
  end
77
86
 
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
94
+
95
+ buster.urls(output).each { |asset| assert_match /\?\d+$/, asset.path }
96
+ end
97
+ end
98
+
78
99
  context "hard cache busters" do
79
100
  should "should alter file name" do
80
101
  File.open(path("a2.css"), "w") { |f| f.puts "" }
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 3
9
- version: 1.0.3
8
+ - 4
9
+ version: 1.0.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Christian Johansen
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-15 00:00:00 +02:00
17
+ date: 2010-05-31 00:00:00 +02:00
18
18
  default_executable: juicer
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -178,6 +178,7 @@ files:
178
178
  - test/data/css/2.gif
179
179
  - test/data/css/test.css
180
180
  - test/data/css/test2.css
181
+ - test/data/css/test3.css
181
182
  - test/data/d1.css
182
183
  - test/data/images/1.png
183
184
  - test/data/my_app.js