Skip to content

Commit

Permalink
Image cache busting in CSS, and embeds.
Browse files Browse the repository at this point in the history
  • Loading branch information
rstacruz committed Aug 29, 2011
1 parent 219d32c commit 9fc898b
Show file tree
Hide file tree
Showing 28 changed files with 134 additions and 26 deletions.
10 changes: 10 additions & 0 deletions HISTORY.md
@@ -1,3 +1,13 @@
v0.0.2 - Aug 29, 2011
---------------------

### Added:
* Added the `img` helper.
* Added support for filetypes used in @font-face.

### Fixed:
* The gem now installs the correct dependencies.

v0.0.1 - Aug 29, 2011
---------------------

Expand Down
23 changes: 19 additions & 4 deletions README.md
Expand Up @@ -182,7 +182,22 @@ To show images, use the `img` helper:
This automatically adds width, height, and a cache buster thingie:

``` html
<img src='/images/email.873842.png' width='16' height='16' />
<img src='/images/email.873842.png' width='16' height='16' />
```

In your CSS files, they will automatically be translated as well.

``` css
/* Original: */ .email { background: url(/images/email.png); }
/* Output: */ .email { background: url(/images/email.6783478.png); }
```

Want to embed images as `data:` URI's? Sure! Just add `?embed` at the end of the
URL.

``` css
/* Original: */ .email { background: url(/images/email.png?embed); }
/* Output: */ .email { background: url(data:image/png;base64,NF8dG3I...); }
```

Need to build the files?
Expand Down Expand Up @@ -224,7 +239,7 @@ To do

AssetPack will eventually have:

* Image cache busting
* Image data: embedding
* CDN support
* Cache folder support (best if your app has many workers)
* Refactor *Compressor* module
* CDN (Cloudfront, S3) support
* Better support for Compass sprites
2 changes: 0 additions & 2 deletions example/app/css/screen.sass

This file was deleted.

1 change: 0 additions & 1 deletion example/app/css/style.css

This file was deleted.

1 change: 1 addition & 0 deletions lib/sinatra/assetpack.rb
Expand Up @@ -44,6 +44,7 @@ def self.tilt_formats_reverse
autoload :Package, "#{PREFIX}/assetpack/package"
autoload :Compressor, "#{PREFIX}/assetpack/compressor"
autoload :Image, "#{PREFIX}/assetpack/image"
autoload :Css, "#{PREFIX}/assetpack/css"

Error = Class.new(StandardError)

Expand Down
11 changes: 9 additions & 2 deletions lib/sinatra/assetpack/class_methods.rb
Expand Up @@ -46,11 +46,18 @@ def add_individual_routes!
if AssetPack.supported_formats.include?(format)
# It's a raw file, just send it
not_found unless format == fmt
send_file fn

if fmt == 'css'
asset_filter_css File.read(fn)
else
send_file fn
end
else
# Dynamic file
not_found unless AssetPack.tilt_formats[format] == fmt
render format.to_sym, File.read(fn)
out = render format.to_sym, File.read(fn)
out = asset_filter_css(out) if fmt == 'css'
out
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/sinatra/assetpack/compressor.rb
Expand Up @@ -93,6 +93,7 @@ def closure_js(str, options={})
response.body
end

