cloud_crooner 0.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 +7 -0
- data/.gitignore +20 -0
- data/.rspec +3 -0
- data/.rvmrc +48 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +142 -0
- data/Rakefile +6 -0
- data/cloud_crooner.gemspec +30 -0
- data/lib/cloud_crooner.rb +3 -0
- data/lib/cloud_crooner/cloud_crooner.rb +212 -0
- data/lib/cloud_crooner/storage.rb +104 -0
- data/lib/cloud_crooner/version.rb +3 -0
- data/spec/cloud_crooner_spec.rb +320 -0
- data/spec/spec_helper.rb +122 -0
- data/spec/storage_spec.rb +239 -0
- metadata +175 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'fog'
|
2
|
+
require 'rack/mime'
|
3
|
+
|
4
|
+
module CloudCrooner
|
5
|
+
class Storage
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@bucket_name = CloudCrooner.bucket_name
|
9
|
+
@prefix = CloudCrooner.prefix
|
10
|
+
@fog_options = CloudCrooner.fog_options
|
11
|
+
@manifest = CloudCrooner.manifest
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection
|
15
|
+
@connection ||= Fog::Storage.new(@fog_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def bucket
|
19
|
+
@bucket ||= connection.directories.get(@bucket_name, :prefix => @prefix)
|
20
|
+
end
|
21
|
+
|
22
|
+
def local_compiled_assets
|
23
|
+
# compiled assets prepended with prefix for comparison against remote
|
24
|
+
@manifest.files.keys.map {|f| File.join(@prefix, f)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def exists_on_remote?(file)
|
28
|
+
bucket.files.head(file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def upload_files
|
32
|
+
files_to_upload = local_compiled_assets.reject { |f| exists_on_remote?(f) }
|
33
|
+
files_to_upload.each do |asset|
|
34
|
+
upload_file(asset)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def log(msg)
|
39
|
+
CloudCrooner.log(msg)
|
40
|
+
end
|
41
|
+
|
42
|
+
def upload_file(f)
|
43
|
+
# grabs the compiled asset from public_path
|
44
|
+
full_file_path = File.join(File.dirname(@manifest.dir), f)
|
45
|
+
one_year = 31557600
|
46
|
+
mime = Rack::Mime.mime_type(File.extname(f))
|
47
|
+
file = {
|
48
|
+
:key => f,
|
49
|
+
:public => true,
|
50
|
+
:content_type => mime,
|
51
|
+
:cache_control => "public, max-age=#{one_year}",
|
52
|
+
:expires => CGI.rfc1123_date(Time.now + one_year)
|
53
|
+
}
|
54
|
+
|
55
|
+
gzipped = "#{full_file_path}.gz"
|
56
|
+
|
57
|
+
# if a gzipped version of the file exists and is a smaller file size than the original, upload that in place of the uncompressed file
|
58
|
+
if File.exists?(gzipped)
|
59
|
+
original_size = File.size(full_file_path)
|
60
|
+
gzipped_size = File.size(gzipped)
|
61
|
+
|
62
|
+
if gzipped_size < original_size
|
63
|
+
file.merge!({
|
64
|
+
:body => File.open(gzipped),
|
65
|
+
:content_encoding => 'gzip'
|
66
|
+
})
|
67
|
+
log "Uploading #{gzipped} in place of #{f}"
|
68
|
+
else
|
69
|
+
file.merge!({
|
70
|
+
:body => File.open(full_file_path)
|
71
|
+
})
|
72
|
+
log "Gzip exists but has larger file size, uploading #{f}"
|
73
|
+
end
|
74
|
+
else
|
75
|
+
file.merge!({
|
76
|
+
:body => File.open(full_file_path)
|
77
|
+
})
|
78
|
+
log "Uploading #{f}"
|
79
|
+
end
|
80
|
+
# put in reduced redundancy option here later if desired
|
81
|
+
|
82
|
+
file = bucket.files.create( file )
|
83
|
+
end
|
84
|
+
|
85
|
+
def remote_assets
|
86
|
+
files = []
|
87
|
+
bucket.files.each { |f| files << f.key }
|
88
|
+
return files
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete_remote_asset(f)
|
92
|
+
log "Deleting #{f.key} from remote"
|
93
|
+
f.destroy
|
94
|
+
end
|
95
|
+
|
96
|
+
def clean_remote
|
97
|
+
to_delete = remote_assets - local_compiled_assets
|
98
|
+
to_delete.each do |f|
|
99
|
+
delete_remote_asset(bucket.files.get(f))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,320 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CloudCrooner do
|
4
|
+
describe 'default general configuration' do
|
5
|
+
|
6
|
+
it 'creates a sprockets environment' do
|
7
|
+
expect(CloudCrooner.sprockets).to be_an_instance_of(Sprockets::Environment)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'sets a default prefix' do
|
11
|
+
expect(CloudCrooner.prefix).to eq("assets")
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sets a default public folder in dev' do
|
15
|
+
expect(CloudCrooner.public_folder).to eq("public")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'defaults to remote enabled' do
|
19
|
+
expect(CloudCrooner.remote_enabled?).to be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defaults to looking for assets in '/assets'" do
|
23
|
+
expect(CloudCrooner.asset_paths).to eq(%w(assets))
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'checks ENV for Amazon credentials' do
|
27
|
+
ENV.stub(:[]).with("AWS_ACCESS_KEY_ID").and_return("asdf123")
|
28
|
+
ENV.stub(:[]).with("AWS_SECRET_ACCESS_KEY").and_return("secret")
|
29
|
+
ENV.stub(:has_key?).with("AWS_ACCESS_KEY_ID").and_return(true)
|
30
|
+
ENV.stub(:has_key?).with("AWS_SECRET_ACCESS_KEY").and_return(true)
|
31
|
+
|
32
|
+
expect(CloudCrooner.aws_access_key_id).to eq("asdf123")
|
33
|
+
expect(CloudCrooner.aws_secret_access_key).to eq("secret")
|
34
|
+
end # it
|
35
|
+
|
36
|
+
it "checks ENV for bucket name" do
|
37
|
+
ENV.stub(:[]).with("AWS_BUCKET_NAME").and_return("test-bucket")
|
38
|
+
ENV.stub(:has_key?).with("AWS_BUCKET_NAME").and_return(true)
|
39
|
+
|
40
|
+
expect(CloudCrooner.bucket_name).to eq("test-bucket")
|
41
|
+
end #it
|
42
|
+
|
43
|
+
it "checks ENV for region" do
|
44
|
+
ENV.stub(:[]).with("AWS_REGION").and_return("eu-west-1")
|
45
|
+
ENV.stub(:has_key?).with("AWS_REGION").and_return(true)
|
46
|
+
|
47
|
+
expect(CloudCrooner.region).to eq("eu-west-1")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "errors if the ENV region is not valid" do
|
51
|
+
ENV.stub(:[]).with("AWS_REGION").and_return("shangrila")
|
52
|
+
ENV.stub(:has_key?).with("AWS_REGION").and_return(true)
|
53
|
+
|
54
|
+
expect{CloudCrooner.region}.to raise_error(CloudCrooner::FogSettingError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'defaults to keeping 2 backups' do
|
58
|
+
expect(CloudCrooner.backups_to_keep).to eq(2)
|
59
|
+
end
|
60
|
+
|
61
|
+
end # describe
|
62
|
+
|
63
|
+
describe 'errors if missing required settings' do
|
64
|
+
it "errors if region is not assigned" do
|
65
|
+
ENV.stub(:[]).and_return(nil)
|
66
|
+
ENV.stub(:has_key?).with("AWS_REGION").and_return(false)
|
67
|
+
expect{CloudCrooner.region}.to raise_error(CloudCrooner::FogSettingError, "AWS Region must be set in ENV or in configure block")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "errors if the bucket is not set" do
|
71
|
+
ENV.stub(:[]).and_return(nil)
|
72
|
+
ENV.stub(:has_key?).with("AWS_BUCKET_NAME").and_return(false)
|
73
|
+
expect{CloudCrooner.bucket_name}.to raise_error(CloudCrooner::FogSettingError, "Bucket name must be set in ENV or configure block")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "errors if aws access key id is unset" do
|
77
|
+
ENV.stub(:[]).and_return(nil)
|
78
|
+
ENV.stub(:has_key?).with("AWS_ACCESS_KEY_ID").and_return(false)
|
79
|
+
expect{CloudCrooner.aws_access_key_id}.to raise_error
|
80
|
+
end
|
81
|
+
|
82
|
+
it "errors if aws secret access key is unset" do
|
83
|
+
ENV.stub(:[]).and_return(nil)
|
84
|
+
ENV.stub(:has_key?).with("AWS_SECRET_ACCESS_KEY").and_return(false)
|
85
|
+
expect{CloudCrooner.aws_secret_access_key}.to raise_error
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'default configuration that touches filesystem' do
|
91
|
+
# aka: these tests require temp files and constructs
|
92
|
+
|
93
|
+
it 'creates a manifest' do
|
94
|
+
within_construct do |c|
|
95
|
+
c.directory 'public/assets'
|
96
|
+
|
97
|
+
expect(CloudCrooner.manifest).to be_an_instance_of(Sprockets::Manifest)
|
98
|
+
expect(CloudCrooner.manifest.dir).to eq(File.join(c, "public/assets"))
|
99
|
+
end #construct
|
100
|
+
end # it
|
101
|
+
|
102
|
+
it 'defaults assets to compile to files under prefix' do
|
103
|
+
within_construct do |c|
|
104
|
+
asset_folder = c.directory 'assets'
|
105
|
+
c.file('assets/a.css')
|
106
|
+
c.file('assets/b.css')
|
107
|
+
|
108
|
+
expect(CloudCrooner.assets_to_compile).to eq(%w(a.css b.css))
|
109
|
+
end # construct
|
110
|
+
end # it
|
111
|
+
|
112
|
+
it 'adds the default asset path to sprockets load path' do
|
113
|
+
within_construct do |c|
|
114
|
+
asset_folder = c.directory 'assets'
|
115
|
+
c.file('assets/a.css')
|
116
|
+
|
117
|
+
expect(CloudCrooner.sprockets['a.css']).to be_an_instance_of(Sprockets::BundledAsset)
|
118
|
+
expect(CloudCrooner.sprockets['a.css'].pathname.to_s).to eq(File.join(c, 'assets', 'a.css'))
|
119
|
+
|
120
|
+
end # construct
|
121
|
+
end # it
|
122
|
+
|
123
|
+
it 'initializes sprockets-helpers in development' do
|
124
|
+
within_construct do |c|
|
125
|
+
c.file 'assets/a.css'
|
126
|
+
CloudCrooner.configure_sprockets_helpers
|
127
|
+
|
128
|
+
expect(Sprockets::Helpers.prefix).to eq('/assets')
|
129
|
+
expect(context.stylesheet_tag('a.css')).to eq(%Q(<link rel="stylesheet" href="/assets/a.css">))
|
130
|
+
|
131
|
+
end # context
|
132
|
+
end #it
|
133
|
+
|
134
|
+
it 'initizalizes sprockets-helpers in production' do
|
135
|
+
within_construct do |c|
|
136
|
+
c.file 'assets/a.css'
|
137
|
+
stub_env_vars
|
138
|
+
ENV.stub(:[]).with('RACK_ENV').and_return("production")
|
139
|
+
CloudCrooner.configure_sprockets_helpers
|
140
|
+
CloudCrooner.manifest.compile('a.css')
|
141
|
+
|
142
|
+
expect(context.asset_path('a.css')).to eq("http://my-bucket.s3.amazonaws.com/assets/#{CloudCrooner.sprockets['a.css'].digest_path}")
|
143
|
+
end # construct
|
144
|
+
end # it
|
145
|
+
|
146
|
+
end # describe
|
147
|
+
|
148
|
+
describe 'custom configuration' do
|
149
|
+
|
150
|
+
it 'accepts a custom prefix' do
|
151
|
+
within_construct do |c|
|
152
|
+
CloudCrooner.configure do |config|
|
153
|
+
config.prefix = "meow"
|
154
|
+
end
|
155
|
+
expect(CloudCrooner.prefix).to eq("meow")
|
156
|
+
expect(Sprockets::Helpers.prefix).to eq("/meow")
|
157
|
+
expect(CloudCrooner.manifest.dir).to eq(File.join(c, "public/meow"))
|
158
|
+
end #context
|
159
|
+
end #it
|
160
|
+
|
161
|
+
it 'adds specified asset paths to load path' do
|
162
|
+
within_construct do |c|
|
163
|
+
c.file 'foo/bar.css'
|
164
|
+
CloudCrooner.configure do |config|
|
165
|
+
config.asset_paths = (%w(foo assets))
|
166
|
+
end
|
167
|
+
|
168
|
+
expect(CloudCrooner.sprockets['bar.css']).to be_an_instance_of(Sprockets::BundledAsset)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'can disable remote asset host' do
|
173
|
+
CloudCrooner.remote_enabled = false
|
174
|
+
expect(CloudCrooner.remote_enabled?).to be_false
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'initializes sprockets-helpers when remote is disabled' do
|
178
|
+
within_construct do |c|
|
179
|
+
# compile the manifest and asset in dev
|
180
|
+
c.file 'assets/a.css'
|
181
|
+
c.directory 'public/assets'
|
182
|
+
manifest = Sprockets::Manifest.new(CloudCrooner.sprockets, 'public/assets')
|
183
|
+
CloudCrooner.manifest = manifest
|
184
|
+
CloudCrooner.manifest.compile('a.css')
|
185
|
+
|
186
|
+
# reload the app & helpers in production
|
187
|
+
reload_crooner
|
188
|
+
|
189
|
+
ENV.stub(:[]).with('RACK_ENV').and_return('production')
|
190
|
+
CloudCrooner.configure do |config|
|
191
|
+
config.manifest = manifest
|
192
|
+
config.remote_enabled = false
|
193
|
+
end
|
194
|
+
|
195
|
+
expect(context.asset_path('a.css')).to eq("/assets/#{CloudCrooner.manifest.assets['a.css']}")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'accepts a custom manifest' do
|
200
|
+
within_construct do |c|
|
201
|
+
manifest = Sprockets::Manifest.new(CloudCrooner.sprockets, 'foo/bar')
|
202
|
+
CloudCrooner.configure do |config|
|
203
|
+
config.manifest = manifest
|
204
|
+
end
|
205
|
+
|
206
|
+
expect(CloudCrooner.manifest.dir).to eq(File.join(c,'foo/bar'))
|
207
|
+
end # construct
|
208
|
+
end # it
|
209
|
+
|
210
|
+
it 'accepts a list of assets to compile' do
|
211
|
+
within_construct do |c|
|
212
|
+
c.file 'assets/a.css'
|
213
|
+
c.file 'assets/b.css'
|
214
|
+
c.file 'assets/c.css'
|
215
|
+
|
216
|
+
CloudCrooner.assets_to_compile = %w(a.css b.css)
|
217
|
+
expect(CloudCrooner.assets_to_compile).to eq(%w(a.css b.css))
|
218
|
+
end # construct
|
219
|
+
end # it
|
220
|
+
|
221
|
+
it "allows bucket to be set in config and overwrites ENV setting" do
|
222
|
+
ENV.stub(:[]).with("AWS_BUCKET_NAME").and_return("test-bucket")
|
223
|
+
ENV.stub(:has_key?).with("AWS_BUCKET_NAME").and_return(true)
|
224
|
+
CloudCrooner.bucket_name = "foo_bucket"
|
225
|
+
|
226
|
+
expect(CloudCrooner.bucket_name).to eq("foo_bucket")
|
227
|
+
end
|
228
|
+
|
229
|
+
it "allows bucket to be set in config if none in env" do
|
230
|
+
CloudCrooner.bucket_name= "bar_bucket"
|
231
|
+
|
232
|
+
ENV.stub(:[]).with("AWS_BUCKET_NAME").and_return(nil)
|
233
|
+
ENV.stub(:has_key?).with("AWS_BUCKET_NAME").and_return(false)
|
234
|
+
expect(CloudCrooner.bucket_name).to eq("bar_bucket")
|
235
|
+
end # it
|
236
|
+
|
237
|
+
it "allows region to be set in config if none in env" do
|
238
|
+
CloudCrooner.region = "us-west-2"
|
239
|
+
|
240
|
+
expect(CloudCrooner.region).to eq("us-west-2")
|
241
|
+
end
|
242
|
+
|
243
|
+
it "allows region to be set in config and overwrites ENV setting" do
|
244
|
+
ENV.stub(:[]).with("AWS_REGION").and_return("eu-west-1")
|
245
|
+
ENV.stub(:has_key?).with("AWS_REGION").and_return(true)
|
246
|
+
CloudCrooner.region = "us-west-2"
|
247
|
+
|
248
|
+
expect(CloudCrooner.region).to eq("us-west-2")
|
249
|
+
end
|
250
|
+
|
251
|
+
it "errors if config region is not valid" do
|
252
|
+
expect{CloudCrooner.region = "el-dorado"}.to raise_error(CloudCrooner::FogSettingError)
|
253
|
+
end
|
254
|
+
|
255
|
+
it "allows aws_access_key_id to be set in config and overwrite ENV" do
|
256
|
+
ENV.stub(:[]).with("AWS_ACCESS_KEY_ID").and_return("asdf123")
|
257
|
+
ENV.stub(:has_key?).with("AWS_ACCESS_KEY_ID").and_return(true)
|
258
|
+
CloudCrooner.aws_access_key_id = "lkjh0987"
|
259
|
+
|
260
|
+
expect(CloudCrooner.aws_access_key_id).to eq("lkjh0987")
|
261
|
+
end
|
262
|
+
|
263
|
+
it "allows aws_access_key_id to be set in config if none in env" do
|
264
|
+
ENV.stub(:[]).with("AWS_ACCESS_KEY_ID").and_return(nil)
|
265
|
+
ENV.stub(:has_key?).with("AWS_ACCESS_KEY_ID").and_return(false)
|
266
|
+
CloudCrooner.aws_access_key_id = "lkjh0987"
|
267
|
+
|
268
|
+
expect(CloudCrooner.aws_access_key_id).to eq("lkjh0987")
|
269
|
+
end
|
270
|
+
|
271
|
+
it "allows aws_secret_access_key to be set in config and overwrite ENV" do
|
272
|
+
ENV.stub(:[]).with("AWS_SECRET_ACCESS_KEY").and_return("secret")
|
273
|
+
ENV.stub(:has_key?).with("AWS_SECRET_ACCESS_KEY").and_return(true)
|
274
|
+
CloudCrooner.aws_secret_access_key = "terces"
|
275
|
+
|
276
|
+
expect(CloudCrooner.aws_secret_access_key).to eq("terces")
|
277
|
+
end
|
278
|
+
|
279
|
+
it "allows secret access key to be set in config when ENV is empty" do
|
280
|
+
ENV.stub(:[]).with("AWS_SECRET_ACCESS_KEY").and_return(nil)
|
281
|
+
ENV.stub(:has_key?).with("AWS_SECRET_ACCESS_KEY").and_return(false)
|
282
|
+
CloudCrooner.aws_secret_access_key = "terces"
|
283
|
+
|
284
|
+
expect(CloudCrooner.aws_secret_access_key).to eq("terces")
|
285
|
+
end
|
286
|
+
|
287
|
+
it "sets the number of backups to keep" do
|
288
|
+
CloudCrooner.configure{|config| config.backups_to_keep= 5}
|
289
|
+
|
290
|
+
expect(CloudCrooner.backups_to_keep).to eq(5)
|
291
|
+
end
|
292
|
+
|
293
|
+
end # describe
|
294
|
+
|
295
|
+
it 'compiles assets' do
|
296
|
+
within_construct do |c|
|
297
|
+
mock_environment(c)
|
298
|
+
CloudCrooner.assets_to_compile = ['a.css', 'b.css']
|
299
|
+
(CloudCrooner.storage.local_compiled_assets).should == []
|
300
|
+
CloudCrooner.compile_sprockets_assets
|
301
|
+
|
302
|
+
expect(CloudCrooner.storage.local_compiled_assets).to eq(['assets/' + CloudCrooner.sprockets['a.css'].digest_path, 'assets/' + CloudCrooner.sprockets['b.css'].digest_path])
|
303
|
+
end # construct
|
304
|
+
end # it
|
305
|
+
|
306
|
+
it 'syncs assets to the cloud', :moo => true do
|
307
|
+
within_construct do |c|
|
308
|
+
mock_environment(c)
|
309
|
+
CloudCrooner.assets_to_compile = ['a.css', 'b.css']
|
310
|
+
mock_fog(CloudCrooner.storage)
|
311
|
+
CloudCrooner.sync
|
312
|
+
|
313
|
+
expect(local_equals_remote?(CloudCrooner.storage)).to be_true
|
314
|
+
end # construct
|
315
|
+
end # it
|
316
|
+
|
317
|
+
after(:each) do
|
318
|
+
reload_crooner
|
319
|
+
end
|
320
|
+
end #describe
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'cloud_crooner'
|
4
|
+
require 'construct'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'sprockets-helpers'
|
7
|
+
|
8
|
+
RSpec.configure do |rconf|
|
9
|
+
rconf.include Construct::Helpers
|
10
|
+
|
11
|
+
# don't pollute stdout with output during tests
|
12
|
+
original_stdout = $stdout
|
13
|
+
rconf.before(:all) do
|
14
|
+
# Redirect stderr and stdout
|
15
|
+
$stdout = File.new(File.join(File.dirname(__FILE__), 'rspec_output.txt'), 'w')
|
16
|
+
end
|
17
|
+
rconf.after(:all) do
|
18
|
+
$stdout = original_stdout
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload_crooner
|
22
|
+
# need to unset the class instance variables
|
23
|
+
Object.send(:remove_const, 'CloudCrooner')
|
24
|
+
load 'cloud_crooner/cloud_crooner.rb'
|
25
|
+
load 'cloud_crooner/storage.rb'
|
26
|
+
Sprockets::Helpers.instance_variables.each do |var|
|
27
|
+
Sprockets::Helpers.instance_variable_set var, nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# used for testing sprockets-helpers
|
32
|
+
def context(logical_path = 'application.js', pathname = nil)
|
33
|
+
pathname ||= Pathname.new(File.join('assets', logical_path)).expand_path
|
34
|
+
CloudCrooner.sprockets.context_class.new CloudCrooner.sprockets, logical_path, pathname
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def stub_env_vars
|
39
|
+
ENV.stub(:has_key?).and_return(false)
|
40
|
+
ENV.stub(:[]).and_return(nil)
|
41
|
+
|
42
|
+
ENV.stub(:[]).with('AWS_BUCKET_NAME').and_return('my-bucket')
|
43
|
+
ENV.stub(:has_key?).with('AWS_BUCKET_NAME').and_return(true)
|
44
|
+
|
45
|
+
ENV.stub(:[]).with('AWS_REGION').and_return('eu-west-1')
|
46
|
+
ENV.stub(:has_key?).with('AWS_REGION').and_return(true)
|
47
|
+
|
48
|
+
ENV.stub(:[]).with('AWS_ACCESS_KEY_ID').and_return('asdf123')
|
49
|
+
ENV.stub(:has_key?).with('AWS_ACCESS_KEY_ID').and_return(true)
|
50
|
+
|
51
|
+
ENV.stub(:[]).with('AWS_SECRET_ACCESS_KEY').and_return('secret')
|
52
|
+
ENV.stub(:has_key?).with('AWS_SECRET_ACCESS_KEY').and_return(true)
|
53
|
+
end
|
54
|
+
|
55
|
+
def sample_assets(construct)
|
56
|
+
lambda { |c|
|
57
|
+
c.file('assets/main.js') do |f|
|
58
|
+
f << "//= require a\n"
|
59
|
+
f << "//= require b\n"
|
60
|
+
end
|
61
|
+
c.file('assets/a.js') do |f|
|
62
|
+
f << "var pi=3.14;"
|
63
|
+
end
|
64
|
+
c.file('assets/b.js') do |f|
|
65
|
+
f << "var person='John Doe';"
|
66
|
+
end
|
67
|
+
|
68
|
+
c.file('assets/main.css') do |f|
|
69
|
+
f << "/*\n"
|
70
|
+
f << "*= require a\n"
|
71
|
+
f << "*= require b\n"
|
72
|
+
f << "*/\n"
|
73
|
+
end
|
74
|
+
c.file('assets/a.css') do |f|
|
75
|
+
f << "p { color: red; }\n"
|
76
|
+
end
|
77
|
+
c.file('assets/b.css') do |f|
|
78
|
+
f << "li { color: pink; }"
|
79
|
+
end
|
80
|
+
c.file('assets/c.css') do |f|
|
81
|
+
f << "h1{color:blue;}\n"
|
82
|
+
f << "h2{color:blue;}\n"
|
83
|
+
f << "h3{color:blue;}\n"
|
84
|
+
end
|
85
|
+
}.call(construct)
|
86
|
+
end
|
87
|
+
|
88
|
+
def local_equals_remote?(storage)
|
89
|
+
# the remote files are not guaranteed to be ordered
|
90
|
+
frequency(storage.local_compiled_assets) == frequency(storage.remote_assets)
|
91
|
+
end
|
92
|
+
|
93
|
+
def frequency(arr)
|
94
|
+
# http://stackoverflow.com/questions/9095017/comparing-two-arrays-in-ruby
|
95
|
+
p = Hash.new(0)
|
96
|
+
arr.each{ |v| p[v] += 1 }
|
97
|
+
p
|
98
|
+
end
|
99
|
+
|
100
|
+
def uncompiled_assets_dir(construct)
|
101
|
+
"#{construct}" + "/assets"
|
102
|
+
end
|
103
|
+
|
104
|
+
def mock_fog(storage)
|
105
|
+
Fog.mock!
|
106
|
+
storage.connection.directories.create(
|
107
|
+
:key => storage.instance_variable_get(:@bucket_name),
|
108
|
+
:public => true
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def mock_environment(c)
|
113
|
+
# requires a construct
|
114
|
+
CloudCrooner.configure do |config|
|
115
|
+
config.bucket_name = SecureRandom.hex
|
116
|
+
end
|
117
|
+
stub_env_vars
|
118
|
+
sample_assets(c)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|