juicer 1.0.3 → 1.0.4

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.
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