cloudfront_asset_host 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +61 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/cloudfront_asset_host.gemspec +64 -0
- data/lib/cloudfront_asset_host.rb +103 -0
- data/lib/cloudfront_asset_host/asset_tag_helper_ext.rb +37 -0
- data/lib/cloudfront_asset_host/css_rewriter.rb +65 -0
- data/lib/cloudfront_asset_host/mime_types.yml +151 -0
- data/lib/cloudfront_asset_host/uploader.rb +124 -0
- data/test/app/config/s3.yml +5 -0
- data/test/app/public/images/image.png +0 -0
- data/test/app/public/javascripts/application.js +1 -0
- data/test/app/public/stylesheets/style.css +4 -0
- data/test/cloudfront_asset_host_test.rb +104 -0
- data/test/css_rewriter_test.rb +28 -0
- data/test/test_helper.rb +26 -0
- data/test/uploader_test.rb +113 -0
- metadata +94 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Menno van der Sman
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# CloudFront AssetHost
|
2
|
+
|
3
|
+
Easy deployment of your assets on CloudFront or S3. When enabled in production, your assets will be served from Cloudfront/S3 which will result in a speedier front-end.
|
4
|
+
|
5
|
+
## Installing
|
6
|
+
|
7
|
+
Include the gem in your app's `environment.rb` or Gemfile. It is hosted at Gemcutter[http://gemcutter.org/gems/cloudfront_asset_host]
|
8
|
+
|
9
|
+
### Dependencies
|
10
|
+
|
11
|
+
Make sure your s3-credentials are stored in _config/s3.yml_ like this:
|
12
|
+
|
13
|
+
access_key_id: 'access_key'
|
14
|
+
secret_access_key: 'secret'
|
15
|
+
|
16
|
+
The gem relies on +openssl md5+ and +gzip+ utilities. Make sure they are available locally and on your servers.
|
17
|
+
|
18
|
+
### Configuration
|
19
|
+
Create an initializer to configure the plugin _config/initializers/cloudfront_asset_host.rb_
|
20
|
+
|
21
|
+
# Simple configuration
|
22
|
+
CloudfrontAssetHost.configure do |config|
|
23
|
+
config.bucket = "bucketname" # required
|
24
|
+
config.enabled = true if Rails.env.production? # only enable in production
|
25
|
+
end
|
26
|
+
|
27
|
+
# Extended configuration
|
28
|
+
CloudfrontAssetHost.configure do |config|
|
29
|
+
config.bucket = "bucketname" # required
|
30
|
+
config.cname = "assets.domain.com" # if you have a cname configured for your distribution or bucket
|
31
|
+
config.key_prefix = "app/" # if you share the bucket and want to keep things separated
|
32
|
+
config.s3_config = "#{RAILS_ROOT}/config/s3.yml" # Alternative location of your s3-config file
|
33
|
+
|
34
|
+
# gzip related configuration
|
35
|
+
config.gzip = true # enable gzipped assets (defaults to true)
|
36
|
+
config.gzip_extensions = ['js', 'css'] # only gzip javascript or css (defaults to %w(js css))
|
37
|
+
config.gzip_prefix = "gz" # prefix for gzipped bucket (defaults to "gz")
|
38
|
+
|
39
|
+
config.enabled = true if Rails.env.production? # only enable in production
|
40
|
+
end
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
### Uploading your assets
|
45
|
+
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.
|
46
|
+
|
47
|
+
### Hooks
|
48
|
+
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.
|
49
|
+
|
50
|
+
### Other plugins
|
51
|
+
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.
|
52
|
+
|
53
|
+
## Compatibility
|
54
|
+
|
55
|
+
Tested on Rails 2.3.5 with SASS and AssetPackager plugins
|
56
|
+
|
57
|
+
## Copyright
|
58
|
+
|
59
|
+
Created at Wakoopa
|
60
|
+
|
61
|
+
Copyright (c) 2010 Menno van der Sman, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the cloudfront_asset_host plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Generate documentation for the cloudfront_asset_host plugin.'
|
17
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = 'CloudfrontAssetHost'
|
20
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
+
rdoc.rdoc_files.include('README')
|
22
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'jeweler'
|
27
|
+
Jeweler::Tasks.new do |gemspec|
|
28
|
+
gemspec.name = "cloudfront_asset_host"
|
29
|
+
gemspec.summary = "Rails plugin to easily and efficiently deploy your assets on Amazon's S3 or CloudFront"
|
30
|
+
gemspec.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."
|
31
|
+
gemspec.email = "menno@wakoopa.com"
|
32
|
+
gemspec.homepage = "http://github.com/menno/cloudfront_asset_host"
|
33
|
+
gemspec.authors = ["Menno van der Sman"]
|
34
|
+
gemspec.add_dependency 'right_aws'
|
35
|
+
end
|
36
|
+
Jeweler::GemcutterTasks.new
|
37
|
+
rescue LoadError
|
38
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
39
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cloudfront_asset_host}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Menno van der Sman"]
|
12
|
+
s.date = %q{2010-03-18}
|
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
|
+
s.email = %q{menno@wakoopa.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"MIT-LICENSE",
|
21
|
+
"README.markdown",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"cloudfront_asset_host.gemspec",
|
25
|
+
"lib/cloudfront_asset_host.rb",
|
26
|
+
"lib/cloudfront_asset_host/asset_tag_helper_ext.rb",
|
27
|
+
"lib/cloudfront_asset_host/css_rewriter.rb",
|
28
|
+
"lib/cloudfront_asset_host/mime_types.yml",
|
29
|
+
"lib/cloudfront_asset_host/uploader.rb",
|
30
|
+
"test/app/config/s3.yml",
|
31
|
+
"test/app/public/images/image.png",
|
32
|
+
"test/app/public/javascripts/application.js",
|
33
|
+
"test/app/public/stylesheets/style.css",
|
34
|
+
"test/cloudfront_asset_host_test.rb",
|
35
|
+
"test/css_rewriter_test.rb",
|
36
|
+
"test/test_helper.rb",
|
37
|
+
"test/uploader_test.rb"
|
38
|
+
]
|
39
|
+
s.homepage = %q{http://github.com/menno/cloudfront_asset_host}
|
40
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.6}
|
43
|
+
s.summary = %q{Rails plugin to easily and efficiently deploy your assets on Amazon's S3 or CloudFront}
|
44
|
+
s.test_files = [
|
45
|
+
"test/cloudfront_asset_host_test.rb",
|
46
|
+
"test/css_rewriter_test.rb",
|
47
|
+
"test/test_helper.rb",
|
48
|
+
"test/uploader_test.rb"
|
49
|
+
]
|
50
|
+
|
51
|
+
if s.respond_to? :specification_version then
|
52
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<right_aws>, [">= 0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<right_aws>, [">= 0"])
|
59
|
+
end
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<right_aws>, [">= 0"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'cloudfront_asset_host/asset_tag_helper_ext'
|
2
|
+
|
3
|
+
module CloudfrontAssetHost
|
4
|
+
|
5
|
+
autoload :Uploader, 'cloudfront_asset_host/uploader'
|
6
|
+
autoload :CssRewriter, 'cloudfront_asset_host/css_rewriter'
|
7
|
+
|
8
|
+
# Bucket that will be used to store all the assets (required)
|
9
|
+
mattr_accessor :bucket
|
10
|
+
|
11
|
+
# CNAME that is configured for the bucket or CloudFront distribution
|
12
|
+
mattr_accessor :cname
|
13
|
+
|
14
|
+
# Prefix keys
|
15
|
+
mattr_accessor :key_prefix
|
16
|
+
|
17
|
+
# Path to S3 config. Expects an +access_key_id+ and +secret_access_key+
|
18
|
+
mattr_accessor :s3_config
|
19
|
+
|
20
|
+
# Indicates whether the plugin should be enabled
|
21
|
+
mattr_accessor :enabled
|
22
|
+
|
23
|
+
# Upload gzipped assets and serve those assets when applicable
|
24
|
+
mattr_accessor :gzip
|
25
|
+
|
26
|
+
# Which extensions to serve as gzip
|
27
|
+
mattr_accessor :gzip_extensions
|
28
|
+
|
29
|
+
# Key-prefix under which to store gzipped assets
|
30
|
+
mattr_accessor :gzip_prefix
|
31
|
+
|
32
|
+
class << self
|
33
|
+
|
34
|
+
def configure
|
35
|
+
# default configuration
|
36
|
+
self.bucket = nil
|
37
|
+
self.cname = nil
|
38
|
+
self.key_prefix = ""
|
39
|
+
self.s3_config = "#{RAILS_ROOT}/config/s3.yml"
|
40
|
+
self.enabled = false
|
41
|
+
|
42
|
+
self.gzip = true
|
43
|
+
self.gzip_extensions = %w(js css)
|
44
|
+
self.gzip_prefix = "gz"
|
45
|
+
|
46
|
+
yield(self)
|
47
|
+
|
48
|
+
if properly_configured?
|
49
|
+
enable!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def asset_host(source = nil, request = nil)
|
54
|
+
host = cname.present? ? "http://#{self.cname}" : "http://#{self.bucket_host}"
|
55
|
+
|
56
|
+
if source && request && CloudfrontAssetHost.gzip
|
57
|
+
gzip_allowed = CloudfrontAssetHost.gzip_allowed_for_source?(source)
|
58
|
+
gzip_accepted = !(request.headers['User-Agent'].to_s =~ /(Mozilla\/4\.0[678])|(MSIE\s[1-6])/) && request.headers['Accept-Encoding'].to_s.include?('gzip')
|
59
|
+
|
60
|
+
if gzip_accepted && gzip_allowed
|
61
|
+
host << "/#{CloudfrontAssetHost.gzip_prefix}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
host
|
66
|
+
end
|
67
|
+
|
68
|
+
def bucket_host
|
69
|
+
"#{self.bucket}.s3.amazonaws.com"
|
70
|
+
end
|
71
|
+
|
72
|
+
def enable!
|
73
|
+
if enabled
|
74
|
+
ActionController::Base.asset_host = Proc.new { |source, request| CloudfrontAssetHost.asset_host(source, request) }
|
75
|
+
ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :rewrite_asset_path, :cloudfront)
|
76
|
+
ActionView::Helpers::AssetTagHelper.send(:alias_method_chain, :rails_asset_id, :cloudfront)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def key_for_path(path)
|
81
|
+
key_prefix + md5sum(path)[0..8]
|
82
|
+
end
|
83
|
+
|
84
|
+
def gzip_allowed_for_source?(source)
|
85
|
+
extension = source.split('.').last
|
86
|
+
CloudfrontAssetHost.gzip_extensions.include?(extension)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def properly_configured?
|
92
|
+
raise "You'll need to specify a bucket" if bucket.blank?
|
93
|
+
raise "Could not find S3-configuration" unless File.exists?(s3_config)
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
def md5sum(path)
|
98
|
+
`openssl md5 #{path}`.split(/\s/)[1].to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActionView
|
2
|
+
module Helpers
|
3
|
+
module AssetTagHelper
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# Override asset_id so it calculates the key by md5 instead of modified-time
|
8
|
+
def rails_asset_id_with_cloudfront(source)
|
9
|
+
if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
|
10
|
+
asset_id
|
11
|
+
else
|
12
|
+
path = File.join(ASSETS_DIR, source)
|
13
|
+
asset_id = File.exist?(path) ? CloudfrontAssetHost.key_for_path(path) : ''
|
14
|
+
|
15
|
+
if @@cache_asset_timestamps
|
16
|
+
@@asset_timestamps_cache_guard.synchronize do
|
17
|
+
@@asset_timestamps_cache[source] = asset_id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
asset_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Override asset_path so it prepends the asset_id
|
26
|
+
def rewrite_asset_path_with_cloudfront(source)
|
27
|
+
asset_id = rails_asset_id(source)
|
28
|
+
if asset_id.blank?
|
29
|
+
source
|
30
|
+
else
|
31
|
+
"/#{asset_id}#{source}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module CloudfrontAssetHost
|
4
|
+
module CssRewriter
|
5
|
+
|
6
|
+
# Location of the stylesheets directory
|
7
|
+
mattr_accessor :stylesheets_dir
|
8
|
+
self.stylesheets_dir = File.join(Rails.public_path, 'stylesheets')
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# matches optional quoted url(<path>)
|
12
|
+
ReplaceRexeg = /url\(["']?([^\)"']+)["']?\)/i
|
13
|
+
|
14
|
+
# Returns the path to the temporary file that contains the
|
15
|
+
# rewritten stylesheet
|
16
|
+
def rewrite_stylesheet(path)
|
17
|
+
contents = File.read(path)
|
18
|
+
contents.gsub!(ReplaceRexeg) do |match|
|
19
|
+
rewrite_asset_link(match, path)
|
20
|
+
end
|
21
|
+
|
22
|
+
tmp = Tempfile.new("cfah-css")
|
23
|
+
tmp.write(contents)
|
24
|
+
tmp.flush
|
25
|
+
tmp
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def rewrite_asset_link(asset_link, stylesheet_path)
|
31
|
+
url = asset_link.match(ReplaceRexeg)[1]
|
32
|
+
if url
|
33
|
+
path = path_for_url(url, stylesheet_path)
|
34
|
+
|
35
|
+
if path.present? && File.exists?(path)
|
36
|
+
key = CloudfrontAssetHost.key_for_path(path) + path.gsub(Rails.public_path, '')
|
37
|
+
"url(#{CloudfrontAssetHost.asset_host}/#{key})"
|
38
|
+
else
|
39
|
+
puts "Could not extract path: #{path}"
|
40
|
+
asset_link
|
41
|
+
end
|
42
|
+
else
|
43
|
+
puts "Could not find url in #{asset_link}"
|
44
|
+
asset_link
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def path_for_url(url, stylesheet_path)
|
49
|
+
if url.starts_with?('/')
|
50
|
+
# absolute to public path
|
51
|
+
File.expand_path(File.join(Rails.public_path, url))
|
52
|
+
else
|
53
|
+
# relative to stylesheet_path
|
54
|
+
File.expand_path(File.join(File.dirname(stylesheet_path), url))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def stylesheets_to_rewrite
|
59
|
+
Dir.glob("#{stylesheets_dir}/**/*.css")
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
"text/html":
|
2
|
+
- html
|
3
|
+
- htm
|
4
|
+
- shtml
|
5
|
+
"text/css":
|
6
|
+
- css
|
7
|
+
"text/xml":
|
8
|
+
- xml
|
9
|
+
- rss
|
10
|
+
"image/gif":
|
11
|
+
- gif
|
12
|
+
"image/jpeg":
|
13
|
+
- jpeg
|
14
|
+
- jpg
|
15
|
+
"application/x-javascript":
|
16
|
+
- js
|
17
|
+
"application/atom+xml":
|
18
|
+
- atom
|
19
|
+
"text/mathml":
|
20
|
+
- mml
|
21
|
+
"text/plain":
|
22
|
+
- txt
|
23
|
+
"text/vnd.sun.j2me.app-descriptor":
|
24
|
+
- jad
|
25
|
+
"text/vnd.wap.wml":
|
26
|
+
- wml
|
27
|
+
"text/x-component":
|
28
|
+
- htc
|
29
|
+
"image/png":
|
30
|
+
- png
|
31
|
+
"image/tiff":
|
32
|
+
- tif
|
33
|
+
- tiff
|
34
|
+
"image/vnd.wap.wbmp":
|
35
|
+
- wbmp
|
36
|
+
"image/x-icon":
|
37
|
+
- ico
|
38
|
+
"image/x-jng":
|
39
|
+
- jng
|
40
|
+
"image/x-ms-bmp":
|
41
|
+
- bmp
|
42
|
+
"image/svg+xml":
|
43
|
+
- svg
|
44
|
+
"application/java-archive":
|
45
|
+
- jar
|
46
|
+
- war
|
47
|
+
- ear
|
48
|
+
"application/mac-binhex40":
|
49
|
+
- hqx
|
50
|
+
"application/msword":
|
51
|
+
- doc
|
52
|
+
"application/pdf":
|
53
|
+
- pdf
|
54
|
+
"application/postscript":
|
55
|
+
- ps
|
56
|
+
- eps
|
57
|
+
- ai
|
58
|
+
"application/rtf":
|
59
|
+
- rtf
|
60
|
+
"application/vnd.ms-excel":
|
61
|
+
- xls
|
62
|
+
"application/vnd.ms-powerpoint":
|
63
|
+
- ppt
|
64
|
+
"application/vnd.wap.wmlc":
|
65
|
+
- wmlc
|
66
|
+
"application/vnd.wap.xhtml+xml":
|
67
|
+
- xhtml
|
68
|
+
"application/x-cocoa":
|
69
|
+
- cco
|
70
|
+
"application/x-java-archive-diff":
|
71
|
+
- jardiff
|
72
|
+
"application/x-java-jnlp-file":
|
73
|
+
- jnlp
|
74
|
+
"application/x-makeself":
|
75
|
+
- run
|
76
|
+
"application/x-perl":
|
77
|
+
- pl
|
78
|
+
- pm
|
79
|
+
"application/x-pilot":
|
80
|
+
- prc
|
81
|
+
- pdb
|
82
|
+
"application/x-rar-compressed":
|
83
|
+
- rar
|
84
|
+
"application/x-redhat-package-manager":
|
85
|
+
- rpm
|
86
|
+
"application/x-sea":
|
87
|
+
- sea
|
88
|
+
"application/x-shockwave-flash":
|
89
|
+
- swf
|
90
|
+
"application/x-stuffit":
|
91
|
+
- sit
|
92
|
+
"application/x-tcl":
|
93
|
+
- tcl
|
94
|
+
- tk
|
95
|
+
"application/x-x509-ca-cert":
|
96
|
+
- der
|
97
|
+
- pem
|
98
|
+
- crt
|
99
|
+
"application/x-xpinstall":
|
100
|
+
- xpi
|
101
|
+
"application/zip":
|
102
|
+
- zip
|
103
|
+
"application/octet-stream":
|
104
|
+
- bin
|
105
|
+
- exe
|
106
|
+
- dll
|
107
|
+
"application/octet-stream":
|
108
|
+
- deb
|
109
|
+
"application/octet-stream":
|
110
|
+
- dmg
|
111
|
+
"application/octet-stream":
|
112
|
+
- eot
|
113
|
+
"application/octet-stream":
|
114
|
+
- iso
|
115
|
+
- img
|
116
|
+
"application/octet-stream":
|
117
|
+
- msi
|
118
|
+
- msp
|
119
|
+
- msm
|
120
|
+
"audio/midi":
|
121
|
+
- mid
|
122
|
+
- midi
|
123
|
+
- kar
|
124
|
+
"audio/mpeg":
|
125
|
+
- mp3
|
126
|
+
"audio/x-realaudio":
|
127
|
+
- ra
|
128
|
+
"video/3gpp":
|
129
|
+
- 3gpp
|
130
|
+
- 3gp
|
131
|
+
"video/mpeg":
|
132
|
+
- mpeg
|
133
|
+
- mpg
|
134
|
+
"video/quicktime":
|
135
|
+
- mov
|
136
|
+
"video/x-flv":
|
137
|
+
- flv
|
138
|
+
"video/x-mng":
|
139
|
+
- mng
|
140
|
+
"video/x-ms-asf":
|
141
|
+
- asx
|
142
|
+
- asf
|
143
|
+
"video/x-ms-wmv":
|
144
|
+
- wmv
|
145
|
+
"video/x-msvideo":
|
146
|
+
- avi
|
147
|
+
"text/plain":
|
148
|
+
- py
|
149
|
+
- php
|
150
|
+
- config
|
151
|
+
- txt
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'right_aws'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module CloudfrontAssetHost
|
5
|
+
module Uploader
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def upload!(options = {})
|
10
|
+
dryrun = options.delete(:dryrun) || false
|
11
|
+
verbose = options.delete(:verbose) || false
|
12
|
+
|
13
|
+
puts "-- Updating uncompressed files" if verbose
|
14
|
+
upload_keys_with_paths(keys_with_paths, dryrun, verbose, false)
|
15
|
+
|
16
|
+
if CloudfrontAssetHost.gzip
|
17
|
+
puts "-- Updating compressed files" if verbose
|
18
|
+
upload_keys_with_paths(gzip_keys_with_paths, dryrun, verbose, true)
|
19
|
+
end
|
20
|
+
|
21
|
+
@existing_keys = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def upload_keys_with_paths(keys_paths, dryrun, verbose, gzip)
|
25
|
+
keys_paths.each do |key, path|
|
26
|
+
if existing_keys.include?(key)
|
27
|
+
puts "= #{key}" if verbose
|
28
|
+
else
|
29
|
+
puts "+ #{key}" if verbose
|
30
|
+
|
31
|
+
extension = File.extname(path)[1..-1]
|
32
|
+
|
33
|
+
path = rewritten_css_path(path)
|
34
|
+
|
35
|
+
data_path = gzip ? gzipped_path(path) : path
|
36
|
+
bucket.put(key, File.read(data_path), {}, 'public-read', headers_for_path(extension, gzip)) unless dryrun
|
37
|
+
|
38
|
+
File.unlink(data_path) if gzip && File.exists?(data_path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def gzipped_path(path)
|
44
|
+
tmp = Tempfile.new("cfah-gz")
|
45
|
+
`gzip #{path} -q -c > #{tmp.path}`
|
46
|
+
tmp.path
|
47
|
+
end
|
48
|
+
|
49
|
+
def rewritten_css_path(path)
|
50
|
+
if File.extname(path) == '.css'
|
51
|
+
tmp = CloudfrontAssetHost::CssRewriter.rewrite_stylesheet(path)
|
52
|
+
tmp.path
|
53
|
+
else
|
54
|
+
path
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def keys_with_paths
|
59
|
+
current_paths.inject({}) do |result, path|
|
60
|
+
key = CloudfrontAssetHost.key_for_path(path) + path.gsub(Rails.public_path, '')
|
61
|
+
|
62
|
+
result[key] = path
|
63
|
+
result
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def gzip_keys_with_paths
|
68
|
+
current_paths.inject({}) do |result, path|
|
69
|
+
source = path.gsub(Rails.public_path, '')
|
70
|
+
|
71
|
+
if CloudfrontAssetHost.gzip_allowed_for_source?(source)
|
72
|
+
key = "#{CloudfrontAssetHost.gzip_prefix}/" << CloudfrontAssetHost.key_for_path(path) << source
|
73
|
+
result[key] = path
|
74
|
+
end
|
75
|
+
|
76
|
+
result
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def existing_keys
|
81
|
+
@existing_keys ||= begin
|
82
|
+
keys = []
|
83
|
+
keys.concat bucket.keys('prefix' => CloudfrontAssetHost.key_prefix).map { |key| key.name }
|
84
|
+
keys.concat bucket.keys('prefix' => CloudfrontAssetHost.gzip_prefix).map { |key| key.name }
|
85
|
+
keys
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def current_paths
|
90
|
+
@current_paths ||= Dir.glob("#{Rails.public_path}/{images,javascripts,stylesheets}/**/*").reject { |path| File.directory?(path) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def headers_for_path(extension, gzip = false)
|
94
|
+
mime = ext_to_mime[extension] || 'application/octet-stream'
|
95
|
+
headers = {
|
96
|
+
'Content-Type' => mime,
|
97
|
+
'Cache-Control' => "max-age=#{10.years.to_i}",
|
98
|
+
'Expires' => 1.year.from_now.utc.to_s
|
99
|
+
}
|
100
|
+
headers['Content-Encoding'] = 'gzip' if gzip
|
101
|
+
|
102
|
+
headers
|
103
|
+
end
|
104
|
+
|
105
|
+
def ext_to_mime
|
106
|
+
@ext_to_mime ||= Hash[ *( YAML::load_file(File.join(File.dirname(__FILE__), "mime_types.yml")).collect { |k,vv| vv.collect{ |v| [v,k] } }.flatten ) ]
|
107
|
+
end
|
108
|
+
|
109
|
+
def bucket
|
110
|
+
@bucket ||= s3.bucket(CloudfrontAssetHost.bucket)
|
111
|
+
end
|
112
|
+
|
113
|
+
def s3
|
114
|
+
@s3 ||= RightAws::S3.new(config['access_key_id'], config['secret_access_key'])
|
115
|
+
end
|
116
|
+
|
117
|
+
def config
|
118
|
+
@config ||= YAML::load_file(CloudfrontAssetHost.s3_config)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
// Javascripts here
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CloudfrontAssetHostTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "A configured plugin" do
|
6
|
+
setup do
|
7
|
+
CloudfrontAssetHost.configure do |config|
|
8
|
+
config.cname = "assethost.com"
|
9
|
+
config.bucket = "bucketname"
|
10
|
+
config.key_prefix = ""
|
11
|
+
config.enabled = false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should "add methods to asset-tag-helper" do
|
16
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id_with_cloudfront')
|
17
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path_with_cloudfront')
|
18
|
+
end
|
19
|
+
|
20
|
+
should "not enable itself by default" do
|
21
|
+
assert_equal false, CloudfrontAssetHost.enabled
|
22
|
+
assert_equal "", ActionController::Base.asset_host
|
23
|
+
end
|
24
|
+
|
25
|
+
should "return key for path" do
|
26
|
+
assert_equal "8ed41cb87", CloudfrontAssetHost.key_for_path(File.join(RAILS_ROOT, 'public', 'javascripts', 'application.js'))
|
27
|
+
end
|
28
|
+
|
29
|
+
should "prepend prefix to key" do
|
30
|
+
CloudfrontAssetHost.key_prefix = "prefix/"
|
31
|
+
assert_equal "prefix/8ed41cb87", CloudfrontAssetHost.key_for_path(File.join(RAILS_ROOT, 'public', 'javascripts', 'application.js'))
|
32
|
+
end
|
33
|
+
|
34
|
+
context "asset-host" do
|
35
|
+
|
36
|
+
setup do
|
37
|
+
@gzip_request = stub(:headers => {'Accept-Encoding' => 'gzip, compress'})
|
38
|
+
@request = stub(:headers => {})
|
39
|
+
@img_source = "/images/logo.png"
|
40
|
+
@js_source = "/javascripts/application.js"
|
41
|
+
end
|
42
|
+
|
43
|
+
should "use cname for asset_host" do
|
44
|
+
assert_equal "http://assethost.com", CloudfrontAssetHost.asset_host(@img_source, @request)
|
45
|
+
end
|
46
|
+
|
47
|
+
should "use bucket_host when cname is not present" do
|
48
|
+
CloudfrontAssetHost.cname = nil
|
49
|
+
assert_equal "http://bucketname.s3.amazonaws.com", CloudfrontAssetHost.asset_host(@img_source, @request)
|
50
|
+
end
|
51
|
+
|
52
|
+
should "add gz to asset_host when applicable" do
|
53
|
+
assert_equal "http://assethost.com/gz", CloudfrontAssetHost.asset_host(@js_source, @gzip_request)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "An enabled and configured plugin" do
|
60
|
+
setup do
|
61
|
+
CloudfrontAssetHost.configure do |config|
|
62
|
+
config.enabled = true
|
63
|
+
config.cname = "assethost.com"
|
64
|
+
config.bucket = "bucketname"
|
65
|
+
config.key_prefix = ""
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
should "set the asset_host" do
|
70
|
+
assert ActionController::Base.asset_host.is_a?(Proc)
|
71
|
+
end
|
72
|
+
|
73
|
+
should "alias methods in asset-tag-helper" do
|
74
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id_without_cloudfront')
|
75
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path_without_cloudfront')
|
76
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rails_asset_id')
|
77
|
+
assert ActionView::Helpers::AssetTagHelper.private_method_defined?('rewrite_asset_path')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "An improperly configured plugin" do
|
82
|
+
should "complain about bucket not being set" do
|
83
|
+
assert_raise(RuntimeError) {
|
84
|
+
CloudfrontAssetHost.configure do |config|
|
85
|
+
config.enabled = false
|
86
|
+
config.cname = "assethost.com"
|
87
|
+
config.bucket = nil
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
should "complain about missing s3-config" do
|
93
|
+
assert_raise(RuntimeError) {
|
94
|
+
CloudfrontAssetHost.configure do |config|
|
95
|
+
config.enabled = false
|
96
|
+
config.cname = "assethost.com"
|
97
|
+
config.bucket = "bucketname"
|
98
|
+
config.s3_config = "bogus"
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CssRewriterTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "The CssRewriter" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
CloudfrontAssetHost.configure do |config|
|
9
|
+
config.cname = "assethost.com"
|
10
|
+
config.bucket = "bucketname"
|
11
|
+
config.key_prefix = ""
|
12
|
+
config.enabled = false
|
13
|
+
end
|
14
|
+
|
15
|
+
@stylesheet_path = File.join(Rails.public_path, 'stylesheets', 'style.css')
|
16
|
+
end
|
17
|
+
|
18
|
+
should "rewrite a single css file" do
|
19
|
+
tmp = CloudfrontAssetHost::CssRewriter.rewrite_stylesheet(@stylesheet_path)
|
20
|
+
contents = File.read(tmp.path)
|
21
|
+
contents.split("\n").each do |line|
|
22
|
+
assert_equal "body { background-image: url(http://assethost.com/d41d8cd98/images/image.png); }", line
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
require 'action_controller'
|
5
|
+
require 'right_aws'
|
6
|
+
|
7
|
+
require 'shoulda'
|
8
|
+
require 'mocha'
|
9
|
+
begin require 'redgreen'; rescue LoadError; end
|
10
|
+
begin require 'turn'; rescue LoadError; end
|
11
|
+
|
12
|
+
RAILS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), 'app'))
|
13
|
+
|
14
|
+
module Rails
|
15
|
+
class << self
|
16
|
+
def root
|
17
|
+
RAILS_ROOT
|
18
|
+
end
|
19
|
+
|
20
|
+
def public_path
|
21
|
+
File.join(RAILS_ROOT, 'public')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'cloudfront_asset_host')
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class UploaderTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "A configured uploader" do
|
6
|
+
setup do
|
7
|
+
CloudfrontAssetHost.configure do |config|
|
8
|
+
config.cname = "assethost.com"
|
9
|
+
config.bucket = "bucketname"
|
10
|
+
config.key_prefix = ""
|
11
|
+
config.s3_config = "#{RAILS_ROOT}/config/s3.yml"
|
12
|
+
config.enabled = false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should "be able to retrieve s3-config" do
|
17
|
+
config = CloudfrontAssetHost::Uploader.config
|
18
|
+
assert_equal 'access_key', config['access_key_id']
|
19
|
+
assert_equal 'secret', config['secret_access_key']
|
20
|
+
end
|
21
|
+
|
22
|
+
should "be able to instantiate s3-interface" do
|
23
|
+
RightAws::S3.expects(:new).with('access_key', 'secret').returns(mock)
|
24
|
+
assert_not_nil CloudfrontAssetHost::Uploader.s3
|
25
|
+
end
|
26
|
+
|
27
|
+
should "glob current files" do
|
28
|
+
assert_equal 3, CloudfrontAssetHost::Uploader.current_paths.length
|
29
|
+
end
|
30
|
+
|
31
|
+
should "calculate keys for paths" do
|
32
|
+
keys_with_paths = CloudfrontAssetHost::Uploader.keys_with_paths
|
33
|
+
assert_equal 3, keys_with_paths.length
|
34
|
+
assert_match %r{/test/app/public/javascripts/application\.js$}, keys_with_paths["8ed41cb87/javascripts/application.js"]
|
35
|
+
end
|
36
|
+
|
37
|
+
should "calculate gzip keys for paths" do
|
38
|
+
gz_keys_with_paths = CloudfrontAssetHost::Uploader.gzip_keys_with_paths
|
39
|
+
assert_equal 2, gz_keys_with_paths.length
|
40
|
+
assert_match %r{/test/app/public/javascripts/application\.js$}, gz_keys_with_paths["gz/8ed41cb87/javascripts/application.js"]
|
41
|
+
assert_match %r{/test/app/public/stylesheets/style\.css$}, gz_keys_with_paths["gz/bd258f13d/stylesheets/style.css"]
|
42
|
+
end
|
43
|
+
|
44
|
+
should "return a mimetype for an extension" do
|
45
|
+
assert_equal 'application/x-javascript', CloudfrontAssetHost::Uploader.ext_to_mime['js']
|
46
|
+
end
|
47
|
+
|
48
|
+
should "construct the headers for a path" do
|
49
|
+
headers = CloudfrontAssetHost::Uploader.headers_for_path('js')
|
50
|
+
assert_equal 'application/x-javascript', headers['Content-Type']
|
51
|
+
assert_match /max-age=\d+/, headers['Cache-Control']
|
52
|
+
assert DateTime.parse(headers['Expires'])
|
53
|
+
end
|
54
|
+
|
55
|
+
should "add gzip-header" do
|
56
|
+
headers = CloudfrontAssetHost::Uploader.headers_for_path('js', true)
|
57
|
+
assert_equal 'application/x-javascript', headers['Content-Type']
|
58
|
+
assert_equal 'gzip', headers['Content-Encoding']
|
59
|
+
assert_match /max-age=\d+/, headers['Cache-Control']
|
60
|
+
assert DateTime.parse(headers['Expires'])
|
61
|
+
end
|
62
|
+
|
63
|
+
should "retrieve existing keys" do
|
64
|
+
bucket_mock = mock
|
65
|
+
bucket_mock.expects(:keys).with({'prefix' => ''}).returns([stub(:name => "keyname")])
|
66
|
+
bucket_mock.expects(:keys).with({'prefix' => 'gz'}).returns([stub(:name => "gz/keyname")])
|
67
|
+
|
68
|
+
CloudfrontAssetHost::Uploader.expects(:bucket).times(2).returns(bucket_mock)
|
69
|
+
assert_equal ["keyname", "gz/keyname"], CloudfrontAssetHost::Uploader.existing_keys
|
70
|
+
end
|
71
|
+
|
72
|
+
should "upload files when there are no existing keys" do
|
73
|
+
bucket_mock = mock
|
74
|
+
bucket_mock.expects(:put).times(5)
|
75
|
+
CloudfrontAssetHost::Uploader.stubs(:bucket).returns(bucket_mock)
|
76
|
+
CloudfrontAssetHost::Uploader.stubs(:existing_keys).returns([])
|
77
|
+
|
78
|
+
CloudfrontAssetHost::Uploader.upload!
|
79
|
+
end
|
80
|
+
|
81
|
+
should "not re-upload existing keys" do
|
82
|
+
CloudfrontAssetHost::Uploader.expects(:bucket).never
|
83
|
+
CloudfrontAssetHost::Uploader.stubs(:existing_keys).returns(
|
84
|
+
["gz/8ed41cb87/javascripts/application.js", "8ed41cb87/javascripts/application.js",
|
85
|
+
"d41d8cd98/images/image.png",
|
86
|
+
"bd258f13d/stylesheets/style.css", "gz/bd258f13d/stylesheets/style.css"]
|
87
|
+
)
|
88
|
+
|
89
|
+
CloudfrontAssetHost::Uploader.upload!
|
90
|
+
end
|
91
|
+
|
92
|
+
should "correctly gzip files" do
|
93
|
+
path = File.join(RAILS_ROOT, 'public', 'javascripts', 'application.js')
|
94
|
+
contents = File.read(path)
|
95
|
+
|
96
|
+
gz_path = CloudfrontAssetHost::Uploader.gzipped_path(path)
|
97
|
+
gunzip_contents = `gunzip #{gz_path} -q -c`
|
98
|
+
|
99
|
+
assert_equal contents, gunzip_contents
|
100
|
+
end
|
101
|
+
|
102
|
+
should "correctly rewrite css files" do
|
103
|
+
path = File.join(RAILS_ROOT, 'public', 'stylesheets', 'style.css')
|
104
|
+
css_path = CloudfrontAssetHost::Uploader.rewritten_css_path(path)
|
105
|
+
|
106
|
+
File.read(css_path).split("\n").each do |line|
|
107
|
+
assert_equal "body { background-image: url(http://assethost.com/d41d8cd98/images/image.png); }", line
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloudfront_asset_host
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Menno van der Sman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-18 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: right_aws
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
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
|
+
email: menno@wakoopa.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files:
|
39
|
+
- README.markdown
|
40
|
+
files:
|
41
|
+
- .gitignore
|
42
|
+
- MIT-LICENSE
|
43
|
+
- README.markdown
|
44
|
+
- Rakefile
|
45
|
+
- VERSION
|
46
|
+
- cloudfront_asset_host.gemspec
|
47
|
+
- lib/cloudfront_asset_host.rb
|
48
|
+
- lib/cloudfront_asset_host/asset_tag_helper_ext.rb
|
49
|
+
- lib/cloudfront_asset_host/css_rewriter.rb
|
50
|
+
- lib/cloudfront_asset_host/mime_types.yml
|
51
|
+
- lib/cloudfront_asset_host/uploader.rb
|
52
|
+
- test/app/config/s3.yml
|
53
|
+
- test/app/public/images/image.png
|
54
|
+
- test/app/public/javascripts/application.js
|
55
|
+
- test/app/public/stylesheets/style.css
|
56
|
+
- test/cloudfront_asset_host_test.rb
|
57
|
+
- test/css_rewriter_test.rb
|
58
|
+
- test/test_helper.rb
|
59
|
+
- test/uploader_test.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/menno/cloudfront_asset_host
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --charset=UTF-8
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Rails plugin to easily and efficiently deploy your assets on Amazon's S3 or CloudFront
|
90
|
+
test_files:
|
91
|
+
- test/cloudfront_asset_host_test.rb
|
92
|
+
- test/css_rewriter_test.rb
|
93
|
+
- test/test_helper.rb
|
94
|
+
- test/uploader_test.rb
|