cdn_helpers 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README.md +69 -0
- data/Rakefile +6 -0
- data/lib/cdn_helpers.rb +131 -0
- data/lib/cdn_helpers/cdn_helpers.rake +21 -0
- data/lib/cdn_helpers/railtie.rb +10 -0
- data/spec/cdn_helpers_spec.rb +64 -0
- data/spec/fixtures/dir/eg.txt +1 -0
- data/spec/fixtures/dir/spec.css +1 -0
- data/spec/fixtures/eg.txt +1 -0
- data/spec/fixtures/other_asset.txt +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +109 -0
data/Gemfile
ADDED
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
data/lib/cdn_helpers.rb
ADDED
@@ -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,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.
|
data/spec/spec_helper.rb
ADDED
@@ -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
|