# For others
def sys(type, str, cmd)
t = Tempfile.new ['', ".#{type}"]
t.write(str)
Expand Down
35 changes: 35 additions & 0 deletions lib/sinatra/assetpack/css.rb
@@ -0,0 +1,35 @@
module Sinatra
module AssetPack
module Css
def self.preproc(str, assets)
str.gsub(/url\(["']?(.*?)["']?\)/) { |url|
file, options = $1.split('?')
local = assets.local_file_for file

url = if local
if options.to_s.include?('embed')
to_data_uri(local)
else
BusterHelpers.add_cache_buster(file, local)
end
else
url
end

"url(#{url})"
}
end

def self.to_data_uri(file)
require 'base64'

data = File.read(file)
ext = File.extname(file)
mime = Sinatra::Base.mime_type(ext)
b64 = Base64.encode64(data).gsub("\n", '')

"data:#{mime};base64,#{b64}"
end
end
end
end
14 changes: 4 additions & 10 deletions lib/sinatra/assetpack/helpers.rb
Expand Up @@ -36,18 +36,12 @@ def show_asset_pack(type, name, options={})
end
end

# These below should be refactored into a new module
def asset_filter_css(str)
Css.preproc str, settings.assets
end

# From a URI path (/js/app.js), get the file for it.
# asset_path_for ('/js/app.js', 'app/js')
def asset_path_for(file, from)
# Remove extension
file = $1 if file =~ /^(.*)(\.[^\.]+)$/

# Remove cache-buster (/js/app.28389.js => /js/app)
file = $1 if file =~ /^(.*)\.[0-9]+$/

Dir[File.join(settings.root, from, "#{file}.*")].first
settings.assets.dyn_local_file_for file, from
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions lib/sinatra/assetpack/options.rb
Expand Up @@ -99,6 +99,19 @@ def local_file_for(path)
end
end

# Returns the local file for a given URI path. (for dynamic files)
# Returns nil if a file is not found.
# TODO: consolidate with local_file_for
def dyn_local_file_for(file, from)
# Remove extension
file = $1 if file =~ /^(.*)(\.[^\.]+)$/

# Remove cache-buster (/js/app.28389.js => /js/app)
file = $1 if file =~ /^(.*)\.[0-9]+$/

Dir[File.join(app.root, from, "#{file}.*")].first
end

def write(path, output)
require 'fileutils'

Expand Down
2 changes: 1 addition & 1 deletion lib/sinatra/assetpack/version.rb
@@ -1,7 +1,7 @@
module Sinatra
module AssetPack
def self.version
"0.0.1"
"0.0.2"
end
end
end
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions example/app.rb → test/app/app.rb
@@ -1,4 +1,4 @@
$:.unshift File.expand_path('../../lib', __FILE__)
$:.unshift File.expand_path('../../../lib', __FILE__)

require 'sinatra/base'
require 'sinatra/assetpack'
Expand All @@ -10,8 +10,8 @@ class Main < Sinatra::Base

register Sinatra::AssetPack

disable :raise_exceptions
enable :dump_errors
enable :raise_errors
disable :show_exceptions

assets {
#serve '/js', from: 'app/js'
Expand Down
File renamed without changes.
9 changes: 9 additions & 0 deletions test/app/app/css/screen.sass
@@ -0,0 +1,9 @@
#background
color: rgba(blue, 0.3)

#email
background: url(/images/email.png)
background-repeat: no-repeat

#sidebar
background: url(/images/email.png?embed)
File renamed without changes.
2 changes: 2 additions & 0 deletions test/app/app/css/style.css
@@ -0,0 +1,2 @@
div { color: red; }
body { background: url(/images/background.jpg); }
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions test/build_test.rb
Expand Up @@ -6,6 +6,7 @@ class BuildTest < UnitTest
end

test "build" do
app.assets.css_compression = :simple
app.assets.build!

assert File.file? File.join(app.root, 'public/js/app.js')
Expand Down
23 changes: 23 additions & 0 deletions test/preproc_test.rb
@@ -0,0 +1,23 @@
require File.expand_path('../test_helper', __FILE__)

class PreprocTest < UnitTest
test "preproc" do
get '/css/screen.css'
assert body =~ %r{email.[0-9]+.png}
end

test "preproc static files" do
get '/css/style.css'
assert body =~ %r{background.[0-9]+.jpg}
end

test "preproc on minify" do
get '/css/application.css'
assert body =~ %r{email.[0-9]+.png}
end

test "embed" do
get '/css/screen.css'
assert body =~ %r{data:image/png;base64,[A-Za-z0-9=/]{100,}}
end
end
2 changes: 1 addition & 1 deletion test/test_helper.rb
Expand Up @@ -4,7 +4,7 @@
require 'yaml'
require 'mocha'

require File.expand_path('../../example/app.rb', __FILE__)
require File.expand_path('../app/app.rb', __FILE__)

class UnitTest < Test::Unit::TestCase
include Rack::Test::Methods
Expand Down
2 changes: 1 addition & 1 deletion test/unit_test.rb
Expand Up @@ -41,7 +41,7 @@ class AppTest < UnitTest

test 'static css' do
get '/css/style.css'
assert body == 'div { color: red; }'
assert body.include?('div { color: red; }')
end

test 'sass' do
Expand Down
2 changes: 1 addition & 1 deletion test/yui_test.rb
Expand Up @@ -9,7 +9,7 @@ class YuiTest < UnitTest

teardown do
app.assets.js_compression = :jsmin
app.assets.css_compression = :sass
app.assets.css_compression = :simple
end

test "build" do
Expand Down

0 comments on commit 9fc898b

Please sign in to comment.