usmu-s3 1.0.0

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: a2bf26b514877f1596619bb43c3f6a702fd26be9
4
+ data.tar.gz: d2ad7cd9958cf71e5e68a123555829d08677db50
5
+ SHA512:
6
+ metadata.gz: a040b35e4fc077cd0b82677b2efe3cc0608be03842e0890bea510af89827ee245de5425e2334a427f50673ffb37298fb7b75910116c2b9fc30bf77f272b98cc5
7
+ data.tar.gz: b05383994090614f96c6befb4d90122d0ed92d5cbcd5362d55e9c9933307a5c649c940bcd0748719d34ab8d34fc409cd5fe1ec7a975508c02aed41d2f4bca097
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile*.lock
4
+ /_yardoc/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,6 @@
1
+ --color
2
+ -I spec
3
+ -r turnip/rspec
4
+ --require spec_helper
5
+ --format documentation
6
+ --no-profile
data/.travis.yml ADDED
@@ -0,0 +1,33 @@
1
+ language: ruby
2
+ script: rake ci
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2
8
+ - ruby-head
9
+ - mruby-head
10
+ - rbx-2.1
11
+ - rbx-2.2
12
+ - rbx-2.3
13
+ - rbx-2.4
14
+ matrix:
15
+ include:
16
+ - rvm: jruby
17
+ - rvm: jruby
18
+ env: JRUBY_OPTS='--2.0'
19
+ - rvm: jruby-head
20
+ - rvm: jruby-head
21
+ env: JRUBY_OPTS='--2.0'
22
+ allow_failures:
23
+ - rvm: 1.9.3
24
+ - rvm: jruby
25
+ - rvm: jruby-head
26
+ - rvm: mruby-head
27
+ - rvm: rbx-2.1
28
+ - rvm: rbx-2.2
29
+ - rvm: rbx-2.3
30
+ - rvm: rbx-2.4
31
+ addons:
32
+ code_climate:
33
+ repo_token: ebb97d3c606e22753911d08f39444a86f27a7da6a40d834f94d640375615fff8
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --private --protected --default-return void -m markdown -M redcarpet -- lib/**/*.rb - README.md CHANGELOG.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Usmu S3 Upload plugin changelog
2
+
3
+ ## 1.0.0
4
+
5
+ Matthew Scharley <matt.scharley@gmail.com>
6
+
7
+ * Fix readme (fea8602d016709c089b3c8b66b6487b07ff48d05)
8
+ * Update build system from Usmu core (65487dfac5a76c8fa9011a7c5286cf2ca2b59b4d)
9
+ * Add an S3Configuration which can read from ENV (2fa8a5d44e539f89dc044c8821ee10e45769d19c)
10
+ * Add gemnasium badge (0e7e2b74b143dd0ac54c38335aef7cf964783dfa)
11
+ * Add spec for S3Configuration (541b1624a524482b57f24b4213792176af480c41)
12
+ * Add Code Coverage (a6b7617b5be714fc1a8fefcaf23f72a6304cfc1d)
13
+ * Convert to Code Climate's coverage data (992c27a773de91034b054a2e3c6c80636c4ef147)
14
+ * Include the AWS SDK and update configuration to use their env vars (4d6a342050eab38f1a2dfbb7db7ac659bf1c5b5e)
15
+ * Add mutant (943c29c358341ae4cb498d6c7e0d3ac7e20cfe3d)
16
+ * Prepare for the deployment API (57c05d4c30b53e62598cdca89897b133b84acbaa)
17
+ * Update coverage for S3Configuration (26287e6089b1071f375adbb793524bbae9d563cb)
18
+ * Tests for completed sections of Usmu::S3 (2c6c9966a6274378efa606b4183c0c819aceb5dd)
19
+ * Add a RemoteFileInterface implementation (d92ee043bf1a1e093927b4930523c7bc4690c770)
20
+ * Add support for uploading to S3 (4f07cd33f7b79f1ecb7cc6b6a00431df99284d1a)
21
+ * Complete 100% mutant test coverage (a1fac102bf7caa7a00fed8a79e54c3a527234efd)
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Contributing to usmu
2
+
3
+ 1. Fork it
4
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
5
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
6
+ 4. Push to the branch (`git push origin my-new-feature`)
7
+ 5. Create a new Pull Request
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in usmu.gemspec
4
+ gemspec name: 'usmu-s3'
5
+
6
+ gem 'codeclimate-test-reporter', group: :test, require: nil
7
+
8
+ if RUBY_VERSION.to_f >= 2 && RUBY_VERSION.to_f < 2.2 && RUBY_ENGINE == 'ruby'
9
+ gem 'mutant', '~> 0.7'
10
+ gem 'mutant-rspec', '~> 0.7'
11
+ end
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, cmd: 'rspec', spec_paths: ['spec'] do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/usmu/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch(%r{^spec/support}) { 'spec' }
8
+ watch('spec/spec_helper.rb') { 'spec' }
9
+
10
+ # Turnip features and steps
11
+ watch(%r{^spec/acceptance/(.+)\.feature$})
12
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
13
+ end
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Matthew Scharley and contributors
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # usmu-s3
2
+
3
+ [![Build Status](https://travis-ci.org/usmu/usmu-s3.svg?branch=master)](https://travis-ci.org/usmu/usmu-s3)
4
+ [![Dependency Status](https://gemnasium.com/usmu/usmu-s3.svg)](https://gemnasium.com/usmu/usmu-s3)
5
+ [![Code Climate](https://codeclimate.com/github/usmu/usmu-s3/badges/gpa.svg)](https://codeclimate.com/github/usmu/usmu-s3)
6
+
7
+ **Source:** [https://github.com/usmu/usmu-s3](https://github.com/usmu/usmu-s3)
8
+ **Author:** Matthew Scharley
9
+ **Contributors:** [See contributors on GitHub][gh-contrib]
10
+ **Bugs/Support:** [Github Issues][gh-issues]
11
+ **Copyright:** 2014
12
+ **License:** [MIT license][license]
13
+ **Status:** Active
14
+
15
+ ## Synopsis
16
+
17
+ Allows you to deploy your [Usmu][usmu] website to Amazon's S3 service.
18
+
19
+ TODO: Write synopsis here
20
+
21
+ ## Installation
22
+
23
+ $ gem install usmu-s3
24
+
25
+ ## Usage
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ [gh-contrib]: https://github.com/usmu/usmu-s3/graphs/contributors
30
+ [gh-issues]: https://github.com/usmu/usmu-s3/issues
31
+ [license]: https://github.com/usmu/usmu-s3/blob/master/LICENSE.md
32
+ [usmu]: https://github.com/usmu/usmu
data/Rakefile ADDED
@@ -0,0 +1,92 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'usmu/s3/version'
6
+
7
+ def current_gems
8
+ Dir["pkg/usmu-s3-#{Usmu::S3::VERSION}*.gem"]
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:spec) do |t|
12
+ t.pattern = 'spec/**/*_spec.rb'
13
+ end
14
+
15
+ desc 'Run all test scripts'
16
+ task :test => [:clean, :spec, :mutant]
17
+
18
+ desc 'Run mutation tests'
19
+ task :mutant, [:target] => [:clean] do |t,args|
20
+ old = ENV.delete('CODECLIMATE_REPO_TOKEN')
21
+ if `which mutant 2>&1 > /dev/null; echo \$?`.to_i != 0
22
+ puts 'Mutant isn\'t supported on your platform. Please run these tests on MRI <= 2.1.5.'
23
+ else
24
+ sh('bundle', 'exec', 'mutant', '--include', 'lib', '--require', 'usmu/s3', '--use', 'rspec', args[:target] || 'Usmu::S3*')
25
+ end
26
+ ENV['CODECLIMATE_REPO_TOKEN'] = old unless old.nil?
27
+ end
28
+
29
+ desc 'Run CI test suite'
30
+ task :ci => [:clean, :spec]
31
+
32
+ desc 'Clean up after tests'
33
+ task :clean do
34
+ [
35
+ 'test/coverage',
36
+ current_gems,
37
+ ].flatten.each do |f|
38
+ rm_r f if File.exist? f
39
+ end
40
+ end
41
+
42
+ namespace :gem do
43
+ desc 'Build gems'
44
+ task :build => [:clean] do
45
+ mkdir 'pkg' unless File.exist? 'pkg'
46
+ Dir['*.gemspec'].each do |gemspec|
47
+ sh "gem build #{gemspec}"
48
+ end
49
+ Dir['*.gem'].each do |gem|
50
+ mv gem, "pkg/#{gem}"
51
+ end
52
+ end
53
+
54
+ desc 'Install gem'
55
+ task :install => ['gem:build'] do
56
+ sh "gem install pkg/usmu-s3-#{Usmu::S3::VERSION}.gem"
57
+ end
58
+
59
+ desc 'Deploy gems to rubygems'
60
+ task :deploy => ['gem:build'] do
61
+ current_gems.each do |gem|
62
+ sh "gem push #{gem}"
63
+ end
64
+ sh "git tag #{Usmu::S3::VERSION}" if File.exist? '.git'
65
+ end
66
+ end
67
+
68
+ # (mostly) borrowed from: https://gist.github.com/mcansky/802396
69
+ desc 'generate changelog with nice clean output'
70
+ task :changelog, :since_c, :until_c do |t,args|
71
+ since_c = args[:since_c] || `git tag | egrep '^[0-9]+\\.[0-9]+\\.[0-9]+\$' | sort -Vr | head -n 1`.chomp
72
+ until_c = args[:until_c]
73
+ cmd=`git log --pretty='format:%ci::::%an <%ae>::::%s::::%H' #{since_c}..#{until_c}`
74
+
75
+ entries = Hash.new
76
+ changelog_content = "\#\# #{Usmu::S3::VERSION}\n\n"
77
+
78
+ cmd.lines.each do |entry|
79
+ date, author, subject, hash = entry.chomp.split('::::')
80
+ entries[author] = Array.new unless entries[author]
81
+ day = date.split(' ').first
82
+ entries[author] << "#{subject} (#{hash})" unless subject =~ /Merge/
83
+ end
84
+
85
+ # generate clean output
86
+ entries.keys.each do |author|
87
+ changelog_content += author + "\n\n"
88
+ entries[author].reverse.each { |entry| changelog_content += "* #{entry}\n" }
89
+ end
90
+
91
+ puts changelog_content
92
+ end
@@ -0,0 +1,46 @@
1
+ require 'usmu/deployment/remote_file_interface'
2
+ require 'aws-sdk'
3
+
4
+ module Usmu
5
+ class S3
6
+ class RemoteFiles
7
+ include Usmu::Deployment::RemoteFileInterface
8
+
9
+ # @param [Usmu::S3::Configuration] configuration
10
+ # An S3 configuration to provide connection details to the remote S3 bucket.
11
+ def initialize(configuration)
12
+ @s3 = Aws::S3::Resource.new(credentials: configuration.credentials, region: configuration.region)
13
+ @bucket = @s3.bucket(configuration.bucket)
14
+ end
15
+
16
+ # @see Usmu::Deployment::RemoteFileInterface#files_list
17
+ def files_list
18
+ objects.map {|o| o.key }
19
+ end
20
+
21
+ # @see Usmu::Deployment::RemoteFileInterface#stat
22
+ def stat(filename)
23
+ obj = objects.select {|o| o.key.eql? filename }.first
24
+ return nil if obj.nil?
25
+
26
+ etag = unless obj.etag.index('-')
27
+ obj.etag.gsub(/^["]|["]$/, '')
28
+ end
29
+
30
+ {
31
+ md5: etag,
32
+ mtime: obj.last_modified,
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :s3
39
+ attr_reader :bucket
40
+
41
+ def objects
42
+ @objects ||= @bucket.objects.to_a
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ require 'aws-sdk'
2
+
3
+ module Usmu
4
+ class S3
5
+ class S3Configuration
6
+ def initialize(hash)
7
+ @config = hash
8
+ end
9
+
10
+ def region
11
+ substitute_env(@config['region'] || '%env{AWS_REGION}')
12
+ end
13
+
14
+ def access_key
15
+ substitute_env(@config['access key'] || '%env{AWS_ACCESS_KEY_ID}')
16
+ end
17
+
18
+ def secret_key
19
+ substitute_env(@config['secret key'] || '%env{AWS_SECRET_ACCESS_KEY}')
20
+ end
21
+
22
+ def bucket
23
+ substitute_env @config['bucket'] || ''
24
+ end
25
+
26
+ def inspect
27
+ "\#<#{self.class} region=\"#{region}\" access_key=\"#{access_key}\" bucket=\"#{bucket}\">"
28
+ end
29
+
30
+ def credentials
31
+ @credentials ||= Aws::Credentials.new(access_key, secret_key)
32
+ end
33
+
34
+ private
35
+
36
+ def substitute_env(string)
37
+ string.gsub(%r[%env\{([^}]*)\}]) { ENV[$1] }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+
2
+ module Usmu
3
+ class S3
4
+ class Uploader
5
+ # @param [Usmu::Configuration] configuration
6
+ # @param [Usmu::S3::S3Configuration] s3_configuration
7
+ def initialize(configuration, s3_configuration)
8
+ @log = Logging.logger[self]
9
+ @configuration = configuration
10
+ s3 = Aws::S3::Resource.new(credentials: s3_configuration.credentials, region: s3_configuration.region)
11
+ @bucket = s3.bucket(s3_configuration.bucket)
12
+ end
13
+
14
+ def push(diff)
15
+ push_local(diff[:local])
16
+ push_local(diff[:updated])
17
+ delete_remote(diff[:remote])
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :log
23
+ attr_reader :configuration
24
+ attr_reader :bucket
25
+
26
+ def push_local(files)
27
+ for file in files
28
+ @log.success("Uploading #{file}")
29
+ File.open(File.join(@configuration.destination_path, file), 'r') do |io|
30
+ @bucket.put_object({
31
+ key: file,
32
+ body: io,
33
+ })
34
+ end
35
+ end
36
+ end
37
+
38
+ def delete_remote(files)
39
+ # We can 'only' delete 1000 items at a time with #delete_objects.
40
+ for i in (0...files.length).step(1000)
41
+ deleting = files.slice(i, 1000)
42
+ for f in deleting
43
+ @log.success("Deleting #{f}")
44
+ end
45
+ @bucket.delete_objects({delete: {objects: deleting.map { |f| {key: f} }}})
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+
2
+ module Usmu
3
+ class S3
4
+ # The current version string for the gem
5
+ VERSION = '1.0.0'
6
+ end
7
+ end
data/lib/usmu/s3.rb ADDED
@@ -0,0 +1,52 @@
1
+ %w{
2
+ aws-sdk
3
+ logging
4
+ usmu/deployment
5
+ usmu/s3/remote_files
6
+ usmu/s3/s3_configuration
7
+ usmu/s3/uploader
8
+ usmu/s3/version
9
+ }.each {|f| require f }
10
+
11
+ # Module from Usmu Core
12
+ module Usmu
13
+ # Usmu::S3 is the main plugin class for this plugin. It contains entry hooks as required to implement the S3
14
+ # deployment interface.
15
+ class S3
16
+ def initialize
17
+ @log = Logging.logger[self]
18
+ @log.debug("Initializing usmu-s3 v#{VERSION}")
19
+ end
20
+
21
+ # @see Usmu::Plugin::CoreHooks#commands
22
+ def commands(ui, c)
23
+ @log.debug('Adding commands from usmu-s3.')
24
+ @ui = ui
25
+
26
+ c.command(:'s3 deploy') do |command|
27
+ command.syntax = 'usmu s3 deploy'
28
+ command.description = 'Deploys your website to S3'
29
+ command.action &method(:command_deploy)
30
+ end
31
+ end
32
+
33
+ def command_deploy(args = [], options = {})
34
+ raise 'This command does not take arguments.' unless args.empty?
35
+ raise 'Invalid options, must be a Hash.' unless options.instance_of? Hash
36
+
37
+ configuration = @ui.configuration
38
+ s3_configuration = S3Configuration.new(configuration['plugin', 's3', default: {}])
39
+ @log.info('Gathering information...')
40
+ diff = Deployment::DirectoryDiff.new(configuration, RemoteFiles.new(s3_configuration))
41
+ @log.info('Uploading files.')
42
+ uploader = Uploader.new(configuration, s3_configuration)
43
+ uploader.push(diff.get_diffs)
44
+ @log.info('Website updated successfully.')
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :log
50
+ attr_accessor :ui
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module Usmu
3
+ class CommanderMock
4
+ def initialize
5
+ @commands = {}
6
+ end
7
+
8
+ def get_command(name)
9
+ @commands[name]
10
+ end
11
+
12
+ def command(name, &block)
13
+ @current_command = name
14
+ @commands[name] = {}
15
+
16
+ block.yield(self)
17
+ end
18
+
19
+ def syntax=(value)
20
+ @commands[@current_command][:syntax] = value
21
+ end
22
+
23
+ def description=(value)
24
+ @commands[@current_command][:description] = value
25
+ end
26
+
27
+ def action(&block)
28
+ @commands[@current_command][:action] = block
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+
2
+ module Usmu
3
+ class S3ServiceMock
4
+ attr_accessor :objects
5
+
6
+ class ArrayWrapper
7
+ def initialize(objects)
8
+ @objects = objects
9
+ end
10
+
11
+ def to_a
12
+ @objects
13
+ end
14
+ end
15
+
16
+ def initialize(objects)
17
+ @objects = ArrayWrapper.new(objects)
18
+ end
19
+
20
+ def bucket(name)
21
+ @bucket = name
22
+ self
23
+ end
24
+
25
+ def get_bucket
26
+ @bucket
27
+ end
28
+
29
+ def put_object(hash)
30
+ end
31
+
32
+ def delete_objects(request)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module Usmu
3
+ class UploaderMock
4
+ attr_reader :diffs
5
+
6
+ def push(diffs)
7
+ @diffs = diffs
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,74 @@
1
+ require 'usmu/s3/s3_configuration'
2
+ require 'usmu/s3/remote_files'
3
+ require 'usmu/s3_service_mock'
4
+ require 'ostruct'
5
+
6
+ RSpec.describe Usmu::S3::RemoteFiles do
7
+ let(:now) { Time.now }
8
+ let(:configuration) {
9
+ Usmu::S3::S3Configuration.new(
10
+ {
11
+ 'access key' => 'access',
12
+ 'secret key' => 'secret',
13
+ 'region' => 'us-east-1',
14
+ 'bucket' => 'bucket',
15
+ }
16
+ )
17
+ }
18
+ let(:creds) { OpenStruct.new({ access_key: 'access', secret_key: 'secret' }) }
19
+ let(:s3) {
20
+ Usmu::S3ServiceMock.new(
21
+ [
22
+ OpenStruct.new({ key: 'index.html', etag: '"dffd613ea3d712d38c230c810be2e4a6"', last_modified: now }),
23
+ OpenStruct.new({ key: 'sitemap.xml', etag: '"dffd613ea3d712d38c230c810be2e4a6-5"', last_modified: now }),
24
+ ]
25
+ )
26
+ }
27
+ let(:remote) { Usmu::S3::RemoteFiles.new(configuration) }
28
+
29
+ before do
30
+ expect(configuration).to receive(:credentials).and_return(creds)
31
+ expect(Aws::S3::Resource).to receive(:new).with({credentials: creds, region: 'us-east-1'}).and_return(s3)
32
+ end
33
+
34
+ context '#initialize' do
35
+ it 'creates an S3 resource instance' do
36
+ remote
37
+ expect(remote.send :s3).to eq(s3)
38
+ end
39
+
40
+ it 'finds the relevant S3 bucket' do
41
+ remote
42
+ expect(remote.send :bucket).to eq(s3)
43
+ expect(s3.get_bucket).to eq(configuration.bucket)
44
+ end
45
+ end
46
+
47
+ context '#files_list' do
48
+ it 'returns a list of file names' do
49
+ expect(remote.files_list.sort).to eq(['index.html', 'sitemap.xml'])
50
+ end
51
+ end
52
+
53
+ context '#stat' do
54
+ it 'returns a hash of information' do
55
+ expect(remote.stat('index.html')).to eq({md5: 'dffd613ea3d712d38c230c810be2e4a6', mtime: now})
56
+ end
57
+
58
+ it 'doesn\'t return an :md5 element for multi-part uploads' do
59
+ expect(remote.stat('sitemap.xml')).to eq({md5: nil, mtime: now})
60
+ end
61
+
62
+ it 'returns nil if there is no information available' do
63
+ expect(remote.stat('jquery.js')).to eq(nil)
64
+ end
65
+ end
66
+
67
+ context '#objects' do
68
+ it 'returns a list of objects about files' do
69
+ objs = remote.send :objects
70
+ expect(objs.length).to eq(2)
71
+ expect(objs.map {|o| o.key }.sort).to eq(['index.html', 'sitemap.xml'])
72
+ end
73
+ end
74
+ end