cdn 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35a4d354f84113e85ad19fa8079627adcb98af02
4
+ data.tar.gz: eecc6b5b759fecb65e8ef1374da6f8c578277da6
5
+ SHA512:
6
+ metadata.gz: 23f852bb011c006223e38e3fb431125bff42a1459da472346cd6ec461c7ebbad1443602340cbf15668014fe5850e91ffcccd2bcf9041cb356a006b9c639eafe8
7
+ data.tar.gz: cba29e963508e0601580d05a2ed4da0475545015c5d50b9ed2d36c9ef608d41e722991d89408a9abc3d3cd97aa070ac164ed29ff0762e7379ee52879563e9452
@@ -0,0 +1,18 @@
1
+ .DS_Store
2
+ /.env
3
+ /.bundle/
4
+ /.yardoc
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /spec/cassettes/**
11
+ /tmp/
12
+ *.bundle
13
+ *.so
14
+ *.o
15
+ *.a
16
+ .ruby-gemset
17
+ mkmf.log
18
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activesupport'
4
+ gem 'aws_cf_signer'
5
+ gem 'timecop'
6
+
7
+ group :development do
8
+ gem 'rake'
9
+ gem 'rspec-core'
10
+ end
@@ -0,0 +1,35 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (4.0.0)
5
+ i18n (~> 0.6, >= 0.6.4)
6
+ minitest (~> 4.2)
7
+ multi_json (~> 1.3)
8
+ thread_safe (~> 0.1)
9
+ tzinfo (~> 0.3.37)
10
+ atomic (1.1.10)
11
+ aws_cf_signer (0.1.1)
12
+ i18n (0.6.4)
13
+ minitest (4.7.5)
14
+ multi_json (1.7.7)
15
+ rake (11.1.2)
16
+ rspec-core (3.4.4)
17
+ rspec-support (~> 3.4.0)
18
+ rspec-support (3.4.1)
19
+ thread_safe (0.1.0)
20
+ atomic
21
+ timecop (0.6.1)
22
+ tzinfo (0.3.37)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ activesupport
29
+ aws_cf_signer
30
+ rake
31
+ rspec-core
32
+ timecop
33
+
34
+ BUNDLED WITH
35
+ 1.10.5
@@ -0,0 +1,48 @@
1
+ # CDN Gem
2
+
3
+ Provides an interface to various cdn services.
4
+
5
+ ## Configure
6
+ ```ruby
7
+ CDN.configure do |config|
8
+ config.enabled = true
9
+ config.provider = "MyProvider"
10
+ config.domain = "mydomain.com"
11
+ config.http_large_secret = "secret"
12
+ config.http_small_secret = "secret"
13
+ end
14
+ ```
15
+
16
+ The CDN module uses provider classes for
17
+ the actual implementation of each service.
18
+ A provider class needs to implement two methods
19
+ for compliance.
20
+
21
+ ```ruby
22
+ generate_token(file_name, options)
23
+ generate_url(options)
24
+ ```
25
+
26
+ Each of the methods should return a string
27
+ To use the helper, include the module.
28
+
29
+ ```ruby
30
+ class ApplicationController
31
+ include CDN
32
+ end
33
+ ```
34
+
35
+ And then simply call the helper.
36
+
37
+ ```ruby
38
+ cdn_large_url("/path/to/my/file")
39
+ ```
40
+
41
+ It can also be called with options.
42
+
43
+ ```ruby
44
+ cdn_large_url("/path/to/my/file",
45
+ protocol: "https",
46
+ domain: "mydomain.com",
47
+ token: { expires_in: 1.day })
48
+ ```
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ namespace :spec do
6
+ RSpec::Core::RakeTask.new(:normal) do |t|
7
+ t.pattern ='spec/**/*_spec.rb'
8
+ t.rcov = false
9
+ end
10
+ end
11
+
12
+ desc 'RSpec tests'
13
+ task spec: 'spec:normal'
14
+
15
+ task default: 'spec'
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cdn/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'cdn'
8
+ s.version = CDN::VERSION
9
+ s.authors = ['Russ Smith (russ@bashme.org)']
10
+ s.email = 'russ@bashme.org'
11
+ s.homepage = 'http://github.com/sscgroup/cdn'
12
+ s.summary = 'CDN support for various providers.'
13
+ s.description = 'CDN support for various providers.'
14
+
15
+ s.files = `git ls-files -z`.split("\x0")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ['lib']
19
+ s.add_dependency('activesupport', '>= 2.3.10')
20
+ s.add_dependency('aws_cf_signer', '~> 0.1')
21
+ s.add_dependency('erubis')
22
+ s.add_development_dependency('rake')
23
+ s.add_development_dependency('rspec', '~> 2.5')
24
+ s.add_development_dependency('yard')
25
+ s.add_development_dependency('timecop')
26
+ end
@@ -0,0 +1,78 @@
1
+ require 'openssl'
2
+ require 'uri'
3
+ require 'cgi'
4
+ require 'aws_cf_signer'
5
+ require 'active_support/core_ext'
6
+ require 'active_support/inflector'
7
+
8
+ require 'cdn/configuration'
9
+ require 'cdn/providers/choopa'
10
+ require 'cdn/providers/swiftwill'
11
+ require 'cdn/providers/cloudfront'
12
+
13
+ # CDN helper methods.
14
+ #
15
+ module CDN
16
+ def self.configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def self.configure
21
+ config = configuration
22
+ yield(config)
23
+ end
24
+
25
+ def self.cdn_provider
26
+ "CDN::#{self.configuration.provider}".constantize
27
+ end
28
+
29
+ # Generates a CDN url for the path and options given.
30
+ # Uses the set CDN provider given in the configuration.
31
+ #
32
+ # path - Relative path for file.
33
+ # options - Hash of options given to the generator.
34
+ # Also takes a :token to customize the token generator.
35
+ #
36
+ # Returns a url.
37
+
38
+ def cdn_url(path, options = {})
39
+ return path unless CDN.configuration.enabled
40
+
41
+ options[:token] ||= {}
42
+ options[:domain] ||= CDN.configuration.domain
43
+ path = CDN.configuration.path_processor.call(path) if CDN.configuration.path_processor
44
+
45
+ url = CDN.cdn_provider.generate_url(
46
+ protocol: options[:protocol] || CDN.configuration.protocol || :http,
47
+ domain: options[:domain],
48
+ path: URI.parse(path).path,
49
+ token: CDN.cdn_provider.generate_token(path, options))
50
+
51
+ # Preserving any existing query params.
52
+ if query = URI.parse(path).query
53
+ query = CGI.parse(query)
54
+ query = query.merge(CGI.parse(url.query.to_s))
55
+ url.query = query.collect { |k,v| (v.empty?) ? k : "#{k}=#{v[0]}" }.join("&")
56
+ end
57
+
58
+ url.to_s
59
+ end
60
+
61
+ # Alias for cdn_url with :cdn_type set to large.
62
+ #
63
+ CDN.CDN.# Returns a url.
64
+
65
+ def cdn_large_url(path, options = {})
66
+ cdn_url(path, { :token => { :cdn_type => :large }}.merge(options))
67
+ end
68
+
69
+ # Alias for cdn_url with :cdn_type set to small.
70
+ #
71
+ # Returns a url.
72
+
73
+ def cdn_small_url(path, options = {})
74
+ options[:domain] ||= CDN.configuration.small_domain
75
+ options[:domain] ||= CDN.configuration.domain
76
+ cdn_url(path, { token: { cdn_type: :small }}.merge(options))
77
+ end
78
+ end
@@ -0,0 +1,14 @@
1
+ module CDN
2
+ class Configuration
3
+ attr_accessor :enabled
4
+ attr_accessor :provider
5
+ attr_accessor :domain
6
+ attr_accessor :small_domain
7
+ attr_accessor :http_large_secret
8
+ attr_accessor :http_small_secret
9
+ attr_accessor :protocol
10
+ attr_accessor :path_processor
11
+ attr_accessor :aws_pem_file
12
+ attr_accessor :ntp_drift_seconds
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module CDN
2
+ class Choopa
3
+ def self.generate_token(path, options = {})
4
+ self.new.generate_token(path, options)
5
+ end
6
+
7
+ def self.generate_url(options = {})
8
+ ((options[:protocol] == :https) ? URI::HTTPS : URI::HTTP).build(
9
+ host: options[:domain],
10
+ path: options[:path],
11
+ query: options[:token])
12
+ end
13
+
14
+ def generate_token(path, options = {})
15
+ return '' if options[:token] && options[:token][:cdn_type].to_s == 'small'
16
+
17
+ options[:start] ||= 0
18
+ options[:expires_in] ||= 600
19
+
20
+ expires_at = (options[:expires_in].is_a?(Fixnum)) ? Time.now.to_i + options[:expires_in] : options[:expires_in].to_i
21
+ expires_at_hex = expires_at.to_s(16)
22
+ hash = Digest::MD5.hexdigest(File.join(path, CDN.configuration.http_large_secret, expires_at_hex))
23
+
24
+ "e=#{expires_at_hex}&h=#{hash}&start=#{options[:start]}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ module CDN
2
+ class Cloudfront
3
+ def self.generate_token(path, options = {})
4
+ self.new.generate_token(path, options)
5
+ end
6
+
7
+ def self.generate_url(options = {})
8
+ ((options[:protocol] == :https) ? URI::HTTPS : URI::HTTP).build(
9
+ host: options[:domain],
10
+ path: options[:path],
11
+ query: options[:token])
12
+ end
13
+
14
+ def generate_token(path, options = {})
15
+ url = if options[:token] && options[:token][:cdn_type].to_s == 'small'
16
+ AwsCfSigner.new(CDN.configuration.aws_pem_file).sign(
17
+ "http://#{options[:domain]}#{path}",
18
+ { ending: Time.now.utc + 1.year })
19
+ else
20
+ AwsCfSigner.new(CDN.configuration.aws_pem_file).sign(
21
+ "http://#{options[:domain]}#{path}",
22
+ self.normalize_options(options))
23
+ end
24
+
25
+ url.split('?').last
26
+ end
27
+
28
+ protected
29
+
30
+ def normalize_options(options)
31
+ params = {}
32
+ params[:ip_range] = options[:allow_ip] if options[:allow_ip]
33
+ params[:ending] = (options[:expires_in].is_a?(Fixnum)) ? Time.now.utc + options[:expires_in] : Time.now.utc + 1.hour
34
+ params
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ module CDN
2
+ class Swiftwill
3
+ def self.generate_token(path, options = {})
4
+ self.new.generate_token(path, options)
5
+ end
6
+
7
+ def self.generate_url(options = {})
8
+ ((options[:protocol] == :https) ? URI::HTTPS : URI::HTTP).build(
9
+ host: options[:domain],
10
+ path: options[:path],
11
+ query: options[:token])
12
+ end
13
+
14
+ def generate_token(path, options = {})
15
+ return '' if options[:token] && options[:token][:cdn_type].to_s == 'small'
16
+ start = options[:start] || 0
17
+ options = self.normalize_options(options)
18
+ hash_string = "#{path}?" + params_string(options)
19
+ options.delete(:clientip)
20
+ params_string(options) + '&hash=0' + hash_string(hash_string, CDN.configuration.http_large_secret) + "&start=#{start}"
21
+ end
22
+
23
+ protected
24
+
25
+ def hash_string(string, secret)
26
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("sha1"), secret, string.to_s)[0..19]
27
+ end
28
+
29
+ def params_string(options)
30
+ options.collect { |k,v| "#{k}=#{v}" }.join("&")
31
+ end
32
+
33
+ def normalize_options(options)
34
+ params = {}
35
+ options[:expires_in] ||= 600
36
+ options[:expires_in] += ntp_drift_seconds
37
+ params[:clientip] = options[:allow_ip] if options[:allow_ip]
38
+ params[:nvb] = (Time.now - ntp_drift_seconds).utc.strftime("%Y%m%d%H%M%S")
39
+ params[:nva] = ((options[:expires_in].is_a?(Fixnum)) ? Time.now.utc + options[:expires_in] : options[:expires_in]).utc.strftime("%Y%m%d%H%M%S")
40
+ params
41
+ end
42
+
43
+ def ntp_drift_seconds
44
+ (CDN.configuration.ntp_drift_seconds || 300).to_i
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module CDN
2
+ VERSION = '0.1.2'
3
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ describe CDN do
4
+ include CDN
5
+
6
+ before do
7
+ CDN.configure do |config|
8
+ config.enabled = true
9
+ config.provider = "TestProvider"
10
+ config.domain = "localhost"
11
+ config.http_large_secret = "secret"
12
+ config.http_small_secret = "secret"
13
+ end
14
+ end
15
+
16
+ describe "cdn_url" do
17
+ it "appends to the query string if it already exists" do
18
+ url = cdn_url("/system/content/videos/4810/screenshot_9484_thumb.jpg?1306909458")
19
+ url.should match(/1306909458/)
20
+ url.should match(/testtoken/)
21
+ end
22
+ end
23
+
24
+ describe "cdn_large_url" do
25
+ it "returns a url with a token" do
26
+ url = cdn_large_url("/my/file")
27
+ url.should match(/testtoken/)
28
+ end
29
+ end
30
+
31
+ describe "cdn_small_url" do
32
+ it "returns a url with a token" do
33
+ url = cdn_small_url("/my/file")
34
+ url.should match(/testtoken/)
35
+ end
36
+ end
37
+
38
+ context "with a custom path processor" do
39
+ before do
40
+ CDN.configure do |config|
41
+ config.path_processor = lambda { |p| "/modifiedpath" }
42
+ end
43
+ end
44
+
45
+ it "modifies the path before generating the url" do
46
+ url = cdn_url("/my/file")
47
+ url.should match(/modifiedpath/)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXQIBAAKBgQDlxuuGAVeWkhnXO2RxJOztTJRnGVwK9sobO+qBdRKml2nJ1Tjs
3
+ 3quzBnrA7pQp3gws7Jdcs0tfsQRVNzPOrGsEZFc3mLC5UTIxXvXfhDsUcB5lcodk
4
+ KXN9XRrJzxZETR9wIDusQ472ps+uuoe7N/dfDbU9c76JWmBOciiUgUNiNQIDAQAB
5
+ AoGBAMPNohBcfm4R7DFMLEh6ZefFdUTyCExTh2Bzy18sC4uoxX43072N9pSlNd/I
6
+ rruKu6dgqY9WPim6J+4SD3TT2pCS6vQ3oOr8X8wn1YAUI0IoBeAo1wzOMf86uaaU
7
+ lYAyoVmHA7gU6mplcCQVG+OBBKFwRqmkMEQ5Fr5/rTq58LxhAkEA+5e7atsmb+OX
8
+ sDVul0KT4NtWkoscrFE58lrtgSnxajMwjrgq7gWaU4wA/7InG9m0QXoFjzVTs4M8
9
+ PYGvFe8sWQJBAOnNWwHDdt3BZf23amw15MqBOrsdo2e0a7TlO2zHAJJgLiqC2pXa
10
+ CpbnGBqX238DmJ0ZQZalWA/ZmAJitBTWOT0CQCh9Q8EvoaDyHLBT+QdIxKm0Qtt0
11
+ ndmxfMTfE0ftKxfvQwpE40vJBQwbBPIhhp+5yoObEpf4eg7yuNghLb827YkCQD4d
12
+ 9UPurrIUJAjYpknBt9ulofj80uFGqicRkExCQwCg1SMuSOzvVUjqdRj7p7GDPxTe
13
+ FPL1UwRDB4P6vWWb1PECQQD1/CntgLp34FikMJ/AZTAxtgBMMdyncQH2jxntn+pK
14
+ kh3IndxZ5SXQL+/ur7eJozisxN8isXaRK1Gq9swmEmka
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe CDN::Choopa do
4
+ before do
5
+ CDN.configure do |config|
6
+ config.http_large_secret = "secret"
7
+ end
8
+ end
9
+
10
+ context "when cdn type is small" do
11
+ it "returns an empty string" do
12
+ token = CDN::Choopa.generate_token("/my/file", :token => { :cdn_type => :small })
13
+ token.should == ""
14
+ end
15
+ end
16
+
17
+ context "when cdn type is large or nothing" do
18
+ it "generates a hash of 20 characters" do
19
+ token = CDN::Choopa.generate_token("/my/file")
20
+ token.should match(/h=([0-9a-z]{32})/)
21
+ end
22
+
23
+ it "generates a hash with an expiration" do
24
+ token = CDN::Choopa.generate_token("/my/file")
25
+ token.should match(/e=([0-9a-z]{8})/)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe CDN::Cloudfront do
4
+ let(:time_format) { "%Y%m%d%H%M%S" }
5
+
6
+ before do
7
+ CDN.configure do |config|
8
+ config.domain = "somedomain.cloudfront.net"
9
+ config.http_large_secret = "secret"
10
+ config.aws_pem_file = File.join(File.dirname(__FILE__), "..", "pk-APKAIBJYP63EELSELVPQ.pem")
11
+ end
12
+ end
13
+
14
+ context "when cdn type is small" do
15
+ it "returns a open ended url with an expires time" do
16
+ token = CDN::Cloudfront.generate_token("/my/file", :token => { :cdn_type => :small })
17
+ token.should match(/Expires=/)
18
+ token.should match(/Signature=/)
19
+ end
20
+ end
21
+
22
+ context "when cdn type is large or nothing" do
23
+ it "generates a signed url" do
24
+ token = CDN::Cloudfront.generate_token("/my/file", allow_ip: "127.0.0.1")
25
+ token.should match(/Policy=/)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe CDN::Swiftwill do
4
+ let(:time_format) { "%Y%m%d%H%M%S" }
5
+ let(:ntp_drift) { 30.seconds.to_i }
6
+
7
+ before do
8
+ CDN.configure do |config|
9
+ config.http_large_secret = "secret"
10
+ config.ntp_drift_seconds = ntp_drift
11
+ end
12
+ end
13
+
14
+ context "when cdn type is small" do
15
+ it "returns an empty string" do
16
+ token = CDN::Swiftwill.generate_token("/my/file", :token => { :cdn_type => :small })
17
+ token.should == ""
18
+ end
19
+ end
20
+
21
+ context "when cdn type is large or nothing" do
22
+ it "generates a hash of 20 characters" do
23
+ token = CDN::Swiftwill.generate_token("/my/file")
24
+ token.should match(/hash=0([0-9A-Za-z]{20})/)
25
+ end
26
+
27
+ it "generates a hash based on a client ip" do
28
+ token = CDN::Swiftwill.generate_token("/my/file", :allow_ip => "192.168.0.1")
29
+ token.should match(/hash=0([0-9A-Za-z]{20})/)
30
+ token.should_not match(/clientip/)
31
+ token.should_not match(/allow_ip/)
32
+ end
33
+
34
+ context "based on expiration" do
35
+ context "when expires_in is empty" do
36
+ it "generates a hash with an expiration of 0" do
37
+ token = CDN::Swiftwill.generate_token("/my/file")
38
+ token.should match(/hash=0([0-9A-Za-z]{20})/)
39
+ token.should match(/nvb=\d{14}/)
40
+ token.should match(/nva=\d{14}/)
41
+ end
42
+ end
43
+
44
+ context "when expires_in is not empty" do
45
+ it "generates a hash based on the given expiration" do
46
+ Timecop.freeze(Time.now.utc) do
47
+ token = CDN::Swiftwill.generate_token("/my/file", :expires_in => (Time.now.utc + 1.day))
48
+ token.should match(/hash=0([0-9A-Za-z]{20})/)
49
+ token.should match(/nvb=#{(Time.now.utc - ntp_drift).strftime(time_format)}/)
50
+ token.should match(/nva=#{(Time.now.utc + ntp_drift + 1.day).strftime(time_format)}/)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ require "cdn"
2
+ require "timecop"
3
+
4
+ module CDN
5
+ class TestProvider
6
+ def self.generate_token(file_name, options = {})
7
+ "testtoken"
8
+ end
9
+
10
+ def self.generate_url(options = {})
11
+ ((options[:protocol] == :https) ? URI::HTTPS : URI::HTTP).build(
12
+ :host => options[:domain],
13
+ :path => options[:path],
14
+ :query => options[:token])
15
+ end
16
+ end
17
+ end
18
+
19
+ RSpec.configure do |config|
20
+ config.filter_run :focus => true
21
+ config.run_all_when_everything_filtered = true
22
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cdn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Russ Smith (russ@bashme.org)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.10
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.10
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws_cf_signer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: erubis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: CDN support for various providers.
112
+ email: russ@bashme.org
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - ".gitignore"
118
+ - Gemfile
119
+ - Gemfile.lock
120
+ - README.md
121
+ - Rakefile
122
+ - cdn.gemspec
123
+ - lib/cdn.rb
124
+ - lib/cdn/configuration.rb
125
+ - lib/cdn/providers/choopa.rb
126
+ - lib/cdn/providers/cloudfront.rb
127
+ - lib/cdn/providers/swiftwill.rb
128
+ - lib/cdn/version.rb
129
+ - spec/cdn_spec.rb
130
+ - spec/pk-APKAIBJYP63EELSELVPQ.pem
131
+ - spec/providers/choopa_spec.rb
132
+ - spec/providers/cloudfront_spec.rb
133
+ - spec/providers/swiftwill_spec.rb
134
+ - spec/spec_helper.rb
135
+ homepage: http://github.com/sscgroup/cdn
136
+ licenses: []
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.4.5
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: CDN support for various providers.
158
+ test_files:
159
+ - spec/cdn_spec.rb
160
+ - spec/pk-APKAIBJYP63EELSELVPQ.pem
161
+ - spec/providers/choopa_spec.rb
162
+ - spec/providers/cloudfront_spec.rb
163
+ - spec/providers/swiftwill_spec.rb
164
+ - spec/spec_helper.rb