middleman-keycdn 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d6f2697cd177060013668ec2337024217372440c
4
+ data.tar.gz: d99041e88c39da3264e993975f15e32845690916
5
+ SHA512:
6
+ metadata.gz: 33b4558ede706d4f47565533c946426b877d1d7f5df0f83e9dc8cca893833f60017f83f90191add988d8146336c86ffd8fd7ecab8718c1aaa0189b42d75a96cd
7
+ data.tar.gz: 30b575e433fec23bb8e135a70b0fcfd9bec3176b8e5569fb54d180ca208f964a9ac555328c57eabf62440c52dbfd7f0bc5e66d6fb441385b45f37c67073d26bc
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ Gemfile.lock
2
+ coverage
3
+ .ruby-version
4
+ *.sublime-*
5
+ *.gem
6
+ pkg
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ before_script:
5
+ - gem update --system
6
+ - gem update bundler
7
+ rvm:
8
+ - 2.2.5
9
+ - 2.3.1
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ guard 'rspec', cli: '--drb --profile', all_after_pass: false do
2
+ # Specs
3
+ watch(%r(^spec/.+_spec\.rb$))
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
+ watch(%r(^spec/support/(.+)\.rb$)) { 'spec' }
6
+
7
+ # Files
8
+ watch(%r(^lib/(.+)\.rb$)) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Andrey Korzhuev <andrew@korzhuev.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Middleman KeyCDN [![Build Status](https://travis-ci.org/ideasasylum/middleman-keycdn.svg?branch=master)](https://travis-ci.org/ideasasylum/middleman-keycdn) [![Dependency Status](https://gemnasium.com/ideasasylum/middleman-keycdn.png)](https://gemnasium.com/ideasasylum/middleman-keycdn) [![Code Climate](https://codeclimate.com/github/ideasasylum/middleman-keycdn.png)](https://codeclimate.com/github/ideasasylum/middleman-keycdn)
2
+
3
+ A deploying tool for middleman which invalidates a [KeyCDN](https://www.keycdn.com/?a=25888) cache.
4
+
5
+ Based almost entirely on [middleman-cloudfront](https://github.com/andrusha/middleman-cloudfront) and used together with [middleman-s3_sync](https://github.com/fredjean/middleman-s3_sync) (though you can use whatever deployment method & KeyCDN source you want)
6
+
7
+ Some of its features are:
8
+
9
+ * KeyCDN cache invalidation &mdash; either purge the whole cache or just updated selected URLs;
10
+ * Ability to call it from command line and after middleman build;
11
+ * Ability to filter files which are going to be invalidated by regex;
12
+
13
+ # Usage
14
+
15
+ ## Installation
16
+
17
+ Add this to `Gemfile`:
18
+ ```ruby
19
+ gem 'middleman-keycdn'
20
+ ```
21
+
22
+ Then run:
23
+ ```
24
+ bundle install
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Edit `config.rb` and add:
30
+
31
+ ```ruby
32
+ activate :keycdn do |cdn|
33
+ cdn.api_key = ENV['KEYCDN_API_KEY']
34
+ cdn.zone_id = ENV['KEYCDN_ZONE_ID']
35
+ cdn.base_url = ENV['KEYCDN_BASE_URL']
36
+ cdn.purge_all = true
37
+ cdn.filter = /.html$/i # default is /.*/
38
+ cdn.after_build = true # default is false
39
+ end```
40
+
41
+ ## Running
42
+
43
+ If you set `after_build` to `true` cache would be automatically invalidated after build:
44
+ ```bash
45
+ bundle exec middleman build
46
+ ```
47
+
48
+ Otherwise you should run it through commandline interface like so:
49
+ ```bash
50
+ bundle exec middleman invalidate
51
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ require 'pathname' # for some reason, had to require this because middleman-core 4.1.7 did not.
2
+ require 'middleman-keycdn/extension'
3
+
4
+ Middleman::Extensions.register :keycdn, Middleman::KeyCDN::Extension
@@ -0,0 +1,152 @@
1
+ require 'middleman-cli'
2
+ require 'middleman-keycdn/extension'
3
+ require 'keycdn'
4
+ require 'ansi/code'
5
+
6
+ module Middleman
7
+ module Cli
8
+ module KeyCDN
9
+ # This class provides an "invalidate" command for the middleman CLI.
10
+ class Invalidate < ::Thor::Group
11
+ include Thor::Actions
12
+
13
+ INVALIDATION_LIMIT = 1000
14
+ INDEX_REGEX = /
15
+ \A
16
+ (.*\/)?
17
+ index\.html
18
+ \z
19
+ /ix
20
+
21
+ check_unknown_options!
22
+
23
+ def self.exit_on_failure?
24
+ true
25
+ end
26
+
27
+ def invalidate(opts = nil, files = nil)
28
+ @options = setup_options opts
29
+
30
+ ensure_required_options_present
31
+
32
+ if @options.purge_all
33
+ purge_all
34
+ else
35
+ purge_urls files
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def setup_options opts
42
+ # If called via commandline, discover config (from bin/middleman)
43
+ if opts.nil?
44
+ app = Middleman::Application.new do
45
+ config[:mode] = :config
46
+ config[:exit_before_ready] = true
47
+ config[:watcher_disable] = true
48
+ config[:disable_sitemap] = true
49
+ end
50
+
51
+ # Get the opts from the keycdn extension
52
+ extension = app.extensions[:keycdn]
53
+ unless extension.nil?
54
+ opts = extension.options
55
+ end
56
+ end
57
+
58
+ if opts.nil?
59
+ configuration_usage
60
+ end
61
+
62
+ opts
63
+ end
64
+
65
+ def ensure_required_options_present
66
+ [:api_key, :zone_id, :base_url].each do |key|
67
+ raise StandardError, "Configuration key #{key} is missing." if @options.public_send(key).nil?
68
+ end
69
+
70
+ if !@options.purge_all
71
+ [:base_url, :filter].each do |key|
72
+ raise StandardError, "Configuration key #{key} is missing." if @options.public_send(key).nil?
73
+ end
74
+ end
75
+ end
76
+
77
+ def purge_all
78
+ puts "Invalidating KeyCDN cache for #{@options.zone_id}"
79
+ response = cdn.get("zones/purge/#{@options.zone_id}.json")
80
+ print_status response
81
+ end
82
+
83
+ def purge_urls files
84
+ files = normalize_files(files || list_files(@options.filter))
85
+ return if files.empty?
86
+ urls = files.collect.with_index { |file, index| ["urls[#{index}]", "#{@options.base_url}#{file}"] }
87
+ puts "Invalidating #{files.count} urls."
88
+ # puts urls.inspect
89
+ response = cdn.del("zones/purgeurl/#{@options.zone_id}.json", Hash[urls])
90
+ print_status response
91
+ end
92
+
93
+ def print_status response
94
+ if response['status'] == 'success'
95
+ puts ANSI.green { "#{response['description']}" }
96
+ else
97
+ puts ANSI.red { "#{response['description']}" }
98
+ end
99
+ end
100
+
101
+ def cdn
102
+ ::KeyCDN::Client.new @options.api_key
103
+ end
104
+
105
+ def configuration_usage
106
+ raise StandardError, <<-TEXT
107
+ ERROR: You need to activate the keycdn extension in config.rb.
108
+
109
+ The example configuration is:
110
+ activate :keycdn do |cf|
111
+ cf.api_key = ENV['KEYCDN_API_KEY']
112
+ cf.zone_id = ENV['KEYCDN_ZONE_ID']
113
+ cf.base_url = ENV['KEYCDN_BASE_URL']
114
+ cf.purge_all = false
115
+ cf.filter = /.html$/i # default is /.*/
116
+ cf.after_build = true # default is false
117
+ end
118
+ TEXT
119
+ end
120
+
121
+ def list_files(filter)
122
+ Dir.chdir('build/') do
123
+ Dir.glob('**/*', File::FNM_DOTMATCH).tap do |files|
124
+ # Remove directories
125
+ files.reject! { |f| File.directory?(f) }
126
+
127
+ # Remove files that do not match filter
128
+ files.reject! { |f| f !~ filter }
129
+ end
130
+ end
131
+ end
132
+
133
+ def normalize_files(files)
134
+ # Add directories since they have to be invalidated
135
+ # as well if :directory_indexes is active
136
+ files += files.grep(INDEX_REGEX).map do |file|
137
+ file == 'index.html' ? '/' : File.dirname(file) << '/'
138
+ end.uniq
139
+
140
+ # URI encode and add leading slash
141
+ files.map { |f| URI::encode(f.start_with?('/') ? f : "/#{f}") }
142
+ end
143
+
144
+ # Add to CLI
145
+ Base.register(self, 'invalidate', 'invalidate', 'Invalidate a KeyCDN zone.')
146
+
147
+ # Map "inv" to "invalidate"
148
+ Base.map('inv' => 'invalidate')
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,32 @@
1
+ require 'middleman-core'
2
+ require 'middleman-keycdn/commands/invalidate'
3
+
4
+ module Middleman
5
+ module KeyCDN
6
+ class Extension < Middleman::Extension
7
+ # @param [Symbol] key The name of the option
8
+ # @param [Object] default The default value for the option
9
+ # @param [String] description A human-readable description of what the option does
10
+ option :api_key, nil, 'API key'
11
+ option :zone_id, nil, 'Zone id'
12
+ option :base_url, nil, 'The base url for the site (only required when purging individual urls'
13
+ option :purge_all, true, 'Purge the whole cache (true) or individual urls (false)'
14
+ option :filter, /.*/, 'Filter files to be invalidated'
15
+ option :after_build, false, 'Invalidate after build'
16
+
17
+ def initialize(app, options_hash={}, &block)
18
+ super
19
+ end
20
+
21
+ def after_build
22
+ Middleman::Cli::KeyCDN::Invalidate.new.invalidate(options) if options.after_build
23
+ end
24
+
25
+ helpers do
26
+ def invalidate(files = nil)
27
+ Middleman::Cli::KeyCDN::Invalidate.new.invalidate(options, files)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module Middleman
2
+ module KeyCDN
3
+ VERSION = '0.1'
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'middleman-keycdn'
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'middleman-keycdn/version'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'middleman-keycdn'
10
+ s.version = Middleman::KeyCDN::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.authors = ["Jamie Lawrence"]
13
+ s.email = ["jamie@ideasasylum.com"]
14
+ s.homepage = "https://github.com/ideasasylum/middleman-keycdn"
15
+ s.summary = %q{Invalidate KeyCDN cache after deployment to S3}
16
+ s.description = %q{Adds ability to invalidate a specific set of files in your KeyCDN cache}
17
+
18
+ s.files = `git ls-files -z`.split("\0")
19
+ s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0")
20
+ s.require_paths = ["lib"]
21
+
22
+ s.required_ruby_version = '>= 2.0.0'
23
+
24
+ s.add_development_dependency 'rake', '>= 0.9.0'
25
+ s.add_development_dependency 'rspec', '~> 3.0'
26
+
27
+ s.add_dependency 'middleman-core', '~> 4.0'
28
+ s.add_dependency 'middleman-cli', '~> 4.0'
29
+ s.add_dependency 'keycdn', '~> 0.1.1'
30
+ s.add_dependency 'ansi', '~> 1.5'
31
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe Middleman::Cli::KeyCDN::Invalidate do
4
+
5
+ let(:cdn) { described_class.new }
6
+ let(:zone) { 'zone123' }
7
+ let(:base_url) { 'example.com' }
8
+ let(:options) do
9
+ config = Middleman::Configuration::ConfigurationManager.new
10
+ config.api_key = 'api123'
11
+ config.zone_id = zone
12
+ config.base_url = base_url
13
+ config.purge_all = true
14
+ config.filter = /.html$/i # default is /.*/
15
+ config.after_build = true # default is false
16
+ config
17
+ end
18
+ let(:success) do { status: 'success', description: 'ok' } end
19
+ let(:purge_all_cmd) { "zones/purge/#{zone}.json" }
20
+ let(:purge_urls_cmd) { "zones/purgeurl/#{zone}.json" }
21
+
22
+ context 'purge all' do
23
+ it 'purge the zone' do
24
+ allow(cdn).to receive(:list_files).and_return([])
25
+ expect_any_instance_of(KeyCDN::Client).to receive(:get).with(purge_all_cmd).and_return(success)
26
+ cdn.invalidate(options)
27
+ end
28
+ end
29
+
30
+ context 'purge urls' do
31
+ before(:each) { options.purge_all = false }
32
+ it 'normalizes paths' do
33
+ files = %w(file directory/index.html)
34
+ normalized_files = %w(/file /directory/index.html /directory/)
35
+ allow(cdn).to receive(:list_files).and_return(files)
36
+ urls = {
37
+ 'urls[0]' => 'example.com/file',
38
+ 'urls[1]' => 'example.com/directory/index.html',
39
+ 'urls[2]' => 'example.com/directory/',
40
+ }
41
+ expect_any_instance_of(KeyCDN::Client).to receive(:del).with(purge_urls_cmd, urls).and_return(success)
42
+ cdn.invalidate(options)
43
+ end
44
+
45
+ context 'when files to invalidate are explicitly specified' do
46
+ it 'uses them instead of the files in the build directory' do
47
+ files = (1..3).map { |i| "/file_#{i}" }
48
+ urls = {
49
+ 'urls[0]' => 'example.com/file_1',
50
+ 'urls[1]' => 'example.com/file_2',
51
+ 'urls[2]' => 'example.com/file_3',
52
+ }
53
+ expect_any_instance_of(KeyCDN::Client).to receive(:del).with(purge_urls_cmd, urls).and_return(success)
54
+ cdn.invalidate(options, files)
55
+ end
56
+
57
+ it "doesn't filter them" do
58
+ files = (1..3).map { |i| "/file_#{i}" }
59
+ options.filter = /filter that matches no files/
60
+ urls = {
61
+ 'urls[0]' => 'example.com/file_1',
62
+ 'urls[1]' => 'example.com/file_2',
63
+ 'urls[2]' => 'example.com/file_3',
64
+ }
65
+ expect_any_instance_of(KeyCDN::Client).to receive(:del).with(purge_urls_cmd, urls).and_return(success)
66
+ cdn.invalidate(options, files)
67
+ end
68
+
69
+ it 'normalizes them' do
70
+ files = %w(file directory/index.html)
71
+ normalized_files = %w(/file /directory/index.html /directory/)
72
+ urls = {
73
+ 'urls[0]' => 'example.com/file',
74
+ 'urls[1]' => 'example.com/directory/index.html',
75
+ 'urls[2]' => 'example.com/directory/',
76
+ }
77
+ expect_any_instance_of(KeyCDN::Client).to receive(:del).with(purge_urls_cmd, urls).and_return(success)
78
+ cdn.invalidate(options, files)
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,7 @@
1
+ require 'middleman-keycdn'
2
+
3
+ RSpec.configure do |config|
4
+ config.run_all_when_everything_filtered = true
5
+ config.filter_run :focus
6
+ config.order = 'random'
7
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: middleman-keycdn
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Jamie Lawrence
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: middleman-core
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: middleman-cli
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: keycdn
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: ansi
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.5'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.5'
97
+ description: Adds ability to invalidate a specific set of files in your KeyCDN cache
98
+ email:
99
+ - jamie@ideasasylum.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Gemfile
107
+ - Guardfile
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - lib/middleman-keycdn.rb
112
+ - lib/middleman-keycdn/commands/invalidate.rb
113
+ - lib/middleman-keycdn/extension.rb
114
+ - lib/middleman-keycdn/version.rb
115
+ - lib/middleman_extension.rb
116
+ - middleman-keycdn.gemspec
117
+ - spec/lib/middleman-cloudfront/invalidate_spec.rb
118
+ - spec/spec_helper.rb
119
+ homepage: https://github.com/ideasasylum/middleman-keycdn
120
+ licenses: []
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 2.0.0
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.4.5.1
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Invalidate KeyCDN cache after deployment to S3
142
+ test_files: []