middleman-s3_sync 3.0.47 → 3.3.0
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 +4 -4
- data/.travis.yml +3 -2
- data/lib/middleman-s3_sync.rb +4 -3
- data/lib/middleman-s3_sync/commands.rb +16 -8
- data/lib/middleman-s3_sync/extension.rb +74 -37
- data/lib/middleman/s3_sync.rb +49 -40
- data/lib/middleman/s3_sync/caching_policy.rb +58 -0
- data/lib/middleman/s3_sync/options.rb +2 -84
- data/lib/middleman/s3_sync/resource.rb +28 -15
- data/lib/middleman/s3_sync/version.rb +1 -1
- data/middleman-s3_sync.gemspec +4 -3
- data/spec/caching_policy_spec.rb +80 -0
- data/spec/resource_spec.rb +16 -8
- data/spec/spec_helper.rb +0 -1
- metadata +25 -10
- data/spec/options_spec.rb +0 -130
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ea0d9098ccfca7a420089e791da7f7881298c5c
|
4
|
+
data.tar.gz: 93b501fb586db5be0cd329c448ac2edb5da3c044
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d4b328645fe0171b6b5f01c0b0f2f19428c17a06b0e0af8bfa9b908763982e5a12b52b2c33e7b1a57b4e7e6bdd424ae27e302d97b17db7a8f08654868239615
|
7
|
+
data.tar.gz: 9d427de8aca80db2ff476dc42da6a2fc11bb769651c8f16a24780dbc0b9a9ae51459a038ae8c85abff8a45c5728d511791254bdc8566368f8d066a1d0089453a
|
data/.travis.yml
CHANGED
data/lib/middleman-s3_sync.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'middleman-core'
|
2
|
+
require 'middleman-s3_sync/commands'
|
2
3
|
require 'middleman/s3_sync'
|
3
4
|
|
4
|
-
::Middleman::Extensions.register(:s3_sync
|
5
|
-
|
5
|
+
::Middleman::Extensions.register(:s3_sync) do
|
6
|
+
require 'middleman-s3_sync/extension'
|
7
|
+
::Middleman::S3SyncExtension
|
6
8
|
end
|
7
|
-
|
@@ -2,6 +2,8 @@ require 'middleman-core/cli'
|
|
2
2
|
|
3
3
|
module Middleman
|
4
4
|
module Cli
|
5
|
+
Base.map("sync" => "s3_sync")
|
6
|
+
|
5
7
|
class S3Sync < Thor
|
6
8
|
include Thor::Actions
|
7
9
|
|
@@ -13,11 +15,11 @@ module Middleman
|
|
13
15
|
true
|
14
16
|
end
|
15
17
|
|
16
|
-
desc "s3_sync", "
|
17
|
-
|
18
|
+
desc "s3_sync [options]", "Synchronizes a middleman site to an AWS S3 bucket"
|
19
|
+
class_option :force, type: :boolean,
|
18
20
|
desc: "Push all local files to the server",
|
19
21
|
aliases: '-f'
|
20
|
-
|
22
|
+
class_option :bucket, type: :string,
|
21
23
|
desc: "Specify which bucket to use, overrides the configured bucket.",
|
22
24
|
aliases: '-b'
|
23
25
|
method_option :prefix, type: :string,
|
@@ -26,25 +28,31 @@ module Middleman
|
|
26
28
|
method_option :verbose, type: :boolean,
|
27
29
|
desc: "Adds more verbosity...",
|
28
30
|
aliases: '-v'
|
31
|
+
class_option :dry_run, type: :boolean,
|
32
|
+
desc: "Performs a dry run of the sync",
|
33
|
+
aliases: '-n'
|
29
34
|
|
30
35
|
def s3_sync
|
31
|
-
|
32
|
-
|
36
|
+
::Middleman::S3Sync.app = ::Middleman::Application.server.inst
|
37
|
+
|
38
|
+
s3_sync_options = ::Middleman::S3Sync.s3_sync_options
|
39
|
+
|
40
|
+
bucket = s3_sync_options.bucket rescue nil
|
41
|
+
|
33
42
|
unless bucket
|
34
43
|
raise Thor::Error.new "You need to activate the s3_sync extension and at least provide the bucket name."
|
35
44
|
end
|
36
45
|
|
37
|
-
s3_sync_options = shared_inst.s3_sync_options
|
38
46
|
|
39
47
|
# Override options based on what was passed on the command line...
|
40
48
|
s3_sync_options.force = options[:force] if options[:force]
|
41
49
|
s3_sync_options.bucket = options[:bucket] if options[:bucket]
|
42
50
|
s3_sync_options.verbose = options[:verbose] if options[:verbose]
|
43
51
|
s3_sync_options.prefix = options[:prefix] if options[:prefix]
|
52
|
+
s3_sync_options.dry_run = options[:dry_run] if options[:dry_run]
|
44
53
|
|
45
|
-
::Middleman::S3Sync.sync(
|
54
|
+
::Middleman::S3Sync.sync()
|
46
55
|
end
|
47
56
|
end
|
48
|
-
Base.map('sync' => 's3_sync')
|
49
57
|
end
|
50
58
|
end
|
@@ -1,59 +1,96 @@
|
|
1
1
|
require 'middleman-core'
|
2
|
+
require 'middleman/s3_sync'
|
2
3
|
require 'map'
|
3
4
|
|
4
5
|
module Middleman
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
class S3SyncExtension < Extension
|
7
|
+
# Options supported by the extension...
|
8
|
+
option :prefix, nil, 'Path prefix of the resource we are looking for on the server.'
|
9
|
+
option :http_prefix, nil, 'Path prefix of the resources'
|
10
|
+
option :acl, 'public-read', 'ACL for the resources being pushed to S3'
|
11
|
+
option :bucket, 'nil', 'The name of the bucket we are pushing to.'
|
12
|
+
option :region, 'us-east-1', 'The name of the AWS region hosting the S3 bucket'
|
13
|
+
option :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID'] , 'The AWS access key id'
|
14
|
+
option :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY'], 'The AWS secret access key'
|
15
|
+
option :after_build, false, 'Whether to synchronize right after the build'
|
16
|
+
option :build_dir, nil, 'Where the built site is stored'
|
17
|
+
option :delete, true, 'Whether to delete resources that do not have a local equivalent'
|
18
|
+
option :encryption, false, 'Whether to encrypt the content on the S3 bucket'
|
19
|
+
option :force, false, 'Whether to push all current resources to S3'
|
20
|
+
option :prefer_gzip, true, 'Whether to push the compressed version of the resource to S3'
|
21
|
+
option :reduced_redundancy_storage, nil, 'Whether to use the reduced redundancy storage option'
|
22
|
+
option :path_style, true, 'Whether to use path_style URLs to communiated with S3'
|
23
|
+
option :version_bucket, false, 'Whether to enable versionning on the S3 bucket content'
|
24
|
+
option :verbose, false, 'Whether to provide more verbose output'
|
25
|
+
option :dry_run, false, 'Whether to perform a dry-run'
|
8
26
|
|
9
|
-
|
10
|
-
|
11
|
-
|
27
|
+
def initialize(app, options_hash = {}, &block)
|
28
|
+
super
|
29
|
+
app.define_hook :after_s3_sync
|
30
|
+
# Temporary workaround for 3.3 and 3.4.
|
31
|
+
app.send :include, ClassMethods
|
32
|
+
end
|
33
|
+
|
34
|
+
def after_configuration
|
35
|
+
read_config
|
36
|
+
options.aws_access_key_id ||= ENV['AWS_ACCESS_KEY_ID']
|
37
|
+
options.aws_secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY']
|
38
|
+
options.http_prefix = app.http_prefix if app.respond_to? :http_prefix
|
39
|
+
options.build_dir ||= app.build_dir if app.respond_to? :build_dir
|
40
|
+
::Middleman::S3Sync.s3_sync_options = s3_sync_options
|
41
|
+
end
|
12
42
|
|
13
|
-
|
43
|
+
def after_build
|
44
|
+
::Middleman::S3Sync.sync() if options.after_build
|
45
|
+
end
|
14
46
|
|
15
|
-
|
47
|
+
def manipulate_resource_list(mm_resources)
|
48
|
+
::Middleman::S3Sync.mm_resources = mm_resources
|
49
|
+
end
|
16
50
|
|
17
|
-
|
51
|
+
def s3_sync_options
|
52
|
+
options
|
53
|
+
end
|
18
54
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
55
|
+
# Read config options from an IO stream and set them on `self`. Defaults
|
56
|
+
# to reading from the `.s3_sync` file in the MM project root if it exists.
|
57
|
+
#
|
58
|
+
# @param io [IO] an IO stream to read from
|
59
|
+
# @return [void]
|
60
|
+
def read_config(io = nil)
|
61
|
+
unless io
|
62
|
+
root_path = ::Middleman::Application.root
|
63
|
+
config_file_path = File.join(root_path, ".s3_sync")
|
24
64
|
|
25
|
-
|
26
|
-
|
27
|
-
app.after_build do |builder|
|
28
|
-
::Middleman::S3Sync.sync(options) if options.after_build
|
29
|
-
end
|
65
|
+
# skip if config file does not exist
|
66
|
+
return unless File.exists?(config_file_path)
|
30
67
|
|
31
|
-
|
32
|
-
end
|
68
|
+
io = File.open(config_file_path, "r")
|
33
69
|
end
|
34
|
-
alias :included :registered
|
35
70
|
|
36
|
-
|
37
|
-
@options
|
38
|
-
end
|
71
|
+
config = YAML.load(io).symbolize_keys
|
39
72
|
|
40
|
-
|
41
|
-
|
73
|
+
OPTIONS.each do |config_option|
|
74
|
+
self.send("#{config_option}=".to_sym, config[config_option]) if config[config_option]
|
42
75
|
end
|
76
|
+
end
|
43
77
|
|
44
|
-
module Helpers
|
45
|
-
def s3_sync_options
|
46
|
-
::Middleman::S3Sync.s3_sync_options
|
47
|
-
end
|
48
78
|
|
49
|
-
|
50
|
-
|
51
|
-
|
79
|
+
module ClassMethods
|
80
|
+
def s3_sync_options
|
81
|
+
::Middleman::S3SyncExtension.s3_sync_options
|
82
|
+
end
|
52
83
|
|
53
|
-
|
54
|
-
|
55
|
-
|
84
|
+
def default_caching_policy(policy = {})
|
85
|
+
::Middleman::S3Sync.add_caching_policy(:default, policy)
|
86
|
+
end
|
87
|
+
|
88
|
+
def caching_policy(content_type, policy = {})
|
89
|
+
::Middleman::S3Sync.add_caching_policy(content_type, policy)
|
56
90
|
end
|
57
91
|
end
|
58
92
|
end
|
93
|
+
|
94
|
+
::Middleman::Extensions.register(:s3_sync, S3SyncExtension)
|
59
95
|
end
|
96
|
+
|
data/lib/middleman/s3_sync.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'fog/aws'
|
2
|
-
require '
|
2
|
+
require 'fog/aws/storage'
|
3
3
|
require 'digest/md5'
|
4
4
|
require 'middleman/s3_sync/version'
|
5
5
|
require 'middleman/s3_sync/options'
|
6
|
-
require 'middleman
|
6
|
+
require 'middleman/s3_sync/caching_policy'
|
7
7
|
require 'middleman/s3_sync/status'
|
8
8
|
require 'middleman/s3_sync/resource'
|
9
9
|
require 'middleman-s3_sync/extension'
|
10
|
+
require 'parallel'
|
10
11
|
require 'ruby-progressbar'
|
11
12
|
require 'thread'
|
12
13
|
|
@@ -14,20 +15,23 @@ module Middleman
|
|
14
15
|
module S3Sync
|
15
16
|
class << self
|
16
17
|
include Status
|
18
|
+
include CachingPolicy
|
19
|
+
|
17
20
|
@@bucket_lock = Mutex.new
|
18
21
|
@@bucket_files_lock = Mutex.new
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
self.s3_sync_options = options
|
23
|
+
attr_accessor :s3_sync_options
|
24
|
+
attr_accessor :mm_resources
|
25
|
+
attr_reader :app
|
24
26
|
|
27
|
+
def sync()
|
28
|
+
say_status "Let's see if there's work to be done..."
|
25
29
|
unless work_to_be_done?
|
26
|
-
say_status "
|
30
|
+
say_status "All S3 files are up to date."
|
27
31
|
return
|
28
32
|
end
|
29
33
|
|
30
|
-
say_status "
|
34
|
+
say_status "Ready to apply updates to #{s3_sync_options.bucket}."
|
31
35
|
|
32
36
|
update_bucket_versioning
|
33
37
|
|
@@ -36,7 +40,7 @@ module Middleman
|
|
36
40
|
update_resources
|
37
41
|
delete_resources
|
38
42
|
|
39
|
-
|
43
|
+
app.run_hook :after_s3_sync, ignored: files_to_ignore.map(&:path),
|
40
44
|
created: files_to_create.map(&:path),
|
41
45
|
updated: files_to_update.map(&:path),
|
42
46
|
deleted: files_to_delete.map(&:path)
|
@@ -52,13 +56,29 @@ module Middleman
|
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
59
|
+
def add_local_resource(mm_resource)
|
60
|
+
s3_sync_resources[mm_resource.destination_path] = S3Sync::Resource.new(mm_resource, remote_resource_for_path(mm_resource.destination_path)).tap(&:status)
|
61
|
+
end
|
62
|
+
|
63
|
+
def remote_only_paths
|
64
|
+
paths - s3_sync_resources.keys
|
65
|
+
end
|
66
|
+
|
67
|
+
def app=(app)
|
68
|
+
@app = app
|
69
|
+
@app.extend ::Middleman::S3SyncExtension::ClassMethods
|
70
|
+
end
|
71
|
+
|
72
|
+
def content_types
|
73
|
+
@content_types || {}
|
74
|
+
end
|
75
|
+
|
55
76
|
protected
|
56
77
|
def update_bucket_versioning
|
57
78
|
connection.put_bucket_versioning(s3_sync_options.bucket, "Enabled") if s3_sync_options.version_bucket
|
58
79
|
end
|
59
80
|
|
60
81
|
def connection
|
61
|
-
|
62
82
|
connection_options = {
|
63
83
|
:region => s3_sync_options.region,
|
64
84
|
:path_style => s3_sync_options.path_style
|
@@ -76,37 +96,20 @@ module Middleman
|
|
76
96
|
@connection ||= Fog::Storage::AWS.new(connection_options)
|
77
97
|
end
|
78
98
|
|
79
|
-
def
|
80
|
-
|
81
|
-
progress_bar.increment
|
82
|
-
S3Sync::Resource.new(p, bucket_files.find { |f| f.key == "#{s3_sync_options.prefix}#{p}" }).tap(&:status)
|
83
|
-
end
|
99
|
+
def remote_resource_for_path(path)
|
100
|
+
bucket_files.find { |f| f.key == "#{s3_sync_options.prefix}#{path}" }
|
84
101
|
end
|
85
102
|
|
86
|
-
def
|
87
|
-
@
|
103
|
+
def s3_sync_resources
|
104
|
+
@s3_sync_resources ||= {}
|
88
105
|
end
|
89
106
|
|
90
107
|
def paths
|
91
108
|
@paths ||= begin
|
92
|
-
|
93
|
-
(remote_paths.map { |rp| rp.gsub(/^#{s3_sync_options.prefix}/, '')} + local_paths).uniq.sort
|
109
|
+
(remote_paths.map { |rp| rp.gsub(/^#{s3_sync_options.prefix}/, '')} + s3_sync_resources.keys).uniq.sort
|
94
110
|
end
|
95
111
|
end
|
96
112
|
|
97
|
-
def local_paths
|
98
|
-
@local_paths ||= begin
|
99
|
-
local_paths = (Dir[build_dir + "/**/*"] + Dir[build_dir + "/**/.*"])
|
100
|
-
.reject { |p| File.directory?(p) }
|
101
|
-
|
102
|
-
if s3_sync_options.prefer_gzip
|
103
|
-
local_paths.reject! { |p| p =~ /\.gz$/ && File.exist?(p.gsub(/\.gz$/, '')) }
|
104
|
-
end
|
105
|
-
|
106
|
-
local_paths.pmap(32) { |p| p.gsub(/#{build_dir}\//, '') }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
113
|
def remote_paths
|
111
114
|
@remote_paths ||= if s3_sync_options.delete
|
112
115
|
bucket_files.map(&:key)
|
@@ -150,27 +153,33 @@ module Middleman
|
|
150
153
|
end
|
151
154
|
|
152
155
|
def work_to_be_done?
|
156
|
+
Parallel.each(mm_resources, in_threads: 8, progress: "Processing sitemap") { |mm_resource| add_local_resource(mm_resource) }
|
157
|
+
|
158
|
+
Parallel.each(remote_only_paths, in_threads: 8, progress: "Processing remote files") do |remote_path|
|
159
|
+
s3_sync_resources[remote_path] ||= S3Sync::Resource.new(nil, remote_resource_for_path(remote_path)).tap(&:status)
|
160
|
+
end
|
161
|
+
|
153
162
|
!(files_to_create.empty? && files_to_update.empty? && files_to_delete.empty?)
|
154
163
|
end
|
155
164
|
|
156
165
|
def files_to_delete
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
166
|
+
if s3_sync_options.delete
|
167
|
+
s3_sync_resources.values.select { |r| r.to_delete? }
|
168
|
+
else
|
169
|
+
[]
|
170
|
+
end
|
162
171
|
end
|
163
172
|
|
164
173
|
def files_to_create
|
165
|
-
|
174
|
+
s3_sync_resources.values.select { |r| r.to_create? }
|
166
175
|
end
|
167
176
|
|
168
177
|
def files_to_update
|
169
|
-
|
178
|
+
s3_sync_resources.values.select { |r| r.to_update? }
|
170
179
|
end
|
171
180
|
|
172
181
|
def files_to_ignore
|
173
|
-
|
182
|
+
s3_sync_resources.values.select { |r| r.to_ignore? }
|
174
183
|
end
|
175
184
|
|
176
185
|
def build_dir
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'map'
|
2
|
+
|
3
|
+
module Middleman
|
4
|
+
module S3Sync
|
5
|
+
module CachingPolicy
|
6
|
+
def add_caching_policy(content_type, options)
|
7
|
+
caching_policies[content_type.to_s] = BrowserCachePolicy.new(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def caching_policy_for(content_type)
|
11
|
+
caching_policies.fetch(content_type.to_s, caching_policies[:default])
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_caching_policy
|
15
|
+
caching_policies[:default]
|
16
|
+
end
|
17
|
+
|
18
|
+
def caching_policies
|
19
|
+
@caching_policies ||= Map.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class BrowserCachePolicy
|
24
|
+
attr_accessor :policies
|
25
|
+
|
26
|
+
def initialize(options = {})
|
27
|
+
@policies = Map.from_hash(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_control
|
31
|
+
policy = []
|
32
|
+
policy << "max-age=#{policies.max_age}" if policies.has_key?(:max_age)
|
33
|
+
policy << "s-maxage=#{policies.s_maxage}" if policies.has_key?(:s_maxage)
|
34
|
+
policy << "public" if policies.fetch(:public, false)
|
35
|
+
policy << "private" if policies.fetch(:private, false)
|
36
|
+
policy << "no-cache" if policies.fetch(:no_cache, false)
|
37
|
+
policy << "no-store" if policies.fetch(:no_store, false)
|
38
|
+
policy << "must-revalidate" if policies.fetch(:must_revalidate, false)
|
39
|
+
policy << "proxy-revalidate" if policies.fetch(:proxy_revalidate, false)
|
40
|
+
if policy.empty?
|
41
|
+
nil
|
42
|
+
else
|
43
|
+
policy.join(", ")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
cache_control
|
49
|
+
end
|
50
|
+
|
51
|
+
def expires
|
52
|
+
if expiration = policies.fetch(:expires, nil)
|
53
|
+
CGI.rfc1123_date(expiration)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -19,36 +19,16 @@ module Middleman
|
|
19
19
|
:reduced_redundancy_storage,
|
20
20
|
:path_style,
|
21
21
|
:version_bucket,
|
22
|
+
:dry_run,
|
22
23
|
:verbose,
|
23
24
|
:content_types
|
24
25
|
]
|
25
26
|
attr_accessor *OPTIONS
|
26
27
|
|
27
|
-
def initialize
|
28
|
-
# read config from .s3_sync on initialization
|
29
|
-
self.read_config
|
30
|
-
end
|
31
|
-
|
32
28
|
def acl
|
33
29
|
@acl || 'public-read'
|
34
30
|
end
|
35
31
|
|
36
|
-
def add_caching_policy(content_type, options)
|
37
|
-
caching_policies[content_type.to_s] = BrowserCachePolicy.new(options)
|
38
|
-
end
|
39
|
-
|
40
|
-
def caching_policy_for(content_type)
|
41
|
-
caching_policies.fetch(content_type.to_s, caching_policies[:default])
|
42
|
-
end
|
43
|
-
|
44
|
-
def default_caching_policy
|
45
|
-
caching_policies[:default]
|
46
|
-
end
|
47
|
-
|
48
|
-
def caching_policies
|
49
|
-
@caching_policies ||= Map.new
|
50
|
-
end
|
51
|
-
|
52
32
|
def aws_access_key_id=(aws_access_key_id)
|
53
33
|
@aws_access_key_id = aws_access_key_id if aws_access_key_id
|
54
34
|
end
|
@@ -95,75 +75,13 @@ module Middleman
|
|
95
75
|
end
|
96
76
|
|
97
77
|
def prefix
|
98
|
-
@prefix.
|
78
|
+
@prefix.nil? || @prefix.empty? ? "" : "#{@prefix}/"
|
99
79
|
end
|
100
80
|
|
101
81
|
def version_bucket
|
102
82
|
@version_bucket.nil? ? false : @version_bucket
|
103
83
|
end
|
104
84
|
|
105
|
-
def content_types
|
106
|
-
@content_types || {}
|
107
|
-
end
|
108
|
-
|
109
|
-
# Read config options from an IO stream and set them on `self`. Defaults
|
110
|
-
# to reading from the `.s3_sync` file in the MM project root if it exists.
|
111
|
-
#
|
112
|
-
# @param io [IO] an IO stream to read from
|
113
|
-
# @return [void]
|
114
|
-
def read_config(io = nil)
|
115
|
-
unless io
|
116
|
-
root_path = ::Middleman::Application.root
|
117
|
-
config_file_path = File.join(root_path, ".s3_sync")
|
118
|
-
|
119
|
-
# skip if config file does not exist
|
120
|
-
return unless File.exists?(config_file_path)
|
121
|
-
|
122
|
-
io = File.open(config_file_path, "r")
|
123
|
-
end
|
124
|
-
|
125
|
-
config = YAML.load(io).symbolize_keys
|
126
|
-
|
127
|
-
OPTIONS.each do |config_option|
|
128
|
-
self.send("#{config_option}=".to_sym, config[config_option]) if config[config_option]
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
protected
|
133
|
-
class BrowserCachePolicy
|
134
|
-
attr_accessor :policies
|
135
|
-
|
136
|
-
def initialize(options)
|
137
|
-
@policies = Map.from_hash(options)
|
138
|
-
end
|
139
|
-
|
140
|
-
def cache_control
|
141
|
-
policy = []
|
142
|
-
policy << "max-age=#{policies.max_age}" if policies.has_key?(:max_age)
|
143
|
-
policy << "s-maxage=#{policies.s_maxage}" if policies.has_key?(:s_maxage)
|
144
|
-
policy << "public" if policies.fetch(:public, false)
|
145
|
-
policy << "private" if policies.fetch(:private, false)
|
146
|
-
policy << "no-cache" if policies.fetch(:no_cache, false)
|
147
|
-
policy << "no-store" if policies.fetch(:no_store, false)
|
148
|
-
policy << "must-revalidate" if policies.fetch(:must_revalidate, false)
|
149
|
-
policy << "proxy-revalidate" if policies.fetch(:proxy_revalidate, false)
|
150
|
-
if policy.empty?
|
151
|
-
nil
|
152
|
-
else
|
153
|
-
policy.join(", ")
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def to_s
|
158
|
-
cache_control
|
159
|
-
end
|
160
|
-
|
161
|
-
def expires
|
162
|
-
if expiration = policies.fetch(:expires, nil)
|
163
|
-
CGI.rfc1123_date(expiration)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
85
|
end
|
168
86
|
end
|
169
87
|
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
module Middleman
|
2
2
|
module S3Sync
|
3
3
|
class Resource
|
4
|
-
attr_accessor :path, :partial_s3_resource, :full_s3_resource, :content_type, :gzipped, :options
|
4
|
+
attr_accessor :path, :resource, :partial_s3_resource, :full_s3_resource, :content_type, :gzipped, :options
|
5
5
|
|
6
6
|
CONTENT_MD5_KEY = 'x-amz-meta-content-md5'
|
7
7
|
|
8
8
|
include Status
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(resource, partial_s3_resource)
|
11
|
+
@resource = resource
|
12
|
+
@path = resource ? resource.destination_path : partial_s3_resource.key
|
12
13
|
@partial_s3_resource = partial_s3_resource
|
13
14
|
end
|
14
15
|
|
@@ -61,7 +62,7 @@ module Middleman
|
|
61
62
|
s3_resource.merge_attributes(to_h)
|
62
63
|
s3_resource.body = body
|
63
64
|
|
64
|
-
s3_resource.save
|
65
|
+
s3_resource.save unless options.dry_run
|
65
66
|
}
|
66
67
|
end
|
67
68
|
|
@@ -76,23 +77,23 @@ module Middleman
|
|
76
77
|
|
77
78
|
def destroy!
|
78
79
|
say_status ANSI.red { "Deleting" } + " " + remote_path
|
79
|
-
bucket.files.destroy remote_path
|
80
|
+
bucket.files.destroy remote_path unless options.dry_run
|
80
81
|
end
|
81
82
|
|
82
83
|
def create!
|
83
84
|
say_status ANSI.green { "Creating" } + " #{remote_path}#{ gzipped ? ANSI.white {' (gzipped)'} : ''}"
|
84
85
|
local_content { |body|
|
85
|
-
bucket.files.create(to_h.merge(body: body))
|
86
|
+
bucket.files.create(to_h.merge(body: body)) unless options.dry_run
|
86
87
|
}
|
87
88
|
end
|
88
89
|
|
89
90
|
def ignore!
|
90
|
-
reason = if redirect?
|
91
|
-
:redirect
|
92
|
-
elsif directory?
|
93
|
-
:directory
|
94
|
-
end
|
95
91
|
if options.verbose
|
92
|
+
reason = if redirect?
|
93
|
+
:redirect
|
94
|
+
elsif directory?
|
95
|
+
:directory
|
96
|
+
end
|
96
97
|
say_status ANSI.yellow {"Ignoring"} + " #{remote_path} #{ reason ? ANSI.white {"(#{reason})" } : "" }"
|
97
98
|
end
|
98
99
|
end
|
@@ -135,6 +136,8 @@ module Middleman
|
|
135
136
|
elsif local? && remote?
|
136
137
|
if options.force
|
137
138
|
:updated
|
139
|
+
elsif not caching_policy_match?
|
140
|
+
:updated
|
138
141
|
elsif local_object_md5 == remote_object_md5
|
139
142
|
:identical
|
140
143
|
else
|
@@ -155,8 +158,10 @@ module Middleman
|
|
155
158
|
:new
|
156
159
|
elsif remote? && redirect?
|
157
160
|
:ignored
|
158
|
-
|
161
|
+
elsif remote?
|
159
162
|
:deleted
|
163
|
+
else
|
164
|
+
:ignored
|
160
165
|
end
|
161
166
|
end
|
162
167
|
|
@@ -205,12 +210,20 @@ module Middleman
|
|
205
210
|
end
|
206
211
|
|
207
212
|
def content_type
|
208
|
-
@content_type ||=
|
209
|
-
@content_type ||=
|
213
|
+
@content_type ||= Middleman::S3Sync.content_types[path]
|
214
|
+
@content_type ||= resource.content_type
|
210
215
|
end
|
211
216
|
|
212
217
|
def caching_policy
|
213
|
-
@caching_policy ||=
|
218
|
+
@caching_policy ||= Middleman::S3Sync.caching_policy_for(content_type)
|
219
|
+
end
|
220
|
+
|
221
|
+
def caching_policy_match?
|
222
|
+
if (caching_policy)
|
223
|
+
caching_policy.cache_control == full_s3_resource.cache_control
|
224
|
+
else
|
225
|
+
true
|
226
|
+
end
|
214
227
|
end
|
215
228
|
|
216
229
|
protected
|
data/middleman-s3_sync.gemspec
CHANGED
@@ -18,19 +18,20 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
gem.add_runtime_dependency 'middleman-core', '
|
21
|
+
gem.add_runtime_dependency 'middleman-core', '~> 3.3'
|
22
22
|
gem.add_runtime_dependency 'unf'
|
23
23
|
gem.add_runtime_dependency 'fog-aws', '>= 0.1.1'
|
24
24
|
gem.add_runtime_dependency 'map'
|
25
|
-
gem.add_runtime_dependency '
|
25
|
+
gem.add_runtime_dependency 'parallel'
|
26
26
|
gem.add_runtime_dependency 'ruby-progressbar'
|
27
27
|
gem.add_runtime_dependency 'ansi', '~> 1.5.0'
|
28
28
|
|
29
29
|
gem.add_development_dependency 'rake'
|
30
30
|
gem.add_development_dependency 'pry'
|
31
|
-
gem.add_development_dependency 'pry-
|
31
|
+
gem.add_development_dependency 'pry-byebug'
|
32
32
|
gem.add_development_dependency 'rspec', '>= 3.0.0'
|
33
33
|
gem.add_development_dependency 'rspec-its'
|
34
|
+
gem.add_development_dependency 'rspec-mocks'
|
34
35
|
gem.add_development_dependency 'timerizer'
|
35
36
|
gem.add_development_dependency 'travis'
|
36
37
|
gem.add_development_dependency 'travis-lint'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'middleman/s3_sync/caching_policy'
|
3
|
+
|
4
|
+
describe Middleman::S3Sync::BrowserCachePolicy do
|
5
|
+
let(:options) { Hash.new }
|
6
|
+
subject(:policy) { Middleman::S3Sync::BrowserCachePolicy.new(options) }
|
7
|
+
|
8
|
+
it "should be blank" do
|
9
|
+
policy.should_not be_nil
|
10
|
+
|
11
|
+
policy.to_s.should_not =~ /max-age=/
|
12
|
+
policy.to_s.should_not =~ /s-maxage=/
|
13
|
+
policy.to_s.should_not =~ /public/
|
14
|
+
policy.to_s.should_not =~ /private/
|
15
|
+
policy.to_s.should_not =~ /no-cache/
|
16
|
+
policy.to_s.should_not =~ /no-store/
|
17
|
+
policy.to_s.should_not =~ /must-revalidate/
|
18
|
+
policy.to_s.should_not =~ /proxy-revalidate/
|
19
|
+
policy.expires.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
context "setting max-age" do
|
23
|
+
let(:options) { { max_age: 300 } }
|
24
|
+
|
25
|
+
its(:to_s) { should =~ /max-age=300/ }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "setting s-maxage" do
|
29
|
+
let(:options) { { s_maxage: 300 } }
|
30
|
+
|
31
|
+
its(:to_s) { should =~ /s-maxage=300/ }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "set public flag" do
|
35
|
+
let(:options) { { public: true } }
|
36
|
+
its(:to_s) { should =~ /public/ }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "it should set the private flag if it is set to true" do
|
40
|
+
let(:options) { { private: true } }
|
41
|
+
its(:to_s) { should =~ /private/ }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "it should set the no-cache flag when set property" do
|
45
|
+
let(:options) { { no_cache: true }}
|
46
|
+
its(:to_s) { should =~ /no-cache/ }
|
47
|
+
end
|
48
|
+
|
49
|
+
context "setting the no-store flag" do
|
50
|
+
let(:options) { { no_store: true } }
|
51
|
+
its(:to_s) { should =~ /no-store/ }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "setting the must-revalidate policy" do
|
55
|
+
let(:options) { { must_revalidate: true } }
|
56
|
+
its(:to_s) { should =~ /must-revalidate/ }
|
57
|
+
end
|
58
|
+
|
59
|
+
context "setting the proxy-revalidate policy" do
|
60
|
+
let(:options) { { proxy_revalidate: true } }
|
61
|
+
its(:to_s) { should =~ /proxy-revalidate/ }
|
62
|
+
end
|
63
|
+
|
64
|
+
context "divide caching policiies with a comma and a space" do
|
65
|
+
let(:options) { { :max_age => 300, :public => true } }
|
66
|
+
|
67
|
+
it "splits policies eith commans and spaces" do
|
68
|
+
policies = policy.to_s.split(/, /)
|
69
|
+
policies.length.should == 2
|
70
|
+
policies.first.should == 'max-age=300'
|
71
|
+
policies.last.should == 'public'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "set the expiration date" do
|
76
|
+
let(:options) { { expires: 1.years.from_now } }
|
77
|
+
|
78
|
+
its(:expires) { should == CGI.rfc1123_date(1.year.from_now )}
|
79
|
+
end
|
80
|
+
end
|
data/spec/resource_spec.rb
CHANGED
@@ -6,14 +6,19 @@ describe Middleman::S3Sync::Resource do
|
|
6
6
|
Middleman::S3Sync::Options.new
|
7
7
|
}
|
8
8
|
|
9
|
+
let(:mm_resource) {
|
10
|
+
double(
|
11
|
+
destination_path: 'path/to/resource.html'
|
12
|
+
)
|
13
|
+
}
|
9
14
|
before do
|
10
|
-
Middleman::S3Sync.
|
15
|
+
Middleman::S3Sync.s3_sync_options = options
|
11
16
|
options.build_dir = "build"
|
12
17
|
options.prefer_gzip = false
|
13
18
|
end
|
14
19
|
|
15
20
|
context "a new resource" do
|
16
|
-
subject(:resource) { Middleman::S3Sync::Resource.new(
|
21
|
+
subject(:resource) { Middleman::S3Sync::Resource.new(mm_resource, nil) }
|
17
22
|
|
18
23
|
context "without a prefix" do
|
19
24
|
before do
|
@@ -75,14 +80,17 @@ describe Middleman::S3Sync::Resource do
|
|
75
80
|
end
|
76
81
|
|
77
82
|
context "the file does not exist locally" do
|
78
|
-
subject(:resource) { Middleman::S3Sync::Resource.new(
|
83
|
+
subject(:resource) { Middleman::S3Sync::Resource.new(nil, remote) }
|
79
84
|
|
80
|
-
let(:remote) {
|
85
|
+
let(:remote) {
|
86
|
+
double(
|
87
|
+
key: 'path/to/resource.html',
|
88
|
+
metadata: {}
|
89
|
+
)
|
90
|
+
}
|
81
91
|
|
82
92
|
before do
|
83
|
-
|
84
|
-
allow(remote).to receive(:metadata).and_return({})
|
85
|
-
resource.full_s3_resource = remote
|
93
|
+
resource.full_s3_resource = remote
|
86
94
|
end
|
87
95
|
|
88
96
|
context "without a prefix" do
|
@@ -116,7 +124,7 @@ describe Middleman::S3Sync::Resource do
|
|
116
124
|
expect(resource).to be_remote
|
117
125
|
end
|
118
126
|
|
119
|
-
it "
|
127
|
+
it "does not exist locally" do
|
120
128
|
expect(resource).not_to be_local
|
121
129
|
end
|
122
130
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: middleman-s3_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frederic Jean
|
@@ -9,22 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-09-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: middleman-core
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 3.
|
20
|
+
version: '3.3'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 3.
|
27
|
+
version: '3.3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: unf
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: parallel
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
74
|
- - ">="
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
140
|
- !ruby/object:Gem::Dependency
|
141
|
-
name: pry-
|
141
|
+
name: pry-byebug
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - ">="
|
@@ -179,6 +179,20 @@ dependencies:
|
|
179
179
|
- - ">="
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '0'
|
182
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: rspec-mocks
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
182
196
|
- !ruby/object:Gem::Dependency
|
183
197
|
name: timerizer
|
184
198
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,13 +255,14 @@ files:
|
|
241
255
|
- lib/middleman-s3_sync/commands.rb
|
242
256
|
- lib/middleman-s3_sync/extension.rb
|
243
257
|
- lib/middleman/s3_sync.rb
|
258
|
+
- lib/middleman/s3_sync/caching_policy.rb
|
244
259
|
- lib/middleman/s3_sync/options.rb
|
245
260
|
- lib/middleman/s3_sync/resource.rb
|
246
261
|
- lib/middleman/s3_sync/status.rb
|
247
262
|
- lib/middleman/s3_sync/version.rb
|
248
263
|
- lib/middleman_extension.rb
|
249
264
|
- middleman-s3_sync.gemspec
|
250
|
-
- spec/
|
265
|
+
- spec/caching_policy_spec.rb
|
251
266
|
- spec/resource_spec.rb
|
252
267
|
- spec/spec_helper.rb
|
253
268
|
homepage: http://github.com/fredjean/middleman-s3_sync
|
@@ -275,6 +290,6 @@ signing_key:
|
|
275
290
|
specification_version: 4
|
276
291
|
summary: Tries really, really hard not to push files to S3.
|
277
292
|
test_files:
|
278
|
-
- spec/
|
293
|
+
- spec/caching_policy_spec.rb
|
279
294
|
- spec/resource_spec.rb
|
280
295
|
- spec/spec_helper.rb
|
data/spec/options_spec.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'middleman/s3_sync/options'
|
3
|
-
|
4
|
-
describe Middleman::S3Sync::Options do
|
5
|
-
subject(:options) { Middleman::S3Sync::Options.new }
|
6
|
-
|
7
|
-
its(:delete) { is_expected.to eq(true) }
|
8
|
-
its(:after_build) { is_expected.to eq(false)}
|
9
|
-
its(:prefer_gzip) { is_expected.to eq(true) }
|
10
|
-
its(:aws_secret_access_key) { is_expected.to eq(ENV['AWS_SECRET_ACCESS_KEY']) }
|
11
|
-
its(:aws_access_key_id) { is_expected.to eq(ENV['AWS_ACCESS_KEY_ID']) }
|
12
|
-
its(:caching_policies) { is_expected.to be_empty }
|
13
|
-
its(:default_caching_policy) { is_expected.to be_nil }
|
14
|
-
|
15
|
-
context "browser caching policy" do
|
16
|
-
let(:policy) { options.default_caching_policy }
|
17
|
-
|
18
|
-
it "should have a blank default caching policy" do
|
19
|
-
options.add_caching_policy :default, {}
|
20
|
-
|
21
|
-
policy.should_not be_nil
|
22
|
-
|
23
|
-
policy.to_s.should_not =~ /max-age=/
|
24
|
-
policy.to_s.should_not =~ /s-maxage=/
|
25
|
-
policy.to_s.should_not =~ /public/
|
26
|
-
policy.to_s.should_not =~ /private/
|
27
|
-
policy.to_s.should_not =~ /no-cache/
|
28
|
-
policy.to_s.should_not =~ /no-store/
|
29
|
-
policy.to_s.should_not =~ /must-revalidate/
|
30
|
-
policy.to_s.should_not =~ /proxy-revalidate/
|
31
|
-
policy.expires.should be_nil
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should set the max-age policy" do
|
35
|
-
options.add_caching_policy :default, :max_age => 300
|
36
|
-
|
37
|
-
policy.to_s.should =~ /max-age=300/
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should set the s-maxage policy" do
|
41
|
-
options.add_caching_policy :default, :s_maxage => 300
|
42
|
-
|
43
|
-
policy.to_s.should =~ /s-maxage=300/
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should set the public flag on the policy if set to true" do
|
47
|
-
options.add_caching_policy :default, :public => true
|
48
|
-
|
49
|
-
policy.to_s.should =~ /public/
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should not set the public flag on the policy if it is set to false" do
|
53
|
-
options.add_caching_policy :default, :public => false
|
54
|
-
|
55
|
-
policy.to_s.should_not =~ /public/
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should set the private flag on the policy if it is set to true" do
|
59
|
-
options.add_caching_policy :default, :private => true
|
60
|
-
|
61
|
-
policy.to_s.should =~ /private/
|
62
|
-
end
|
63
|
-
|
64
|
-
it "should set the no-cache flag on the policy if it is set to true" do
|
65
|
-
options.add_caching_policy :default, :no_cache => true
|
66
|
-
|
67
|
-
policy.to_s.should =~ /no-cache/
|
68
|
-
end
|
69
|
-
|
70
|
-
it "should set the no-store flag if it is set to true" do
|
71
|
-
options.add_caching_policy :default, :no_store => true
|
72
|
-
|
73
|
-
policy.to_s.should =~ /no-store/
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should set the must-revalidate policy if it is set to true" do
|
77
|
-
options.add_caching_policy :default, :must_revalidate => true
|
78
|
-
|
79
|
-
policy.to_s.should =~ /must-revalidate/
|
80
|
-
end
|
81
|
-
|
82
|
-
it "should set the proxy-revalidate policy if it is set to true" do
|
83
|
-
options.add_caching_policy :default, :proxy_revalidate => true
|
84
|
-
|
85
|
-
policy.to_s.should =~ /proxy-revalidate/
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should divide caching policies with commas and a space" do
|
89
|
-
options.add_caching_policy :default, :max_age => 300, :public => true
|
90
|
-
|
91
|
-
policies = policy.to_s.split(/, /)
|
92
|
-
policies.length.should == 2
|
93
|
-
policies.first.should == 'max-age=300'
|
94
|
-
policies.last.should == 'public'
|
95
|
-
end
|
96
|
-
|
97
|
-
it "should set the expiration date" do
|
98
|
-
expiration = 1.years.from_now
|
99
|
-
|
100
|
-
options.add_caching_policy :default, :expires => expiration
|
101
|
-
policy.expires.should == CGI.rfc1123_date(expiration)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
context "#read_config" do
|
106
|
-
let(:aws_access_key_id) { "foo" }
|
107
|
-
let(:aws_secret_access_key) { "bar" }
|
108
|
-
let(:bucket) { "baz" }
|
109
|
-
let(:config) { { "aws_access_key_id" => aws_access_key_id, "aws_secret_access_key" => aws_secret_access_key, "bucket" => bucket } }
|
110
|
-
let(:file) { StringIO.new(YAML.dump(config)) }
|
111
|
-
|
112
|
-
before do
|
113
|
-
options.read_config(file)
|
114
|
-
end
|
115
|
-
|
116
|
-
its(:aws_access_key_id) { should eq(aws_access_key_id) }
|
117
|
-
its(:aws_secret_access_key) { should eq(aws_secret_access_key) }
|
118
|
-
its(:bucket) { should eq(bucket) }
|
119
|
-
end
|
120
|
-
|
121
|
-
context "prefix with http_prefix" do
|
122
|
-
before do
|
123
|
-
options.http_prefix = "/blog"
|
124
|
-
options.prefix = "blog"
|
125
|
-
end
|
126
|
-
|
127
|
-
its(:prefix) { should eq("")}
|
128
|
-
end
|
129
|
-
|
130
|
-
end
|