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 ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gemspec
@@ -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
@@ -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 determined which assets aren't uploaded yet. This speeds up the deployment considerably.
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
- Include the gem in your app's `environment.rb` or `Gemfile`.
25
+ Add the gem to your `environment.rb` or `Gemfile`.
26
26
 
27
27
  ### Dependencies
28
28
 
29
- The gem relies on `openssl md5` and `gzip` utilities. Make sure they are available locally and on your servers.
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 = "bucketname" # required
49
- config.cname = "assets.domain.com" # if you have a cname configured for your distribution or bucket
50
- config.key_prefix = "app/" # if you share the bucket and want to keep things separated
51
- config.s3_config = "#{RAILS_ROOT}/config/s3.yml" # Alternative location of your s3-config file
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 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.
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-files and package your assets before uploading them to Cloudfront. For example, call `Sass::Plugin.update_stylesheets` and `Synthesis::AssetPackage.build_all` first.
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-requests.
114
+ Feel free to fork the project and send pull requests.
115
+
116
+ ## Contributors
75
117
 
76
- ## Known Limitations
118
+ Thanks to these people who contributed patches:
77
119
 
78
- - Does not delete old assets
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.2
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.2"
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-03-23}
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.6}
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::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
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
- host = cname.present? ? "http://#{self.cname}" : "http://#{self.bucket_host}"
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
- `openssl md5 #{path}`.split(/\s/)[1].to_s
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
- asset_id = File.exist?(path) ? CloudfrontAssetHost.key_for_path(path) : ''
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
- 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)
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, dryrun, verbose, true)
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, dryrun, verbose, gzip)
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 existing_keys.include?(key)
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 File.extname(path) == '.css'
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.key_for_path(path) + path.gsub(Rails.public_path, '')
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
- keys.concat bucket.keys('prefix' => CloudfrontAssetHost.key_prefix).map { |key| key.name }
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}/{images,javascripts,stylesheets}/**/*").reject { |path| File.directory?(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 ||= s3.bucket(CloudfrontAssetHost.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 ||= YAML::load_file(CloudfrontAssetHost.s3_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
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ production:
3
+ access_key_id: 'access_key'
4
+ secret_access_key: 'secret'
5
+ options:
6
+ use_ssl: true
@@ -2,4 +2,4 @@
2
2
  access_key_id: 'access_key'
3
3
  secret_access_key: 'secret'
4
4
  options:
5
- use_ssl: true
5
+ use_ssl: true
@@ -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 = "/javascripts/application.js"
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 "not support gzip for images" do
50
- request = stub(:headers => {'User-Agent' => 'Mozilla/5.0', 'Accept-Encoding' => 'gzip, compress'})
51
- source = "/images/logo.png"
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
@@ -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
 
@@ -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["8ed41cb87/javascripts/application.js"]
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/8ed41cb87/javascripts/application.js"]
41
- assert_match %r{/test/app/public/stylesheets/style\.css$}, gz_keys_with_paths["gz/bd258f13d/stylesheets/style.css"]
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/8ed41cb87/javascripts/application.js", "8ed41cb87/javascripts/application.js",
97
+ ["gz/#{@js_md5}/javascripts/application.js", "#{@js_md5}/javascripts/application.js",
85
98
  "d41d8cd98/images/image.png",
86
- "bd258f13d/stylesheets/style.css", "gz/bd258f13d/stylesheets/style.css"]
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
- - 2
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-03-23 00:00:00 +01:00
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.6
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