cdn 0.1.2

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.
@@ -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