stapler 0.1.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +35 -12
- data/lib/stapler.rb +7 -6
- data/lib/stapler/asset.rb +37 -0
- data/lib/stapler/bundle.rb +41 -0
- data/lib/stapler/bundle/key.rb +12 -0
- data/lib/stapler/bundle/serialized_key.rb +38 -0
- data/lib/stapler/config.rb +57 -4
- data/lib/stapler/middleware.rb +49 -14
- data/lib/stapler/rails.rb +65 -0
- data/lib/stapler/utils.rb +54 -0
- metadata +34 -57
- data/lib/stapler/compressor.rb +0 -40
- data/lib/stapler/helper.rb +0 -35
- data/lib/stapler/stapler.rb +0 -33
data/README.md
CHANGED
@@ -1,31 +1,54 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
== DESCRIPTION:
|
1
|
+
Stapler
|
2
|
+
=======
|
4
3
|
|
5
4
|
Stapler uses YUI Compressor to automatically compress cached assets in Rails
|
6
5
|
|
7
|
-
== FEATURES/PROBLEMS:
|
8
6
|
|
9
|
-
|
7
|
+
Features/Problems
|
8
|
+
-----------------
|
9
|
+
|
10
|
+
* Support both CSS and Javascript
|
11
|
+
|
10
12
|
|
11
|
-
|
13
|
+
Requirements
|
14
|
+
------------
|
12
15
|
|
13
16
|
* yuicompressor gem
|
14
17
|
|
15
|
-
|
18
|
+
TODO
|
19
|
+
------------
|
20
|
+
|
21
|
+
* warnings if the bundle URL is longer than 2048 chars (for IE)
|
22
|
+
* make sure that Config.secret is not a class variable
|
23
|
+
* logging
|
24
|
+
* specs
|
25
|
+
* pass 404 errors to the underlying app
|
26
|
+
|
27
|
+
Install
|
28
|
+
-------
|
16
29
|
|
17
30
|
* gem install stapler
|
18
31
|
|
19
|
-
== REFERENCES:
|
20
32
|
|
21
|
-
|
22
|
-
|
33
|
+
References
|
34
|
+
----------
|
35
|
+
|
36
|
+
* YUI Compressor - <http://developer.yahoo.com/yui/compressor/>
|
37
|
+
* yuicompressor gem - <http://github.com/mjijackson/yuicompressor>
|
38
|
+
|
39
|
+
|
40
|
+
Author
|
41
|
+
------
|
42
|
+
|
43
|
+
Michael Rykov :: missingfeature.com :: @michaelrykov
|
44
|
+
|
23
45
|
|
24
|
-
|
46
|
+
License
|
47
|
+
-------
|
25
48
|
|
26
49
|
(The MIT License)
|
27
50
|
|
28
|
-
Copyright (c) 2009 Michael Rykov
|
51
|
+
Copyright (c) 2009 Michael Rykov
|
29
52
|
|
30
53
|
Permission is hereby granted, free of charge, to any person obtaining
|
31
54
|
a copy of this software and associated documentation files (the
|
data/lib/stapler.rb
CHANGED
@@ -5,12 +5,13 @@
|
|
5
5
|
$:.unshift File.dirname(__FILE__)
|
6
6
|
|
7
7
|
require 'yuicompressor'
|
8
|
+
require 'stapler/utils'
|
9
|
+
require 'stapler/asset'
|
10
|
+
require 'stapler/bundle/key'
|
11
|
+
require 'stapler/bundle'
|
8
12
|
require 'stapler/config'
|
9
|
-
require 'stapler/compressor'
|
10
|
-
require 'stapler/stapler'
|
11
|
-
require 'stapler/helper'
|
12
13
|
require 'stapler/middleware'
|
13
14
|
|
14
|
-
if defined?(
|
15
|
-
|
16
|
-
end
|
15
|
+
if defined?(Rails)
|
16
|
+
require 'stapler/rails'
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) Michael Rykov
|
3
|
+
#++
|
4
|
+
|
5
|
+
module Stapler
|
6
|
+
class Asset
|
7
|
+
FORBIDDEN_PATHS = %w(.. WEB-INF META-INF)
|
8
|
+
REWRITE_KEYS = %w(PATH_INFO REQUEST_PATH REQUEST_URI)
|
9
|
+
|
10
|
+
def initialize(config, path)
|
11
|
+
@config = config
|
12
|
+
@path = Utils.groom_path(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def response(env)
|
16
|
+
if forbidden?
|
17
|
+
@config.rack_file.not_found
|
18
|
+
else
|
19
|
+
@config.rack_file.call(rewrite_env(env))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def forbidden?
|
25
|
+
FORBIDDEN_PATHS.any? { |fp| @path.include?(fp) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def rewrite_env(env)
|
29
|
+
out = env.dup
|
30
|
+
REWRITE_KEYS.each do |key|
|
31
|
+
next unless out[key].is_a?(String)
|
32
|
+
out[key] = out[key].gsub(@config.path_regex, @path)
|
33
|
+
end
|
34
|
+
out
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) Michael Rykov
|
3
|
+
#++
|
4
|
+
require 'rack/utils'
|
5
|
+
require 'rack/file'
|
6
|
+
|
7
|
+
module Stapler
|
8
|
+
class Bundle
|
9
|
+
def initialize(config, bundle)
|
10
|
+
@config = config
|
11
|
+
@paths = Key.to_paths(bundle.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Collect asset responses and build the collective response
|
15
|
+
def response(env)
|
16
|
+
if not @paths.is_a?(Array)
|
17
|
+
@config.rack_file.not_found
|
18
|
+
else
|
19
|
+
@paths.inject([200, {}, '']) do |sum, path|
|
20
|
+
# Fetch the response
|
21
|
+
item = Stapler::Asset.new(@config, path).response(env)
|
22
|
+
|
23
|
+
# Add the file title
|
24
|
+
sum[2] << "\n\n\n/***** #{path} *****/\n\n\n"
|
25
|
+
|
26
|
+
if item[0] != 200
|
27
|
+
sum[2] << "/* [#{item[0]}] Asset problem */\n\n\n"
|
28
|
+
else
|
29
|
+
# Headers are merged
|
30
|
+
sum[1].merge!(item[1])
|
31
|
+
# Body is concatenated
|
32
|
+
sum[2] << Stapler::Utils.response_body(item)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the response
|
36
|
+
sum
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) Michael Rykov
|
3
|
+
#++
|
4
|
+
|
5
|
+
module Stapler
|
6
|
+
class Bundle
|
7
|
+
class SerializedKey; end
|
8
|
+
@@asset_id = Time.now.to_i.to_s
|
9
|
+
|
10
|
+
class << SerializedKey
|
11
|
+
def to_key(paths)
|
12
|
+
key = Utils.url_encode(paths.map { |a| Utils.groom_path(a) })
|
13
|
+
File.join(key, signature(key), @@asset_id)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_paths(bundle)
|
17
|
+
key, signature = bundle.split(/[\.\/]/, 3)
|
18
|
+
url_decode_with_signature(key, signature)
|
19
|
+
rescue Utils::BadString
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Security signature for URLs #
|
25
|
+
def signature(key)
|
26
|
+
Digest::SHA1.hexdigest("#{key}#{Config.secret}")[0...8]
|
27
|
+
end
|
28
|
+
|
29
|
+
def url_decode_with_signature(key, key_signature)
|
30
|
+
if signature(key.to_s) == key_signature.to_s
|
31
|
+
Utils.url_decode(key)
|
32
|
+
else
|
33
|
+
raise BadString, "Invalid signature"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/stapler/config.rb
CHANGED
@@ -5,11 +5,64 @@
|
|
5
5
|
#++
|
6
6
|
|
7
7
|
module Stapler
|
8
|
-
|
9
|
-
|
8
|
+
class Config
|
9
|
+
PREFIX = 'stapler'
|
10
|
+
WHITELIST_DIR = %w(javascripts stylesheets)
|
11
|
+
WHITELIST_RE = Regexp.union(
|
12
|
+
%r{^/javascripts/.*\.js($|\?)},
|
13
|
+
%r{^/stylesheets/.*\.css($|\?)}
|
14
|
+
)
|
10
15
|
|
11
|
-
|
12
|
-
|
16
|
+
STAPLIZE_RE = Regexp.union(
|
17
|
+
%r{/javascripts/.*\.js($|\?)},
|
18
|
+
%r{/stylesheets/.*\.css($|\?)}
|
19
|
+
)
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def stapleable_path?(source)
|
23
|
+
source =~ WHITELIST_RE
|
24
|
+
end
|
25
|
+
|
26
|
+
def stapleable_dir?(dir)
|
27
|
+
WHITELIST_DIR.include?(dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
def staplize_url(url)
|
31
|
+
url.gsub(STAPLIZE_RE, "/#{PREFIX}\\0")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
DEFAULT_ROOT = defined?(Rails) ? Rails.public_path : 'public'
|
36
|
+
DEFAULT_PREFIX = 'stapler'
|
37
|
+
|
38
|
+
attr_accessor :path_regex, :bundle_regex, :rack_file,
|
39
|
+
:perform_caching, :perform_compress
|
40
|
+
|
41
|
+
def initialize(opts = {})
|
42
|
+
# The root directory of where all the public files are stored
|
43
|
+
asset_dir = opts[:public_path] || DEFAULT_ROOT
|
44
|
+
|
45
|
+
# The path prefix and correcsponding RegEx for stapled assets
|
46
|
+
path_prefix = opts[:path_prefix] || DEFAULT_PREFIX
|
47
|
+
@path_regex = %r(^/#{path_prefix}/(.+)$)
|
48
|
+
@bundle_regex = %r(^/#{path_prefix}/bundle/(.+)$)
|
49
|
+
|
50
|
+
# Rack::File will pull all the source files
|
51
|
+
@rack_file = Rack::File.new(asset_dir)
|
52
|
+
|
53
|
+
# Should we write stapled results and where?
|
54
|
+
@perform_caching = opts[:cache_assets] || false
|
55
|
+
|
56
|
+
# Should we compress stapled results
|
57
|
+
@perform_compress = opts[:compress_assets] || false
|
58
|
+
|
59
|
+
# Bundle URL signature secret
|
60
|
+
@@secret = opts[:secret] || 'nakedemperor'
|
61
|
+
end
|
62
|
+
|
63
|
+
# FIXME: This should not be class variable
|
64
|
+
def self.secret
|
65
|
+
@@secret
|
13
66
|
end
|
14
67
|
end
|
15
68
|
end
|
data/lib/stapler/middleware.rb
CHANGED
@@ -1,35 +1,70 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
require 'rack/file'
|
3
|
+
|
1
4
|
module Stapler
|
2
5
|
class Middleware
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(app)
|
6
|
+
def initialize(app, opts = {})
|
6
7
|
@app = app
|
8
|
+
@config = Stapler::Config.new(opts)
|
7
9
|
end
|
8
10
|
|
9
11
|
def call(env)
|
10
|
-
|
11
|
-
puts "LOOKING AT PATH #{path}"
|
12
|
+
path_info = env["PATH_INFO"]
|
12
13
|
|
13
|
-
if
|
14
|
-
|
14
|
+
if path_info =~ @config.bundle_regex
|
15
|
+
_call_bundle(env, Rack::Utils.unescape($1))
|
16
|
+
elsif path_info =~ @config.path_regex
|
17
|
+
_call_asset(env, Rack::Utils.unescape($1))
|
15
18
|
else
|
16
19
|
@app.call(env)
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
20
23
|
private
|
21
|
-
def
|
22
|
-
|
24
|
+
def _call_bundle(env, path)
|
25
|
+
bundle = Stapler::Bundle.new(@config, path)
|
26
|
+
staple_response(bundle.response(env), "bundle/#{path}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def _call_asset(env, path)
|
30
|
+
assets = Stapler::Asset.new(@config, path)
|
31
|
+
staple_response(assets.response(env), path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def staple_response(response, asset_path)
|
35
|
+
if response[0] == 200
|
36
|
+
# Pull out the body
|
37
|
+
body = Stapler::Utils.response_body(response)
|
38
|
+
|
39
|
+
# Compress the body and update response
|
40
|
+
body = compress(body, asset_path) if @config.perform_compress
|
41
|
+
|
42
|
+
# Prepare the response and headers
|
43
|
+
response[1]['Content-Length'] = Rack::Utils.bytesize(body).to_s
|
44
|
+
response[2] = body
|
45
|
+
|
46
|
+
# Add cache headers for Rack::Cache, etc
|
47
|
+
response[1].merge!(cache_headers(asset_path))
|
48
|
+
end
|
49
|
+
|
50
|
+
response
|
51
|
+
end
|
23
52
|
|
24
|
-
|
25
|
-
|
53
|
+
def compress(content, path)
|
54
|
+
# Compress using the path as an indicator for format
|
55
|
+
if path =~ /\.css$/
|
56
|
+
YUICompressor.compress_css(content)
|
57
|
+
elsif path =~ /\.js$/
|
58
|
+
YUICompressor.compress_js(content, :munge => true)
|
26
59
|
else
|
27
|
-
|
60
|
+
content
|
28
61
|
end
|
29
62
|
end
|
30
63
|
|
31
|
-
def
|
32
|
-
|
64
|
+
def cache_headers(path)
|
65
|
+
duration = @config.perform_caching ? 31536000 : 0
|
66
|
+
{ "Cache-Control" => "public, max-age=#{duration.to_s}",
|
67
|
+
"ETag" => %("#{Digest::SHA1.hexdigest(path)}") }
|
33
68
|
end
|
34
69
|
end
|
35
70
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010 Michael Rykov
|
3
|
+
#++
|
4
|
+
|
5
|
+
module Stapler
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def activate!
|
9
|
+
::ActionView::Base.send(:include, RailsHelper)
|
10
|
+
end
|
11
|
+
|
12
|
+
def asset_host_proc(image_host = nil)
|
13
|
+
prefix = File.join('/', Config::PREFIX)
|
14
|
+
if image_host
|
15
|
+
Proc.new { |source|
|
16
|
+
host = image_host
|
17
|
+
host << prefix if Config.stapleable_path?(source)
|
18
|
+
host
|
19
|
+
}
|
20
|
+
else
|
21
|
+
Proc.new { |source, request|
|
22
|
+
host = request.host_with_port
|
23
|
+
host << prefix if Config.stapleable_path?(source)
|
24
|
+
host
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module RailsHelper
|
31
|
+
def javascript_include_tag(*sources)
|
32
|
+
options = sources.extract_options!.stringify_keys
|
33
|
+
|
34
|
+
if options.delete('cache') || options.delete('concat')
|
35
|
+
recursive = options.delete('recursive')
|
36
|
+
paths = compute_javascript_paths(sources, recursive)
|
37
|
+
javascript_src_tag(Utils.bundle_path(paths), options)
|
38
|
+
else
|
39
|
+
sources << options
|
40
|
+
super(*sources)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def stylesheet_link_tag(*sources)
|
45
|
+
options = sources.extract_options!.stringify_keys
|
46
|
+
|
47
|
+
if options.delete('cache') || options.delete('concat')
|
48
|
+
recursive = options.delete('recursive')
|
49
|
+
paths = compute_stylesheet_paths(sources, recursive)
|
50
|
+
stylesheet_tag(Utils.bundle_path(paths), options)
|
51
|
+
else
|
52
|
+
sources << options
|
53
|
+
super(*sources)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def compute_public_path(src, dir, ext = nil, include_host = true)
|
59
|
+
staple = include_host && src !~ %r{^[-a-z]+://} && Config.stapleable_dir?(dir)
|
60
|
+
path = super(src, dir, ext, include_host)
|
61
|
+
staple ? Config.staplize_url(path) : path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) Michael Rykov
|
3
|
+
#
|
4
|
+
# Some methods copied from Dragonfly gem Serializer
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'base64'
|
8
|
+
require 'cgi'
|
9
|
+
require 'digest/sha1'
|
10
|
+
|
11
|
+
module Stapler
|
12
|
+
module Utils
|
13
|
+
# Exceptions
|
14
|
+
class BadString < RuntimeError; end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def response_body(response)
|
18
|
+
body = response[2]
|
19
|
+
body.is_a?(Rack::File) ? File.read(body.path) : body
|
20
|
+
end
|
21
|
+
|
22
|
+
#### Base64 encoding for URLS ####
|
23
|
+
def b64_encode(string)
|
24
|
+
Base64.encode64(string).tr("\n=",'')
|
25
|
+
end
|
26
|
+
|
27
|
+
def b64_decode(string)
|
28
|
+
padding_length = string.length % 4
|
29
|
+
Base64.decode64(string + '=' * padding_length)
|
30
|
+
end
|
31
|
+
|
32
|
+
def url_encode(object)
|
33
|
+
CGI.escape(b64_encode(Marshal.dump(object)))
|
34
|
+
end
|
35
|
+
|
36
|
+
def url_decode(string)
|
37
|
+
Marshal.load(b64_decode(CGI.unescape(string)))
|
38
|
+
rescue TypeError, ArgumentError => e
|
39
|
+
raise BadString, "couldn't decode #{string} - got #{e}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def groom_path(path)
|
43
|
+
# Remove leading slashes and query params
|
44
|
+
path.gsub(/^\//, '').gsub(/\?.*$/, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convert a list of assets into an URL
|
48
|
+
# Note: Rails helpers adds the appropriate extension
|
49
|
+
def bundle_path(assets)
|
50
|
+
File.join('/stapler/bundle/', Bundle::Key.to_key(assets))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,94 +1,71 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: stapler
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 0
|
10
|
-
version: 0.1.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.1
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Michael Rykov
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-12-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: yuicompressor
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70096179375860 !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
18
|
+
requirements:
|
27
19
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 31
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 2
|
33
|
-
- 0
|
20
|
+
- !ruby/object:Gem::Version
|
34
21
|
version: 1.2.0
|
35
22
|
type: :runtime
|
36
|
-
|
37
|
-
|
38
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70096179375860
|
25
|
+
description: ! 'Dynamic asset compression for Rails using YUI Compressor
|
39
26
|
|
27
|
+
'
|
40
28
|
email: mrykov@gmail
|
41
29
|
executables: []
|
42
|
-
|
43
30
|
extensions: []
|
44
|
-
|
45
31
|
extra_rdoc_files: []
|
46
|
-
|
47
|
-
files:
|
32
|
+
files:
|
48
33
|
- README.md
|
49
34
|
- Rakefile
|
50
35
|
- LICENSE
|
51
|
-
- lib/stapler/
|
36
|
+
- lib/stapler/asset.rb
|
37
|
+
- lib/stapler/bundle/key.rb
|
38
|
+
- lib/stapler/bundle/serialized_key.rb
|
39
|
+
- lib/stapler/bundle.rb
|
52
40
|
- lib/stapler/config.rb
|
53
|
-
- lib/stapler/helper.rb
|
54
41
|
- lib/stapler/middleware.rb
|
55
|
-
- lib/stapler/
|
42
|
+
- lib/stapler/rails.rb
|
43
|
+
- lib/stapler/utils.rb
|
56
44
|
- lib/stapler.rb
|
57
45
|
- lib/tasks/stapler.rake
|
58
46
|
- lib/tasks/tasks.rb
|
59
|
-
has_rdoc: false
|
60
47
|
homepage: http://github.com/rykov/stapler
|
61
48
|
licenses: []
|
62
|
-
|
63
49
|
post_install_message:
|
64
50
|
rdoc_options: []
|
65
|
-
|
66
|
-
require_paths:
|
51
|
+
require_paths:
|
67
52
|
- lib
|
68
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
54
|
none: false
|
70
|
-
requirements:
|
71
|
-
- -
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
|
74
|
-
|
75
|
-
- 0
|
76
|
-
version: "0"
|
77
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
60
|
none: false
|
79
|
-
requirements:
|
80
|
-
- -
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
|
83
|
-
segments:
|
84
|
-
- 0
|
85
|
-
version: "0"
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
86
65
|
requirements: []
|
87
|
-
|
88
66
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.8.11
|
90
68
|
signing_key:
|
91
69
|
specification_version: 3
|
92
70
|
summary: Dynamic asset compression for Rails
|
93
71
|
test_files: []
|
94
|
-
|
data/lib/stapler/compressor.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2009 Michael Rykov
|
3
|
-
#++
|
4
|
-
|
5
|
-
module Stapler
|
6
|
-
class Compressor
|
7
|
-
def initialize(from, to)
|
8
|
-
@from_path = from
|
9
|
-
@to_path = to
|
10
|
-
end
|
11
|
-
|
12
|
-
def compress!
|
13
|
-
compress(@from_path, @to_path)
|
14
|
-
logger.info("[Stapler] Compressed #{@from_path}")
|
15
|
-
rescue => e
|
16
|
-
logger.warn("[Stapler] Compression failure for #{@from_path}: #{e}")
|
17
|
-
FileUtil.cp(@from_path, @to_path)
|
18
|
-
end
|
19
|
-
|
20
|
-
protected
|
21
|
-
def compress(from, to)
|
22
|
-
out = File.open(from) do |from_file|
|
23
|
-
if from =~ /\.css$/
|
24
|
-
YUICompressor.compress_css(from_file)
|
25
|
-
elsif from =~ /\.js$/
|
26
|
-
YUICompressor.compress_js(from_file, :munge => true)
|
27
|
-
else
|
28
|
-
raise StandardError.new("Unrecognized file type")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
File.open(to, 'w') { |f| f.write(out) }
|
33
|
-
end
|
34
|
-
|
35
|
-
def logger
|
36
|
-
Rails.logger
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
data/lib/stapler/helper.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2009 Michael Rykov
|
3
|
-
#++
|
4
|
-
|
5
|
-
module Stapler
|
6
|
-
module Helper
|
7
|
-
StaplerRoot = "/stapler"
|
8
|
-
|
9
|
-
def self.included(base)
|
10
|
-
base.send :alias_method_chain, :rewrite_asset_path, :stapler
|
11
|
-
#base.send :alias_method_chain, :compute_asset_host, :stapler
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
def rewrite_asset_path_with_stapler(source)
|
16
|
-
if Config.stapleable?(source)
|
17
|
-
@@stapler ||= Stapler.new
|
18
|
-
stapled_source = "#{StaplerRoot}#{source}"
|
19
|
-
stapled_path = asset_file_path(stapled_source)
|
20
|
-
|
21
|
-
if @@stapler.ready?(stapled_path)
|
22
|
-
source = stapled_source
|
23
|
-
else
|
24
|
-
@@stapler.process(asset_file_path(source), stapled_path)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
rewrite_asset_path_without_stapler(source)
|
29
|
-
end
|
30
|
-
|
31
|
-
#def compute_asset_host_with_stapler(source)
|
32
|
-
# compute_asset_host_without_stapler(source)
|
33
|
-
#end
|
34
|
-
end
|
35
|
-
end
|
data/lib/stapler/stapler.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (c) 2009 Michael Rykov
|
3
|
-
#++
|
4
|
-
|
5
|
-
module Stapler
|
6
|
-
class Stapler
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@in_progress = {}
|
10
|
-
@in_progress_guard = Mutex.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def ready?(path)
|
14
|
-
!@in_progress[path] && File.exists?(path)
|
15
|
-
end
|
16
|
-
|
17
|
-
def process(from, to)
|
18
|
-
@in_progress_guard.synchronize do
|
19
|
-
return if @in_progress[to]
|
20
|
-
@in_progress[to] = true
|
21
|
-
end
|
22
|
-
|
23
|
-
Thread.new { self.compress(from, to) }
|
24
|
-
end
|
25
|
-
|
26
|
-
protected
|
27
|
-
def compress(from, to)
|
28
|
-
FileUtils.mkdir_p(File.dirname(to))
|
29
|
-
Compressor.new(from, to).compress!
|
30
|
-
@in_progress.delete(to)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|