middleman-keycdn 0.1

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.
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: []