cdn_helpers 0.9

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ CDN asset URL helpers
2
+ =====================
3
+
4
+ This library contains a Rubygem and Django custom templatetag library to do the following:
5
+
6
+ turn an asset path like
7
+
8
+ /images/thing.png
9
+
10
+ into a CDN-pointed URL like
11
+
12
+ http://cdn.name.com/images/thing.png/{sha1}.png
13
+
14
+ Rails
15
+ -----
16
+
17
+ Add `cdn_helpers` to your `Gemfile`:
18
+
19
+ gem 'cdn_helpers', :git => 'git@github.com:alphagov/cdn_helpers.git'
20
+
21
+ In your `environments/production.rb`:
22
+
23
+ require 'cdn_helpers'
24
+ config.action_controller.asset_host = "http://cdn.url.gov.uk"
25
+ config.action_controller.asset_path = CdnHelpers::AssetPath
26
+
27
+ The gem also make the `rake` task `cdn:css_urls` available to you. This is for post-deployment rewriting of URLs within CSS files. It's destructive - it rewrites the files in place - but it doesn't require that the config vars above are set since it leaves URLs as absolute local URLs.
28
+
29
+ Django
30
+ ------
31
+
32
+ Add `cdn_helpers` to your `requirements.txt`:
33
+
34
+ -e git+ssh://git@github.com/alphagov/cdn_helpers.git#egg=cdn_helpers
35
+
36
+ in `settings.py` add cdn_helpers to your list of installed apps:
37
+
38
+ INSTALLED_APPS = (
39
+ 'django.contrib.auth',
40
+ ...
41
+ 'cdn_helpers'
42
+ )
43
+
44
+ in your local-development `local_settings.py`:
45
+
46
+ APP_DEPLOYMENT_ENV = 'local' # (disables CDN url generation)
47
+ CDN_HOSTS = []
48
+
49
+ in the appropriate `local_settings.py`:
50
+
51
+ APP_DEPLOYMENT_ENV = 'dev' # (or staging, production)
52
+ CDN_HOSTS = ['cdn1', 'cdn2'] # (as appropriate for the environment)
53
+ SHARED_PUBLIC_ROOT = "/path/to/shared/assets" # (i.e. where Static's public dir is)
54
+
55
+ in templates use the `asset_url` helper:
56
+
57
+ <img src="{% asset_url "/images/thing.png" %}">
58
+ <img src="{% asset_url thing.image.url %}">
59
+
60
+ The app provides the `cdn_css` command for `manage.py`. This uses the Ruby script process_css to do the heavy lifting, but relies on the `MEDIA_ROOT` and `MEDIA_URL` settings in `settings.py` to tell where to look and how to construct the URLs.
61
+
62
+
63
+ Running tests
64
+ =============
65
+
66
+ bundle exec rake spec
67
+
68
+ PYTHONPATH=py nosetests -w test
69
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new :spec
@@ -0,0 +1,131 @@
1
+ require 'digest/sha1'
2
+ require 'pathname'
3
+ require 'uri'
4
+ require 'nokogiri'
5
+
6
+ module CdnHelpers
7
+ require 'cdn_helpers/railtie' if defined?(Rails)
8
+
9
+ module AssetPath
10
+ def self.asset_cache
11
+ @@asset_cache ||= {}
12
+ end
13
+
14
+ def self.call(path)
15
+ hash_file(path, Rails.root.join('public'), Rails.logger)
16
+ end
17
+
18
+ def self.hash_file(path, public_root_path, logger)
19
+ unless asset_cache.has_key?(path)
20
+ file_path = public_root_path.join(path.sub(/^\//, '')).to_s
21
+ unless File.file?(file_path)
22
+ logger.warn("Cannot rewrite URL for #{path}: File not found")
23
+ return path
24
+ end
25
+ sha1 = Digest::SHA1.file(file_path).hexdigest
26
+ extension = File.extname(path)
27
+ return asset_cache[path] = path + "/#{sha1[0..7]}#{hash_salt}#{extension}"
28
+ end
29
+ asset_cache[path]
30
+ end
31
+
32
+ def self.hash_salt
33
+ @@hash_salt ||= 'X'
34
+ end
35
+
36
+ def self.set_hash_salt(value)
37
+ @@hash_salt = value
38
+ end
39
+ end
40
+
41
+ module HtmlRewriter
42
+ def self.rewrite_file(logger, file_path, public_root_path, asset_hosts)
43
+ html_content = open(file_path).read
44
+ html = Nokogiri::HTML.parse(html_content)
45
+ process_document(html, file_path, public_root_path, asset_hosts, logger)
46
+ File.open(file_path, 'w') { |f| f << html.to_s }
47
+ end
48
+
49
+ def self.process_asset_url(href, file_path, public_root_path, logger)
50
+ local_url = Pathname.new(href)
51
+ url_prefix = "/"
52
+ if local_url.relative?
53
+ logger.warn "We don't yet support relative paths to assets: #{local_url}"
54
+ return href
55
+ else
56
+ local_url = local_url.to_s[(url_prefix.length - 1)..-1] if local_url.to_s.index(url_prefix) == 0
57
+ file_path = public_root_path.join(local_url[1..-1]).cleanpath.relative_path_from(public_root_path).to_s
58
+ end
59
+ return "#{url_prefix[0..-2]}#{CdnHelpers::AssetPath.hash_file("/" + file_path, public_root_path, logger)}"
60
+ end
61
+
62
+ def self.process_document(html, file_path, public_root_path, asset_hosts, logger)
63
+ to_handle = {
64
+ "link[rel=stylesheet]" => "href",
65
+ "script[src]" => "src",
66
+ "img" => "src",
67
+ 'link[rel="shortcut icon"]' => "href"
68
+ }
69
+
70
+ to_handle.each do |selector, attribute|
71
+ html.search(selector).each do |elem|
72
+ if URI.parse(elem[attribute]).scheme.nil?
73
+ elem[attribute] = asset_hosts.sample + process_asset_url(elem[attribute], file_path, public_root_path, logger)
74
+ end
75
+ end
76
+ end
77
+
78
+ # The contents of any style elements should be processed as CSS files
79
+ require 'stringio'
80
+ html.search('style').each do |elem|
81
+ pseudo_css_file = StringIO.new(elem.content)
82
+ processed_css = CssRewriter.process_css_file(logger, pseudo_css_file, file_path, public_root_path, asset_hosts.sample + "/")
83
+ elem.content = processed_css
84
+ end
85
+
86
+ html
87
+ end
88
+
89
+ end
90
+
91
+ module CssRewriter
92
+ def self.rewrite_css_file(logger, css_file_path, public_root_path, url_prefix = '/')
93
+ logger.info("Rewriting CSS file URLs in #{css_file_path}")
94
+ css_file = File.open(css_file_path)
95
+ output = process_css_file(logger, css_file, css_file_path, public_root_path, url_prefix)
96
+ File.open(css_file_path, 'w') { |f| f.write(output) }
97
+ end
98
+
99
+ def self.process_css_file(logger, css_file, css_file_path, public_root_path, url_prefix = '/')
100
+ out_lines = []
101
+ css_file_path = Pathname.new(css_file_path).realpath
102
+ public_root_path = Pathname.new(public_root_path)
103
+ context_path = css_file_path.parent
104
+ while line = css_file.gets
105
+ out_lines << line.gsub(/url\(["']?([^"'\)]+)["']?\)/) do |url_match|
106
+ if URI.parse($1).scheme.nil?
107
+ local_url = Pathname.new($1)
108
+ if local_url.relative?
109
+ file_path = context_path.join(local_url).cleanpath.relative_path_from(public_root_path).to_s
110
+ else
111
+ url_prefix = url_prefix + '/' unless url_prefix.rindex('/') == (url_prefix.length - 1)
112
+
113
+ if local_url.to_s.index(url_prefix) == 0
114
+ local_url = local_url.to_s[(url_prefix.length - 1)..-1]
115
+ else
116
+ local_url = local_url.to_s
117
+ end
118
+
119
+ file_path = public_root_path.join(local_url[1..-1]).cleanpath.relative_path_from(public_root_path).to_s
120
+ end
121
+ "url(#{url_prefix[0..-2]}#{CdnHelpers::AssetPath.hash_file("/" + file_path, public_root_path, logger)})"
122
+ else
123
+ "url(#{$1})"
124
+ end
125
+ end
126
+ end
127
+ css_file.close
128
+ out_lines.join("")
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,21 @@
1
+ namespace :cdn do
2
+ desc "Rewrite CSS files under ./public/ with CDN-friendly URLs"
3
+ task :css_urls => :environment do
4
+ require 'cdn_helpers'
5
+ require 'pathname'
6
+ require 'logger'
7
+
8
+ CdnHelpers::AssetPath.set_hash_salt(ENV['CDN_HASH_SALT']) if ENV['CDN_HASH_SALT']
9
+
10
+ begin
11
+ public_root_path = Pathname.new('public').realpath
12
+ logger = Logger.new(STDOUT)
13
+ logger.level = Logger::INFO
14
+ FileList['public/**/*.css'].each do |css_file_path|
15
+ css_file_path = Pathname.new(css_file_path).realpath
16
+ CdnHelpers::CssRewriter.rewrite_css_file(logger, css_file_path, public_root_path)
17
+ end
18
+ rescue Errno::ENOENT
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'cdn_helpers'
2
+ require 'rails'
3
+
4
+ module CdnHelpers
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load "cdn_helpers/cdn_helpers.rake"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe CdnHelpers::CssRewriter do
5
+ def css_path
6
+ File.expand_path('fixtures/dir/spec.css', File.dirname(__FILE__))
7
+ end
8
+
9
+ def public_root_path
10
+ Pathname.new(File.expand_path('fixtures', File.dirname(__FILE__)))
11
+ end
12
+
13
+ class MockLogger
14
+ attr_reader :logged
15
+
16
+ def initialize
17
+ @logged = []
18
+ end
19
+
20
+ def warn(msg)
21
+ @logged << msg
22
+ end
23
+ end
24
+
25
+ it "correctly rewrites a relative URL (no ../)" do
26
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new('background-image: url("eg.txt")'), css_path, public_root_path).
27
+ should == 'background-image: url(/dir/eg.txt/1ba05a2dX.txt)'
28
+ end
29
+
30
+ it "correctly rewrites a relative URL (with ../)" do
31
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new("background-image: url('../eg.txt')"), css_path, public_root_path).
32
+ should == 'background-image: url(/eg.txt/13c1f102X.txt)'
33
+ end
34
+
35
+ it "correctly rewrites an absolute local URL (starts with /)" do
36
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new('background-image: url("/eg.txt")'), css_path, public_root_path).
37
+ should == 'background-image: url(/eg.txt/13c1f102X.txt)'
38
+ end
39
+
40
+ it "correctly rewrites an absolute local URL given a non-/ URL prefix" do
41
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new('background-image: url("/prefix/eg.txt")'), css_path, public_root_path, '/prefix/').
42
+ should == 'background-image: url(/prefix/eg.txt/13c1f102X.txt)'
43
+ end
44
+
45
+ it "correctly ignores an http:// url" do
46
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new('background-image: url("http://things.com/nice.png")'), css_path, public_root_path).
47
+ should == 'background-image: url(http://things.com/nice.png)'
48
+ end
49
+
50
+ it "logs and passes-through an unfound file" do
51
+ logger = MockLogger.new
52
+ CdnHelpers::CssRewriter.process_css_file(logger, StringIO.new('background-image: url("/nice.png")'), css_path, public_root_path).
53
+ should == 'background-image: url(/nice.png)'
54
+ logger.logged.should include("Cannot rewrite URL for /nice.png: File not found")
55
+ end
56
+
57
+ context "Salting the hash" do
58
+ it "correctly rewrites an absolute local URL (starts with /)" do
59
+ CdnHelpers::AssetPath.set_hash_salt('Y')
60
+ CdnHelpers::CssRewriter.process_css_file(MockLogger.new, StringIO.new('background-image: url("/other_asset.txt")'), css_path, public_root_path).
61
+ should == 'background-image: url(/other_asset.txt/56f0cb6dY.txt)'
62
+ end
63
+ end
64
+ end
@@ -0,0 +1 @@
1
+ Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly. Some more text, repeatedly repeated repeatedly.
@@ -0,0 +1 @@
1
+ /* Here to keep Pathname happy */
@@ -0,0 +1 @@
1
+ Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly. Some text, repeated repeatedly.
@@ -0,0 +1 @@
1
+ Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly. Some other text, repeatedly repeated repeatedly.
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.expand_path(".")) unless $:.include?(File.expand_path("."))
2
+ $:.unshift(File.expand_path("../lib")) unless $:.include?(File.expand_path("../lib"))
3
+
4
+ require 'bundler'
5
+ Bundler.require :default, :test
6
+
7
+ require 'rspec/core'
8
+ require 'rspec/expectations'
9
+ require 'rspec/matchers'
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cdn_helpers
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.9'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Patterson
9
+ - James Stewart
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-01 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: actionpack
17
+ requirement: &70238319517500 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70238319517500
26
+ - !ruby/object:Gem::Dependency
27
+ name: nokogiri
28
+ requirement: &70238319517080 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70238319517080
37
+ - !ruby/object:Gem::Dependency
38
+ name: rake
39
+ requirement: &70238319516660 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70238319516660
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: &70238319516160 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 2.5.0
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70238319516160
59
+ description: Provides CDN helper methods for Rails as used in the Alpha.gov.uk project
60
+ email:
61
+ - matt@constituentparts.com
62
+ - james.stewart@digital.cabinet-office.gov.uk
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - lib/cdn_helpers/cdn_helpers.rake
68
+ - lib/cdn_helpers/railtie.rb
69
+ - lib/cdn_helpers.rb
70
+ - README.md
71
+ - Gemfile
72
+ - Rakefile
73
+ - spec/cdn_helpers_spec.rb
74
+ - spec/fixtures/dir/eg.txt
75
+ - spec/fixtures/dir/spec.css
76
+ - spec/fixtures/eg.txt
77
+ - spec/fixtures/other_asset.txt
78
+ - spec/spec_helper.rb
79
+ homepage: http://github.com/alphagov/cdn_helpers
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project: cdn_helpers
99
+ rubygems_version: 1.8.10
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Provides CDN helper methods for Rails
103
+ test_files:
104
+ - spec/cdn_helpers_spec.rb
105
+ - spec/fixtures/dir/eg.txt
106
+ - spec/fixtures/dir/spec.css
107
+ - spec/fixtures/eg.txt
108
+ - spec/fixtures/other_asset.txt
109
+ - spec/spec_helper.rb