cloudfront_asset_host 1.0.2 → 1.1.0
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/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/README.markdown +57 -13
- data/Rakefile +6 -0
- data/VERSION +1 -1
- data/cloudfront_asset_host.gemspec +25 -4
- data/lib/cloudfront_asset_host.rb +53 -2
- data/lib/cloudfront_asset_host/asset_tag_helper_ext.rb +2 -1
- data/lib/cloudfront_asset_host/css_rewriter.rb +2 -2
- data/lib/cloudfront_asset_host/uploader.rb +45 -19
- data/test/app/config/s3-env.yml +6 -0
- data/test/app/config/s3.yml +1 -1
- data/test/app/public/stylesheets/style.css +3 -1
- data/test/cloudfront_asset_host_test.rb +32 -5
- data/test/test_helper.rb +8 -2
- data/test/uploader_test.rb +60 -7
- metadata +102 -4
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cloudfront_asset_host (1.1.0)
|
5
|
+
right_aws
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionpack (2.3.9)
|
11
|
+
activesupport (= 2.3.9)
|
12
|
+
rack (~> 1.1.0)
|
13
|
+
activesupport (2.3.9)
|
14
|
+
ansi (1.2.2)
|
15
|
+
facets (2.8.4)
|
16
|
+
mocha (0.9.8)
|
17
|
+
rake
|
18
|
+
rack (1.1.0)
|
19
|
+
rake (0.8.7)
|
20
|
+
redgreen (1.2.2)
|
21
|
+
right_aws (2.0.0)
|
22
|
+
right_http_connection (>= 1.2.1)
|
23
|
+
right_http_connection (1.2.4)
|
24
|
+
shoulda (2.11.3)
|
25
|
+
turn (0.7.0)
|
26
|
+
ansi (>= 1.1.0)
|
27
|
+
facets (>= 2.8.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
actionpack (= 2.3.9)
|
34
|
+
activesupport (= 2.3.9)
|
35
|
+
cloudfront_asset_host!
|
36
|
+
mocha
|
37
|
+
redgreen
|
38
|
+
right_aws
|
39
|
+
shoulda
|
40
|
+
turn
|
data/README.markdown
CHANGED
@@ -12,7 +12,7 @@ The best way to expire your assets on CloudFront is to upload the asset to a new
|
|
12
12
|
|
13
13
|
### Efficient uploading
|
14
14
|
|
15
|
-
By using the MD5-hash we can easily
|
15
|
+
By using the MD5-hash we can easily determine which assets aren't uploaded yet. This speeds up the deployment considerably.
|
16
16
|
|
17
17
|
### Compressed assets
|
18
18
|
|
@@ -22,11 +22,11 @@ CloudFront will not serve compressed assets automatically. To counter this, the
|
|
22
22
|
|
23
23
|
gem install cloudfront_asset_host
|
24
24
|
|
25
|
-
|
25
|
+
Add the gem to your `environment.rb` or `Gemfile`.
|
26
26
|
|
27
27
|
### Dependencies
|
28
28
|
|
29
|
-
The gem relies on
|
29
|
+
The gem relies on the `gzip` utility. Make sure it is available locally.
|
30
30
|
|
31
31
|
### Configuration
|
32
32
|
|
@@ -35,6 +35,12 @@ Make sure your s3-credentials are stored in _config/s3.yml_ like this:
|
|
35
35
|
access_key_id: 'access_key'
|
36
36
|
secret_access_key: 'secret'
|
37
37
|
|
38
|
+
or with environment specified:
|
39
|
+
|
40
|
+
production:
|
41
|
+
access_key_id: 'access_key'
|
42
|
+
secret_access_key: 'secret'
|
43
|
+
|
38
44
|
Create an initializer to configure the plugin _config/initializers/cloudfront_asset_host.rb_
|
39
45
|
|
40
46
|
# Simple configuration
|
@@ -45,10 +51,18 @@ Create an initializer to configure the plugin _config/initializers/cloudfront_as
|
|
45
51
|
|
46
52
|
# Extended configuration
|
47
53
|
CloudfrontAssetHost.configure do |config|
|
48
|
-
config.bucket
|
49
|
-
config.cname
|
50
|
-
|
51
|
-
config.
|
54
|
+
config.bucket = "bucketname" # required
|
55
|
+
config.cname = "assets.domain.com" # cname configured for your distribution or bucket
|
56
|
+
|
57
|
+
config.key_prefix = "app/" # prefix for paths
|
58
|
+
config.plain_prefix = "" # prefix for paths to uncompressed files
|
59
|
+
|
60
|
+
config.asset_dirs = %w(images flash) # specify directories to be uploaded
|
61
|
+
config.exclude_pattern = /pdf/ # exclude matching assets from uploading
|
62
|
+
|
63
|
+
# s3 configuration
|
64
|
+
config.s3_config = "path/to/s3.yml" # Alternative location of your s3-config file
|
65
|
+
config.s3_logging = true # enable logging for this bucket
|
52
66
|
|
53
67
|
# gzip related configuration
|
54
68
|
config.gzip = true # enable gzipped assets (defaults to true)
|
@@ -58,24 +72,54 @@ Create an initializer to configure the plugin _config/initializers/cloudfront_as
|
|
58
72
|
config.enabled = true if Rails.env.production? # only enable in production
|
59
73
|
end
|
60
74
|
|
75
|
+
The _cname_ option also accepts a `Proc` or `String` with the `%d` parameter (e.g. "assets%d.example.com" for multiple hosts).
|
76
|
+
|
61
77
|
## Usage
|
62
78
|
|
63
79
|
### Uploading your assets
|
64
|
-
Run `CloudfrontAssetHost::Uploader.upload!(:verbose => true, :dryrun => false)` before your deployment. Put it for example in your Rakefile or
|
80
|
+
Run `CloudfrontAssetHost::Uploader.upload!(:verbose => true, :dryrun => false)` before your deployment. Put it for example in your Rakefile or Capistrano-recipe. Verbose output will include information about which keys are being uploaded. Enabling _dryrun_ will skip the actual upload if you're just interested to see what will be uploaded.
|
65
81
|
|
66
82
|
### Hooks
|
67
83
|
If the plugin is enabled. Rails' internal `asset_host` and `asset_id` functionality will be overridden to point to the location of the assets on Cloudfront.
|
68
84
|
|
69
85
|
### Other plugins
|
70
|
-
When using in combination with SASS and/or asset_packager it is recommended to generate the css
|
86
|
+
When using in combination with SASS and/or asset_packager it is recommended to generate the css files and package your assets before uploading them to Cloudfront. For example, call `Sass::Plugin.update_stylesheets` and `Synthesis::AssetPackage.build_all` first.
|
87
|
+
|
88
|
+
## Changelog
|
89
|
+
|
90
|
+
- 1.1
|
91
|
+
- New features:
|
92
|
+
- Add support for CNAME-interpolation [wpeterson]
|
93
|
+
- Add ability to specify an exclude regex for CDN content [wpeterson]
|
94
|
+
- Configure directories to upload through CloudfrontAssetHost.asset_dirs [wpeterson]
|
95
|
+
- CloudfrontAssetHost.cname accepts Proc [foresth]
|
96
|
+
- Rewrite all css-files when some images are modified [foresth]
|
97
|
+
- Enable S3-logging with CloudfrontAssetHost.s3_loggin (defaults to false) [foresth]
|
98
|
+
- Ability to insert prefix to paths to uncompressed files [foresth]
|
99
|
+
- Ability to define environment specific credentials in s3.yml [foresth]
|
100
|
+
|
101
|
+
- Fixes and improvements:
|
102
|
+
- Strip query-parameters from image-urls in CSS [wpeterson]
|
103
|
+
- Use Digest::MD5 as md5-implementation [wpeterson, mattdb, foresth]
|
104
|
+
- Fix bug working with paths with spaces [caleb]
|
105
|
+
|
106
|
+
- 1.0.2
|
107
|
+
- Fix bug serving gzipped-assets to IE
|
108
|
+
|
109
|
+
- 1.0.1
|
110
|
+
- First release
|
71
111
|
|
72
112
|
## Contributing
|
73
113
|
|
74
|
-
Feel free to fork the project and send pull
|
114
|
+
Feel free to fork the project and send pull requests.
|
115
|
+
|
116
|
+
## Contributors
|
75
117
|
|
76
|
-
|
118
|
+
Thanks to these people who contributed patches:
|
77
119
|
|
78
|
-
|
120
|
+
* [Winfield](http://github.com/wpeterson)
|
121
|
+
* [Caleb Land](http://github.com/caleb)
|
122
|
+
* [foresth](http://github.com/foresth)
|
79
123
|
|
80
124
|
## Compatibility
|
81
125
|
|
@@ -83,6 +127,6 @@ Tested on Rails 2.3.5 with SASS and AssetPackager plugins
|
|
83
127
|
|
84
128
|
## Copyright
|
85
129
|
|
86
|
-
Created at Wakoopa
|
130
|
+
Created at [Wakoopa](http://wakoopa.com)
|
87
131
|
|
88
132
|
Copyright (c) 2010 Menno van der Sman, released under the MIT license
|
data/Rakefile
CHANGED
@@ -32,6 +32,12 @@ begin
|
|
32
32
|
gemspec.homepage = "http://github.com/menno/cloudfront_asset_host"
|
33
33
|
gemspec.authors = ["Menno van der Sman"]
|
34
34
|
gemspec.add_dependency 'right_aws'
|
35
|
+
gemspec.add_development_dependency 'activesupport', '2.3.9'
|
36
|
+
gemspec.add_development_dependency 'actionpack', '2.3.9'
|
37
|
+
gemspec.add_development_dependency 'shoulda'
|
38
|
+
gemspec.add_development_dependency 'mocha'
|
39
|
+
gemspec.add_development_dependency 'redgreen'
|
40
|
+
gemspec.add_development_dependency 'turn'
|
35
41
|
end
|
36
42
|
Jeweler::GemcutterTasks.new
|
37
43
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cloudfront_asset_host}
|
8
|
-
s.version = "1.0
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Menno van der Sman"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-10-02}
|
13
13
|
s.description = %q{Easy deployment of your assets on CloudFront or S3 using a simple rake-task. When enabled in production, the application's asset_host and public_paths will point to the correct location.}
|
14
14
|
s.email = %q{menno@wakoopa.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
|
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".gitignore",
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
20
22
|
"MIT-LICENSE",
|
21
23
|
"README.markdown",
|
22
24
|
"Rakefile",
|
@@ -27,6 +29,7 @@ Gem::Specification.new do |s|
|
|
27
29
|
"lib/cloudfront_asset_host/css_rewriter.rb",
|
28
30
|
"lib/cloudfront_asset_host/mime_types.yml",
|
29
31
|
"lib/cloudfront_asset_host/uploader.rb",
|
32
|
+
"test/app/config/s3-env.yml",
|
30
33
|
"test/app/config/s3.yml",
|
31
34
|
"test/app/public/images/image.png",
|
32
35
|
"test/app/public/javascripts/application.js",
|
@@ -39,7 +42,7 @@ Gem::Specification.new do |s|
|
|
39
42
|
s.homepage = %q{http://github.com/menno/cloudfront_asset_host}
|
40
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
41
44
|
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = %q{1.3.
|
45
|
+
s.rubygems_version = %q{1.3.7}
|
43
46
|
s.summary = %q{Rails plugin to easily and efficiently deploy your assets on Amazon's S3 or CloudFront}
|
44
47
|
s.test_files = [
|
45
48
|
"test/cloudfront_asset_host_test.rb",
|
@@ -52,13 +55,31 @@ Gem::Specification.new do |s|
|
|
52
55
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
56
|
s.specification_version = 3
|
54
57
|
|
55
|
-
if Gem::Version.new(Gem::
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
59
|
s.add_runtime_dependency(%q<right_aws>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<activesupport>, ["= 2.3.9"])
|
61
|
+
s.add_development_dependency(%q<actionpack>, ["= 2.3.9"])
|
62
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<redgreen>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<turn>, [">= 0"])
|
57
66
|
else
|
58
67
|
s.add_dependency(%q<right_aws>, [">= 0"])
|
68
|
+
s.add_dependency(%q<activesupport>, ["= 2.3.9"])
|
69
|
+
s.add_dependency(%q<actionpack>, ["= 2.3.9"])
|
70
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
71
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
72
|
+
s.add_dependency(%q<redgreen>, [">= 0"])
|
73
|
+
s.add_dependency(%q<turn>, [">= 0"])
|
59
74
|
end
|
60
75
|
else
|
61
76
|
s.add_dependency(%q<right_aws>, [">= 0"])
|
77
|
+
s.add_dependency(%q<activesupport>, ["= 2.3.9"])
|
78
|
+
s.add_dependency(%q<actionpack>, ["= 2.3.9"])
|
79
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
80
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
81
|
+
s.add_dependency(%q<redgreen>, [">= 0"])
|
82
|
+
s.add_dependency(%q<turn>, [">= 0"])
|
62
83
|
end
|
63
84
|
end
|
64
85
|
|
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'digest/md5'
|
1
2
|
require 'cloudfront_asset_host/asset_tag_helper_ext'
|
3
|
+
require 'digest/md5'
|
2
4
|
|
3
5
|
module CloudfrontAssetHost
|
4
6
|
|
@@ -17,6 +19,9 @@ module CloudfrontAssetHost
|
|
17
19
|
# Path to S3 config. Expects an +access_key_id+ and +secret_access_key+
|
18
20
|
mattr_accessor :s3_config
|
19
21
|
|
22
|
+
# Log S3 server access on a bucket
|
23
|
+
mattr_accessor :s3_logging
|
24
|
+
|
20
25
|
# Indicates whether the plugin should be enabled
|
21
26
|
mattr_accessor :enabled
|
22
27
|
|
@@ -29,6 +34,18 @@ module CloudfrontAssetHost
|
|
29
34
|
# Key-prefix under which to store gzipped assets
|
30
35
|
mattr_accessor :gzip_prefix
|
31
36
|
|
37
|
+
# Key-prefix under which to store plain (unzipped) assets
|
38
|
+
mattr_accessor :plain_prefix
|
39
|
+
|
40
|
+
# Which extensions are likely to occur in css files
|
41
|
+
mattr_accessor :image_extensions
|
42
|
+
|
43
|
+
# Array of directories to search for asset files in
|
44
|
+
mattr_accessor :asset_dirs
|
45
|
+
|
46
|
+
# Key Regular Expression to filter out/exclude content
|
47
|
+
mattr_accessor :exclude_pattern
|
48
|
+
|
32
49
|
class << self
|
33
50
|
|
34
51
|
def configure
|
@@ -37,12 +54,20 @@ module CloudfrontAssetHost
|
|
37
54
|
self.cname = nil
|
38
55
|
self.key_prefix = ""
|
39
56
|
self.s3_config = "#{RAILS_ROOT}/config/s3.yml"
|
57
|
+
self.s3_logging = false
|
40
58
|
self.enabled = false
|
41
59
|
|
60
|
+
self.asset_dirs = %w(images javascripts stylesheets)
|
61
|
+
self.exclude_pattern = nil
|
62
|
+
|
42
63
|
self.gzip = true
|
43
64
|
self.gzip_extensions = %w(js css)
|
44
65
|
self.gzip_prefix = "gz"
|
45
66
|
|
67
|
+
self.plain_prefix = ""
|
68
|
+
|
69
|
+
self.image_extensions = %w(jpg jpeg gif png)
|
70
|
+
|
46
71
|
yield(self)
|
47
72
|
|
48
73
|
if properly_configured?
|
@@ -51,7 +76,16 @@ module CloudfrontAssetHost
|
|
51
76
|
end
|
52
77
|
|
53
78
|
def asset_host(source = nil, request = nil)
|
54
|
-
|
79
|
+
if cname.present?
|
80
|
+
if cname.is_a?(Proc)
|
81
|
+
host = cname.call(source, request)
|
82
|
+
else
|
83
|
+
host = (cname =~ /%d/) ? cname % (source.hash % 4) : cname.to_s
|
84
|
+
host = "http://#{host}"
|
85
|
+
end
|
86
|
+
else
|
87
|
+
host = "http://#{self.bucket_host}"
|
88
|
+
end
|
55
89
|
|
56
90
|
if source && request && CloudfrontAssetHost.gzip
|
57
91
|
gzip_allowed = CloudfrontAssetHost.gzip_allowed_for_source?(source)
|
@@ -64,7 +98,11 @@ module CloudfrontAssetHost
|
|
64
98
|
|
65
99
|
if gzip_accepted && gzip_allowed
|
66
100
|
host << "/#{CloudfrontAssetHost.gzip_prefix}"
|
101
|
+
else
|
102
|
+
host << "/#{CloudfrontAssetHost.plain_prefix}" if CloudfrontAssetHost.plain_prefix.present?
|
67
103
|
end
|
104
|
+
else
|
105
|
+
host << "/#{CloudfrontAssetHost.plain_prefix}" if CloudfrontAssetHost.plain_prefix.present?
|
68
106
|
end
|
69
107
|
|
70
108
|
host
|
@@ -91,6 +129,19 @@ module CloudfrontAssetHost
|
|
91
129
|
CloudfrontAssetHost.gzip_extensions.include?(extension)
|
92
130
|
end
|
93
131
|
|
132
|
+
def image?(path)
|
133
|
+
extension = path.split('.').last
|
134
|
+
CloudfrontAssetHost.image_extensions.include?(extension)
|
135
|
+
end
|
136
|
+
|
137
|
+
def css?(path)
|
138
|
+
File.extname(path) == '.css'
|
139
|
+
end
|
140
|
+
|
141
|
+
def disable_cdn_for_source?(source)
|
142
|
+
source.match(exclude_pattern) if exclude_pattern.present?
|
143
|
+
end
|
144
|
+
|
94
145
|
private
|
95
146
|
|
96
147
|
def properly_configured?
|
@@ -100,7 +151,7 @@ module CloudfrontAssetHost
|
|
100
151
|
end
|
101
152
|
|
102
153
|
def md5sum(path)
|
103
|
-
|
154
|
+
Digest::MD5.hexdigest(File.read(path))
|
104
155
|
end
|
105
156
|
|
106
157
|
end
|
@@ -10,7 +10,8 @@ module ActionView
|
|
10
10
|
asset_id
|
11
11
|
else
|
12
12
|
path = File.join(ASSETS_DIR, source)
|
13
|
-
|
13
|
+
rewrite_path = File.exist?(path) && !CloudfrontAssetHost.disable_cdn_for_source?(source)
|
14
|
+
asset_id = rewrite_path ? CloudfrontAssetHost.key_for_path(path) : ''
|
14
15
|
|
15
16
|
if @@cache_asset_timestamps
|
16
17
|
@@asset_timestamps_cache_guard.synchronize do
|
@@ -9,7 +9,7 @@ module CloudfrontAssetHost
|
|
9
9
|
|
10
10
|
class << self
|
11
11
|
# matches optional quoted url(<path>)
|
12
|
-
ReplaceRexeg = /url\(["']?([^\)"']+)["']?\)/i
|
12
|
+
ReplaceRexeg = /url\(["']?([^\)\?"']+)(\?[^"']*)?["']?\)/i
|
13
13
|
|
14
14
|
# Returns the path to the temporary file that contains the
|
15
15
|
# rewritten stylesheet
|
@@ -34,7 +34,7 @@ module CloudfrontAssetHost
|
|
34
34
|
|
35
35
|
if path.present? && File.exists?(path)
|
36
36
|
key = CloudfrontAssetHost.key_for_path(path) + path.gsub(Rails.public_path, '')
|
37
|
-
"url(#{CloudfrontAssetHost.asset_host}/#{key})"
|
37
|
+
"url(#{CloudfrontAssetHost.asset_host(url)}/#{key})"
|
38
38
|
else
|
39
39
|
puts "Could not extract path: #{path}"
|
40
40
|
asset_link
|
@@ -7,25 +7,24 @@ module CloudfrontAssetHost
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def upload!(options = {})
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
puts "-- Updating uncompressed files" if verbose
|
14
|
-
upload_keys_with_paths(keys_with_paths, dryrun, verbose, false)
|
10
|
+
puts "-- Updating uncompressed files" if options[:verbose]
|
11
|
+
upload_keys_with_paths(keys_with_paths, options)
|
15
12
|
|
16
13
|
if CloudfrontAssetHost.gzip
|
17
|
-
puts "-- Updating compressed files" if verbose
|
18
|
-
upload_keys_with_paths(gzip_keys_with_paths,
|
14
|
+
puts "-- Updating compressed files" if options[:verbose]
|
15
|
+
upload_keys_with_paths(gzip_keys_with_paths, options.merge(:gzip => true))
|
19
16
|
end
|
20
17
|
|
21
18
|
@existing_keys = nil
|
22
19
|
end
|
23
20
|
|
24
|
-
def upload_keys_with_paths(keys_paths,
|
21
|
+
def upload_keys_with_paths(keys_paths, options={})
|
22
|
+
gzip = options[:gzip] || false
|
23
|
+
dryrun = options[:dryrun] || false
|
24
|
+
verbose = options[:verbose] || false
|
25
|
+
|
25
26
|
keys_paths.each do |key, path|
|
26
|
-
if
|
27
|
-
puts "= #{key}" if verbose
|
28
|
-
else
|
27
|
+
if should_upload?(key, options)
|
29
28
|
puts "+ #{key}" if verbose
|
30
29
|
|
31
30
|
extension = File.extname(path)[1..-1]
|
@@ -36,18 +35,27 @@ module CloudfrontAssetHost
|
|
36
35
|
bucket.put(key, File.read(data_path), {}, 'public-read', headers_for_path(extension, gzip)) unless dryrun
|
37
36
|
|
38
37
|
File.unlink(data_path) if gzip && File.exists?(data_path)
|
38
|
+
else
|
39
|
+
puts "= #{key}" if verbose
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def should_upload?(key, options={})
|
45
|
+
return false if CloudfrontAssetHost.disable_cdn_for_source?(key)
|
46
|
+
return true if CloudfrontAssetHost.css?(key) && rewrite_all_css?
|
47
|
+
|
48
|
+
options[:force_write] || !existing_keys.include?(key)
|
49
|
+
end
|
50
|
+
|
43
51
|
def gzipped_path(path)
|
44
52
|
tmp = Tempfile.new("cfah-gz")
|
45
|
-
`gzip #{path} -q -c > #{tmp.path}`
|
53
|
+
`gzip '#{path}' -q -c > '#{tmp.path}'`
|
46
54
|
tmp.path
|
47
55
|
end
|
48
56
|
|
49
57
|
def rewritten_css_path(path)
|
50
|
-
if
|
58
|
+
if CloudfrontAssetHost.css?(path)
|
51
59
|
tmp = CloudfrontAssetHost::CssRewriter.rewrite_stylesheet(path)
|
52
60
|
tmp.path
|
53
61
|
else
|
@@ -57,7 +65,8 @@ module CloudfrontAssetHost
|
|
57
65
|
|
58
66
|
def keys_with_paths
|
59
67
|
current_paths.inject({}) do |result, path|
|
60
|
-
key = CloudfrontAssetHost.
|
68
|
+
key = CloudfrontAssetHost.plain_prefix.present? ? "#{CloudfrontAssetHost.plain_prefix}/" : ""
|
69
|
+
key << CloudfrontAssetHost.key_for_path(path) + path.gsub(Rails.public_path, '')
|
61
70
|
|
62
71
|
result[key] = path
|
63
72
|
result
|
@@ -77,17 +86,23 @@ module CloudfrontAssetHost
|
|
77
86
|
end
|
78
87
|
end
|
79
88
|
|
89
|
+
def rewrite_all_css?
|
90
|
+
@rewrite_all_css ||= !keys_with_paths.delete_if { |key, path| existing_keys.include?(key) || !CloudfrontAssetHost.image?(path) }.empty?
|
91
|
+
end
|
92
|
+
|
80
93
|
def existing_keys
|
81
94
|
@existing_keys ||= begin
|
82
95
|
keys = []
|
83
|
-
|
96
|
+
prefix = CloudfrontAssetHost.key_prefix
|
97
|
+
prefix = "#{CloudfrontAssetHost.plain_prefix}/#{prefix}" if CloudfrontAssetHost.plain_prefix.present?
|
98
|
+
keys.concat bucket.keys('prefix' => prefix).map { |key| key.name }
|
84
99
|
keys.concat bucket.keys('prefix' => CloudfrontAssetHost.gzip_prefix).map { |key| key.name }
|
85
100
|
keys
|
86
101
|
end
|
87
102
|
end
|
88
103
|
|
89
104
|
def current_paths
|
90
|
-
@current_paths ||= Dir.glob("#{Rails.public_path}/{
|
105
|
+
@current_paths ||= Dir.glob("#{Rails.public_path}/{#{asset_dirs.join(',')}}/**/*").reject { |path| File.directory?(path) }
|
91
106
|
end
|
92
107
|
|
93
108
|
def headers_for_path(extension, gzip = false)
|
@@ -107,7 +122,11 @@ module CloudfrontAssetHost
|
|
107
122
|
end
|
108
123
|
|
109
124
|
def bucket
|
110
|
-
@bucket ||=
|
125
|
+
@bucket ||= begin
|
126
|
+
bucket = s3.bucket(CloudfrontAssetHost.bucket)
|
127
|
+
bucket.disable_logging unless CloudfrontAssetHost.s3_logging
|
128
|
+
bucket
|
129
|
+
end
|
111
130
|
end
|
112
131
|
|
113
132
|
def s3
|
@@ -115,10 +134,17 @@ module CloudfrontAssetHost
|
|
115
134
|
end
|
116
135
|
|
117
136
|
def config
|
118
|
-
@config ||=
|
137
|
+
@config ||= begin
|
138
|
+
config = YAML::load_file(CloudfrontAssetHost.s3_config)
|
139
|
+
config.has_key?(Rails.env) ? config[Rails.env] : config
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def asset_dirs
|
144
|
+
@asset_dirs ||= CloudfrontAssetHost.asset_dirs
|
119
145
|
end
|
120
146
|
|
121
147
|
end
|
122
148
|
|
123
149
|
end
|
124
|
-
end
|
150
|
+
end
|
data/test/app/config/s3.yml
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
body { background-image: url(../images/image.png); }
|
2
2
|
body { background-image: url(/images/image.png); }
|
3
3
|
body { background-image: url('/images/image.png'); }
|
4
|
-
body { background-image: url("/images/image.png"); }
|
4
|
+
body { background-image: url("/images/image.png"); }
|
5
|
+
body { background-image: url("/images/image.png?98732857"); }
|
6
|
+
body { background-image: url("/images/image.png?fo0=bar"); }
|
@@ -31,29 +31,48 @@ class CloudfrontAssetHostTest < Test::Unit::TestCase
|
|
31
31
|
assert_equal "prefix/8ed41cb87", CloudfrontAssetHost.key_for_path(File.join(RAILS_ROOT, 'public', 'javascripts', 'application.js'))
|
32
32
|
end
|
33
33
|
|
34
|
+
should "default asset_dirs setting" do
|
35
|
+
assert_equal %w(images javascripts stylesheets), CloudfrontAssetHost.asset_dirs
|
36
|
+
end
|
37
|
+
|
34
38
|
context "asset-host" do
|
35
39
|
|
36
40
|
setup do
|
37
|
-
@source
|
41
|
+
@source = "/javascripts/application.js"
|
38
42
|
end
|
39
43
|
|
40
44
|
should "use cname for asset_host" do
|
41
45
|
assert_equal "http://assethost.com", CloudfrontAssetHost.asset_host(@source)
|
42
46
|
end
|
43
47
|
|
48
|
+
should "use interpolated cname for asset_host" do
|
49
|
+
CloudfrontAssetHost.cname = "assethost-%d.com"
|
50
|
+
assert_equal "http://assethost-3.com", CloudfrontAssetHost.asset_host(@source)
|
51
|
+
end
|
52
|
+
|
53
|
+
should "call proc for asset_host" do
|
54
|
+
CloudfrontAssetHost.cname = Proc.new { |source, request| "http://assethost-proc.com" }
|
55
|
+
assert_equal "http://assethost-proc.com", CloudfrontAssetHost.asset_host(@source)
|
56
|
+
end
|
57
|
+
|
44
58
|
should "use bucket_host when cname is not present" do
|
45
59
|
CloudfrontAssetHost.cname = nil
|
46
60
|
assert_equal "http://bucketname.s3.amazonaws.com", CloudfrontAssetHost.asset_host(@source)
|
47
61
|
end
|
48
62
|
|
49
|
-
should "
|
50
|
-
|
51
|
-
|
52
|
-
assert_equal "http://assethost.com", CloudfrontAssetHost.asset_host(source, request)
|
63
|
+
should "add plain_prefix if present" do
|
64
|
+
CloudfrontAssetHost.plain_prefix = "prefix"
|
65
|
+
assert_equal "http://assethost.com/prefix", CloudfrontAssetHost.asset_host(@source)
|
53
66
|
end
|
54
67
|
|
55
68
|
context "when taking the headers into account" do
|
56
69
|
|
70
|
+
should "not support gzip for images" do
|
71
|
+
request = stub(:headers => {'User-Agent' => 'Mozilla/5.0', 'Accept-Encoding' => 'gzip, compress'})
|
72
|
+
source = "/images/logo.png"
|
73
|
+
assert_equal "http://assethost.com", CloudfrontAssetHost.asset_host(source, request)
|
74
|
+
end
|
75
|
+
|
57
76
|
should "support gzip for IE" do
|
58
77
|
request = stub(:headers => {'User-Agent' => 'Mozilla/4.0 (compatible; MSIE 8.0)', 'Accept-Encoding' => 'gzip, compress'})
|
59
78
|
assert_equal "http://assethost.com/gz", CloudfrontAssetHost.asset_host(@source, request)
|
@@ -124,4 +143,12 @@ class CloudfrontAssetHostTest < Test::Unit::TestCase
|
|
124
143
|
end
|
125
144
|
end
|
126
145
|
|
146
|
+
should "respect custom asset_dirs" do
|
147
|
+
CloudfrontAssetHost.configure do |config|
|
148
|
+
config.bucket = "bucketname"
|
149
|
+
config.asset_dirs = %w(custom)
|
150
|
+
end
|
151
|
+
assert_equal %w(custom), CloudfrontAssetHost.asset_dirs
|
152
|
+
end
|
153
|
+
|
127
154
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
require 'test/unit'
|
2
1
|
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
3
4
|
|
5
|
+
require 'active_support'
|
4
6
|
require 'action_controller'
|
5
|
-
require 'right_aws'
|
6
7
|
|
8
|
+
require 'test/unit'
|
7
9
|
require 'shoulda'
|
8
10
|
require 'mocha'
|
9
11
|
begin require 'redgreen'; rescue LoadError; end
|
@@ -20,6 +22,10 @@ module Rails
|
|
20
22
|
def public_path
|
21
23
|
File.join(RAILS_ROOT, 'public')
|
22
24
|
end
|
25
|
+
|
26
|
+
def env
|
27
|
+
"production"
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
data/test/uploader_test.rb
CHANGED
@@ -4,6 +4,8 @@ class UploaderTest < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
context "A configured uploader" do
|
6
6
|
setup do
|
7
|
+
@css_md5 = md5_key('stylesheets/style.css') #7026e6ce3
|
8
|
+
@js_md5 = md5_key('javascripts/application.js') #8ed41cb87
|
7
9
|
CloudfrontAssetHost.configure do |config|
|
8
10
|
config.cname = "assethost.com"
|
9
11
|
config.bucket = "bucketname"
|
@@ -19,6 +21,17 @@ class UploaderTest < Test::Unit::TestCase
|
|
19
21
|
assert_equal 'secret', config['secret_access_key']
|
20
22
|
end
|
21
23
|
|
24
|
+
should "be able to retrieve enviroment-specific config" do
|
25
|
+
CloudfrontAssetHost.configure do |config|
|
26
|
+
config.enabled = false
|
27
|
+
config.bucket = "bucketname"
|
28
|
+
config.s3_config = "#{RAILS_ROOT}/config/s3-env.yml"
|
29
|
+
end
|
30
|
+
config = CloudfrontAssetHost::Uploader.config
|
31
|
+
assert_equal 'access_key', config['access_key_id']
|
32
|
+
assert_equal 'secret', config['secret_access_key']
|
33
|
+
end
|
34
|
+
|
22
35
|
should "be able to instantiate s3-interface" do
|
23
36
|
RightAws::S3.expects(:new).with('access_key', 'secret').returns(mock)
|
24
37
|
assert_not_nil CloudfrontAssetHost::Uploader.s3
|
@@ -31,14 +44,14 @@ class UploaderTest < Test::Unit::TestCase
|
|
31
44
|
should "calculate keys for paths" do
|
32
45
|
keys_with_paths = CloudfrontAssetHost::Uploader.keys_with_paths
|
33
46
|
assert_equal 3, keys_with_paths.length
|
34
|
-
assert_match %r{/test/app/public/javascripts/application\.js$}, keys_with_paths["
|
47
|
+
assert_match %r{/test/app/public/javascripts/application\.js$}, keys_with_paths["#{@js_md5}/javascripts/application.js"]
|
35
48
|
end
|
36
49
|
|
37
50
|
should "calculate gzip keys for paths" do
|
38
51
|
gz_keys_with_paths = CloudfrontAssetHost::Uploader.gzip_keys_with_paths
|
39
52
|
assert_equal 2, gz_keys_with_paths.length
|
40
|
-
assert_match %r{/test/app/public/javascripts/application\.js$}, gz_keys_with_paths["gz/
|
41
|
-
assert_match %r{/test/app/public/stylesheets/style\.css$}, gz_keys_with_paths["gz/
|
53
|
+
assert_match %r{/test/app/public/javascripts/application\.js$}, gz_keys_with_paths["gz/#{@js_md5}/javascripts/application.js"]
|
54
|
+
assert_match %r{/test/app/public/stylesheets/style\.css$}, gz_keys_with_paths["gz/#{@css_md5}/stylesheets/style.css"]
|
42
55
|
end
|
43
56
|
|
44
57
|
should "return a mimetype for an extension" do
|
@@ -78,23 +91,36 @@ class UploaderTest < Test::Unit::TestCase
|
|
78
91
|
CloudfrontAssetHost::Uploader.upload!
|
79
92
|
end
|
80
93
|
|
81
|
-
should "not re-upload existing keys" do
|
94
|
+
should "not re-upload existing keys by default" do
|
82
95
|
CloudfrontAssetHost::Uploader.expects(:bucket).never
|
83
96
|
CloudfrontAssetHost::Uploader.stubs(:existing_keys).returns(
|
84
|
-
["gz/
|
97
|
+
["gz/#{@js_md5}/javascripts/application.js", "#{@js_md5}/javascripts/application.js",
|
85
98
|
"d41d8cd98/images/image.png",
|
86
|
-
"
|
99
|
+
"#{@css_md5}/stylesheets/style.css", "gz/#{@css_md5}/stylesheets/style.css"]
|
87
100
|
)
|
88
101
|
|
89
102
|
CloudfrontAssetHost::Uploader.upload!
|
90
103
|
end
|
91
104
|
|
105
|
+
should "re-upload existing keys w/ force_write" do
|
106
|
+
bucket_mock = mock
|
107
|
+
bucket_mock.expects(:put).times(5)
|
108
|
+
CloudfrontAssetHost::Uploader.stubs(:bucket).returns(bucket_mock)
|
109
|
+
CloudfrontAssetHost::Uploader.stubs(:existing_keys).returns(
|
110
|
+
["gz/#{@js_md5}/javascripts/application.js", "#{@js_md5}/javascripts/application.js",
|
111
|
+
"d41d8cd98/images/image.png",
|
112
|
+
"#{@css_md5}/stylesheets/style.css", "gz/#{@css_md5}/stylesheets/style.css"]
|
113
|
+
)
|
114
|
+
|
115
|
+
CloudfrontAssetHost::Uploader.upload!(:force_write => true)
|
116
|
+
end
|
117
|
+
|
92
118
|
should "correctly gzip files" do
|
93
119
|
path = File.join(RAILS_ROOT, 'public', 'javascripts', 'application.js')
|
94
120
|
contents = File.read(path)
|
95
121
|
|
96
122
|
gz_path = CloudfrontAssetHost::Uploader.gzipped_path(path)
|
97
|
-
gunzip_contents = `gunzip #{gz_path} -q -c`
|
123
|
+
gunzip_contents = `gunzip '#{gz_path}' -q -c`
|
98
124
|
|
99
125
|
assert_equal contents, gunzip_contents
|
100
126
|
end
|
@@ -110,4 +136,31 @@ class UploaderTest < Test::Unit::TestCase
|
|
110
136
|
|
111
137
|
end
|
112
138
|
|
139
|
+
context 'with exclude_pattern' do
|
140
|
+
setup do
|
141
|
+
@css_md5 = md5_key('stylesheets/style.css') #7026e6ce3
|
142
|
+
@js_md5 = md5_key('javascripts/application.js') #8ed41cb87
|
143
|
+
CloudfrontAssetHost.configure do |config|
|
144
|
+
config.cname = "assethost.com"
|
145
|
+
config.bucket = "bucketname"
|
146
|
+
config.key_prefix = ""
|
147
|
+
config.s3_config = "#{RAILS_ROOT}/config/s3.yml"
|
148
|
+
config.enabled = false
|
149
|
+
config.exclude_pattern = /style/
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
should "filter keys out" do
|
154
|
+
bucket_mock = mock
|
155
|
+
bucket_mock.expects(:put).times(3)
|
156
|
+
CloudfrontAssetHost::Uploader.stubs(:bucket).returns(bucket_mock)
|
157
|
+
CloudfrontAssetHost::Uploader.stubs(:existing_keys).returns([])
|
158
|
+
|
159
|
+
CloudfrontAssetHost::Uploader.upload!
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def md5_key(path)
|
164
|
+
CloudfrontAssetHost.send(:md5sum, File.join('test/app/public', path))[0..8]
|
165
|
+
end
|
113
166
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudfront_asset_host
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 1
|
8
|
+
- 1
|
7
9
|
- 0
|
8
|
-
|
9
|
-
version: 1.0.2
|
10
|
+
version: 1.1.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Menno van der Sman
|
@@ -14,21 +15,111 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-10-02 00:00:00 +02:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: right_aws
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
27
30
|
segments:
|
28
31
|
- 0
|
29
32
|
version: "0"
|
30
33
|
type: :runtime
|
31
34
|
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activesupport
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 17
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 3
|
47
|
+
- 9
|
48
|
+
version: 2.3.9
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: actionpack
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - "="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 17
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 3
|
63
|
+
- 9
|
64
|
+
version: 2.3.9
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: shoulda
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: mocha
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
type: :development
|
94
|
+
version_requirements: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: redgreen
|
97
|
+
prerelease: false
|
98
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
type: :development
|
108
|
+
version_requirements: *id006
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: turn
|
111
|
+
prerelease: false
|
112
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
type: :development
|
122
|
+
version_requirements: *id007
|
32
123
|
description: Easy deployment of your assets on CloudFront or S3 using a simple rake-task. When enabled in production, the application's asset_host and public_paths will point to the correct location.
|
33
124
|
email: menno@wakoopa.com
|
34
125
|
executables: []
|
@@ -39,6 +130,8 @@ extra_rdoc_files:
|
|
39
130
|
- README.markdown
|
40
131
|
files:
|
41
132
|
- .gitignore
|
133
|
+
- Gemfile
|
134
|
+
- Gemfile.lock
|
42
135
|
- MIT-LICENSE
|
43
136
|
- README.markdown
|
44
137
|
- Rakefile
|
@@ -49,6 +142,7 @@ files:
|
|
49
142
|
- lib/cloudfront_asset_host/css_rewriter.rb
|
50
143
|
- lib/cloudfront_asset_host/mime_types.yml
|
51
144
|
- lib/cloudfront_asset_host/uploader.rb
|
145
|
+
- test/app/config/s3-env.yml
|
52
146
|
- test/app/config/s3.yml
|
53
147
|
- test/app/public/images/image.png
|
54
148
|
- test/app/public/javascripts/application.js
|
@@ -67,23 +161,27 @@ rdoc_options:
|
|
67
161
|
require_paths:
|
68
162
|
- lib
|
69
163
|
required_ruby_version: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
70
165
|
requirements:
|
71
166
|
- - ">="
|
72
167
|
- !ruby/object:Gem::Version
|
168
|
+
hash: 3
|
73
169
|
segments:
|
74
170
|
- 0
|
75
171
|
version: "0"
|
76
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
77
174
|
requirements:
|
78
175
|
- - ">="
|
79
176
|
- !ruby/object:Gem::Version
|
177
|
+
hash: 3
|
80
178
|
segments:
|
81
179
|
- 0
|
82
180
|
version: "0"
|
83
181
|
requirements: []
|
84
182
|
|
85
183
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.3.
|
184
|
+
rubygems_version: 1.3.7
|
87
185
|
signing_key:
|
88
186
|
specification_version: 3
|
89
187
|
summary: Rails plugin to easily and efficiently deploy your assets on Amazon's S3 or CloudFront
|