static_sync 0.0.1 → 0.1.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.
data/README.md CHANGED
@@ -1,13 +1,19 @@
1
1
  # StaticSync
2
2
 
3
- This gem provides a stand alone mechanism for uploading static websites to cloud hosting providers such as
4
- [Amazon AWS](http://en.wikipedia.org/wiki/Amazon_S3#Hosting_entire_websites).
3
+ This gem provides a command line tool for uploading static websites to amazon/rackspace.
5
4
 
6
5
  ### Features
7
6
 
8
- * Standalone.
9
- * Configurable caching.
10
- * Automatic gzip compression.
7
+ * Compares the md5 cheksum of each file before deciding to upload it.
8
+ * Automatically gzips non binary files.
9
+ * Sets appropriate HTTP metadata header information for serving html files.
10
+ * Content Type (text/css, image/png etc).
11
+ * Cache Control (public, max-age=31536000).
12
+ * Content Enconding (gzip).
13
+
14
+ ## Requirements
15
+
16
+ * Ruby 1.9
11
17
 
12
18
  ## Installation
13
19
 
@@ -15,23 +21,36 @@ This gem provides a stand alone mechanism for uploading static websites to cloud
15
21
  gem install static_sync
16
22
  ```
17
23
 
18
- ## Usage
24
+ ## Example Usage
19
25
 
20
26
  In your project directory create a `.static` file:
21
27
 
22
28
  ```
23
29
  > cat .static
30
+
31
+ # Source directory
24
32
  local:
25
- directory: build # The directory to upload
33
+ directory: build
26
34
 
35
+ # Target directory
27
36
  remote:
28
37
  provider: AWS
29
38
  username: my-aws-key
30
39
  password: my-aws-secret
31
40
  directory: my-aws-bucket
41
+
42
+ # Number of seconds to cache each content type, defaults to no cache.
43
+ cache:
44
+ html: 31536000
45
+ javascript: 31536000
46
+ css: 31536000
47
+ image: 31536000
48
+
49
+ # Flag to enable / disable automatic gzip compression.
50
+ gzip: true
32
51
  ```
33
52
 
34
- And run the following command any time you want to upload.
53
+ And simply run the following command any time you want to upload.
35
54
 
36
55
  ```bash
37
56
  static_sync
@@ -49,28 +68,6 @@ remote:
49
68
  directory: <%= ENV['s3_bucket'] %>
50
69
  ```
51
70
 
52
- ### Cache Control
53
-
54
- By default uploaded files are not cached.
55
-
56
- You can cache content for a given number of seconds by updating your `.static` file:
57
-
58
- ```
59
- cache:
60
- javascript: 31536000
61
- css: 31536000
62
- ```
63
-
64
- ### Compression
65
-
66
- By default uploaded files are not compressed.
67
-
68
- You can gzip all non binary content by updating your `.static` file:
69
-
70
- ```
71
- gzip: true
72
- ```
73
-
74
71
  ## Contributing
75
72
 
76
73
  1. Fork it
data/bin/static_sync CHANGED
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "rubygems"
4
- require "static_sync"
3
+ require_relative "../lib/static_sync"
5
4
 
6
5
  StaticSync.upload
7
-
data/lib/static_sync.rb CHANGED
@@ -1,10 +1,3 @@
1
- require "rubygems"
2
- require "fog"
3
- require "erb"
4
- require "logger"
5
- require "tempfile"
6
- require "zlib"
7
-
8
1
  require_relative "static_sync/version"
9
2
  require_relative "static_sync/config"
10
3
  require_relative "static_sync/storage"
@@ -1,10 +1,14 @@
1
+ require "erb"
2
+
1
3
  module StaticSync
2
4
  class Config < Hash
3
5
 
4
- # TODO Validate.
6
+ def log
7
+ self.fetch('log', true)
8
+ end
5
9
 
6
10
  def cache
7
- self['cache'] || {}
11
+ self.fetch('cache', {})
8
12
  end
9
13
 
10
14
  def source
@@ -1,45 +1,42 @@
1
+ require "digest/md5"
2
+ require "mime/types"
3
+
4
+ require_relative "meta/caching"
5
+ require_relative "meta/compression"
6
+
1
7
  module StaticSync
2
8
  class Meta
3
9
 
4
10
  def initialize(config)
5
- @config = config
11
+ @config = config
12
+ @compression = Compression.new(@config)
13
+ @caching = Caching.new(@config)
6
14
  end
7
15
 
8
- #TODO This class / method has too many responsibilities.
9
16
  def for(file)
10
17
  meta = {
11
- :key => file,
12
- :body => File.new(file),
13
- :public => true
18
+ :key => file,
19
+ :body => File.new(file),
20
+ :public => true,
21
+ :storage_class => 'REDUCED_REDUNDANCY'
14
22
  }
15
- MIME::Types::of(file).each do |mime|
16
- if @config.gzip
17
- unless mime.binary?
18
- meta.merge!(
19
- :body => gzip(file),
20
- :content_encoding => 'gzip'
21
- )
22
- end
23
- end
23
+
24
+ mime = MIME::Types::of(file).first
25
+
26
+ if mime
24
27
  meta.merge!(
25
28
  :content_type => MIME::Type.simplified(mime)
26
29
  )
27
- if @config.cache.has_key?(mime.sub_type)
28
- expiry = @config.cache[mime.sub_type].to_i
29
- meta.merge!(
30
- :cache_control => "public, max-age=#{expiry}"
31
- )
32
- end
30
+ meta.merge!(@compression.for(file, mime))
31
+ meta.merge!(@caching.for(file, mime))
33
32
  end
34
- meta
35
- end
36
33
 
37
- def gzip(file)
38
- zipped = Tempfile.new("static_sync")
39
- Zlib::GzipWriter.open(zipped) do |archive|
40
- archive.write File.read(file)
41
- end
42
- zipped
34
+ body = meta[:body].read
35
+
36
+ meta.merge!(
37
+ :etag => Digest::MD5.hexdigest(body), # A local version of the etag expected after upload.
38
+ :content_md5 => Digest::MD5.base64digest(body) # Enable checksum validation during uploads.
39
+ )
43
40
  end
44
41
 
45
42
  end
@@ -0,0 +1,24 @@
1
+ module StaticSync
2
+ class Meta
3
+ class Caching
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def for(file, mime)
10
+ meta = {}
11
+ type = mime.sub_type
12
+ type = mime.media_type if mime.media_type == "image"
13
+ if @config.cache.has_key?(type)
14
+ expiry = @config.cache[type].to_i
15
+ meta.merge!(
16
+ :cache_control => "public, max-age=#{expiry}"
17
+ )
18
+ end
19
+ meta
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ require "tempfile"
2
+ require "zlib"
3
+
4
+ module StaticSync
5
+ class Meta
6
+ class Compression
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ def for(file, mime)
13
+ meta = {}
14
+ if @config.gzip && !mime.binary?
15
+ meta.merge!(
16
+ :body => gzip(file),
17
+ :content_encoding => 'gzip'
18
+ )
19
+ end
20
+ meta
21
+ end
22
+
23
+ private
24
+
25
+ def gzip(file)
26
+ zipped = Tempfile.new("static_sync")
27
+ Zlib::GzipWriter.open(zipped) do |archive|
28
+ # Gzip by default incorporates file creation time into the content.
29
+ # For the purpose of repeatable etag comparison we want avoid this.
30
+ archive.mtime = 1
31
+ archive.write File.read(file)
32
+ end
33
+ zipped
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,6 @@
1
+ require "fog"
2
+ require "logger"
3
+
1
4
  require_relative "meta"
2
5
 
3
6
  module StaticSync
@@ -9,18 +12,40 @@ module StaticSync
9
12
  end
10
13
 
11
14
  def sync
15
+ remote_keys = []
16
+ remote_directory.files.each do |file|
17
+ remote_keys << [file.key, file.etag]
18
+ end
12
19
  Dir.chdir(@config.source) do
13
- Dir.glob("**/*.*") do |file|
14
- log.info("Uploading #{file}")
15
- @config.storage.directories.get(@config.storage_directory).files.create(
16
- @meta.for(file)
17
- )
20
+ local_files.each do |file|
21
+ current_file = @meta.for(file)
22
+ current_file_key = [current_file[:key], current_file[:etag]]
23
+
24
+ unless remote_keys.include?(current_file_key)
25
+ log.info("Uploading #{file}") if @config.log
26
+ begin
27
+ remote_directory.files.create(current_file)
28
+ rescue => error
29
+ log.error("Failed to upload #{file}")
30
+ raise error
31
+ end
32
+ end
18
33
  end
19
34
  end
20
35
  end
21
36
 
22
37
  private
23
38
 
39
+ def local_files
40
+ Dir.glob("**/*.*").reject do |file|
41
+ File.directory?(file)
42
+ end
43
+ end
44
+
45
+ def remote_directory
46
+ @config.storage.directories.new(:key => @config.storage_directory)
47
+ end
48
+
24
49
  def log
25
50
  @log ||= begin
26
51
  logger = Logger.new(STDOUT)
@@ -1,3 +1,3 @@
1
1
  module StaticSync
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
File without changes
File without changes
@@ -0,0 +1,75 @@
1
+ require "mime/types"
2
+ require "./lib/static_sync/config"
3
+ require "./lib/static_sync/meta/caching"
4
+
5
+ describe StaticSync::Meta::Caching do
6
+
7
+ let(:config) { StaticSync::Config.new }
8
+
9
+ let(:html) { MIME::Type.new('text/html') }
10
+ let(:plain) { MIME::Type.new('text/plain') }
11
+ let(:css) { MIME::Type.new('text/css') }
12
+ let(:gif) { MIME::Type.new('image/gif') }
13
+ let(:jpg) { MIME::Type.new('image/jpg') }
14
+ let(:png) { MIME::Type.new('image/png') }
15
+
16
+ subject do
17
+ StaticSync::Meta::Caching.new(config)
18
+ end
19
+
20
+ context "when caching is completely disabled" do
21
+
22
+ before do
23
+ config['cache'] = { }
24
+ end
25
+
26
+ describe "#for" do
27
+ it "does not set cache headers for html files" do
28
+ subject.for("index.html", html).should_not include(:cache_control)
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when caching is enabled for html only" do
34
+
35
+ before do
36
+ config['cache'] = { 'html' => '86400' }
37
+ end
38
+
39
+ describe "#for" do
40
+ it "sets cache headers for html files" do
41
+ subject.for("index.html", html).should include(:cache_control)
42
+ end
43
+
44
+ it "does not set cache headers for text files" do
45
+ subject.for("data.txt", plain).should_not include(:cache_control)
46
+ end
47
+
48
+ it "does not set cache headers for css files" do
49
+ subject.for("screen.css", css).should_not include(:cache_control)
50
+ end
51
+ end
52
+ end
53
+
54
+ context "when caching is enabled for images" do
55
+
56
+ before do
57
+ config['cache'] = { 'image' => '86400' }
58
+ end
59
+
60
+ describe "#for" do
61
+ it "sets cache headers for gif files" do
62
+ subject.for("spinner.gif", gif).should include(:cache_control)
63
+ end
64
+
65
+ it "sets cache headers for jpg files" do
66
+ subject.for("kitten.jpg", jpg).should include(:cache_control)
67
+ end
68
+
69
+ it "sets cache headers for png files" do
70
+ subject.for("cat.png", png).should include(:cache_control)
71
+ end
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,50 @@
1
+ require "mime/types"
2
+ require "./lib/static_sync/config"
3
+ require "./lib/static_sync/meta/compression"
4
+
5
+ describe StaticSync::Meta::Compression do
6
+
7
+ let(:config) { StaticSync::Config.new }
8
+
9
+ let(:html) { MIME::Type.new('text/html') }
10
+ let(:image) { MIME::Type.new('image/gif') }
11
+
12
+ subject do
13
+ StaticSync::Meta::Compression.new(config)
14
+ end
15
+
16
+ before do
17
+ File.stub(:read).and_return("")
18
+ end
19
+
20
+ context "when compression is disabled" do
21
+
22
+ before do
23
+ config['gzip'] = false
24
+ end
25
+
26
+ describe "#for" do
27
+ it "does not set compression headers for html files" do
28
+ subject.for("index.html", html).should_not include(:content_encoding => "gzip")
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when compression is enabled" do
34
+
35
+ before do
36
+ config['gzip'] = true
37
+ end
38
+
39
+ describe "#for" do
40
+ it "sets compression headers for html files" do
41
+ subject.for("index.html", html).should include(:content_encoding => "gzip")
42
+ end
43
+
44
+ it "does not set compression headers for binary files" do
45
+ subject.for("assets/images/spinner.gif", image).should_not include(:content_encoding => "gzip")
46
+ end
47
+ end
48
+ end
49
+
50
+ end
data/spec/meta_spec.rb CHANGED
@@ -1,7 +1,3 @@
1
- require "cgi"
2
- require "mime/types"
3
- require "tempfile"
4
- require "zlib"
5
1
  require "./lib/static_sync/config"
6
2
  require "./lib/static_sync/meta"
7
3
 
@@ -13,6 +9,21 @@ describe StaticSync::Meta do
13
9
  StaticSync::Meta.new(config)
14
10
  end
15
11
 
12
+ it "sets files as public" do
13
+ Dir.chdir("spec/fixtures/site") do
14
+ subject.for("index.html").should include(:public => true)
15
+ end
16
+ end
17
+
18
+ it "sets appropriate content types for a range of common file types" do
19
+ Dir.chdir("spec/fixtures/site") do
20
+ subject.for("index.html").should include(:content_type => "text/html")
21
+ subject.for("assets/images/spinner.gif").should include(:content_type => "image/gif")
22
+ subject.for("assets/stylesheets/screen.css").should include(:content_type => "text/css")
23
+ subject.for("assets/javascripts/jquery.min.js").should include(:content_type => "application/javascript")
24
+ end
25
+ end
26
+
16
27
  describe "all files" do
17
28
 
18
29
  it "should be public" do
@@ -35,41 +46,5 @@ describe StaticSync::Meta do
35
46
  end
36
47
  end
37
48
 
38
- it "should cache files when requested" do
39
- config['cache'] = {
40
- 'css' => '86400',
41
- 'javascript' => '86400'
42
- }
43
- Dir.chdir("spec/fixtures/site") do
44
- subject.for("index.html").should_not include(
45
- :cache_control
46
- )
47
- subject.for("assets/stylesheets/screen.css").should include(
48
- :cache_control
49
- )
50
- subject.for("assets/javascripts/jquery.min.js").should include(
51
- :cache_control
52
- )
53
- end
54
- end
55
-
56
- it "should gzip files when requested" do
57
- Dir.chdir("spec/fixtures/site") do
58
- subject.for("index.html").should_not include(
59
- :content_encoding => 'gzip'
60
- )
61
- config['gzip'] = true
62
- subject.for("index.html").should include(
63
- :content_encoding => 'gzip'
64
- )
65
- subject.for("assets/stylesheets/screen.css").should include(
66
- :content_encoding => 'gzip'
67
- )
68
- subject.for("assets/javascripts/jquery.min.js").should include(
69
- :content_encoding => 'gzip'
70
- )
71
- end
72
- end
73
-
74
49
  end
75
50
  end
data/spec/storage_spec.rb CHANGED
@@ -1,9 +1,11 @@
1
- require "./lib/static_sync"
1
+ require "./lib/static_sync/config"
2
+ require "./lib/static_sync/storage"
2
3
 
3
4
  describe StaticSync::Storage do
4
5
 
5
6
  let(:config) do
6
7
  StaticSync::Config.new.merge({
8
+ 'log' => false,
7
9
  'local' => {
8
10
  'directory' => 'spec/fixtures/site'
9
11
  },
@@ -22,28 +24,39 @@ describe StaticSync::Storage do
22
24
 
23
25
  before do
24
26
  Fog.mock!
25
- config.storage.directories.create(
26
- :key => config.storage_directory,
27
- :public => true
28
- )
29
27
  end
30
28
 
31
29
  after do
32
30
  Fog::Mock.reset
33
31
  end
34
32
 
35
- describe "html files" do
33
+ context "with a remote storage directory" do
36
34
 
37
- it "are uploaded to the remote directory" do
38
- subject.sync
35
+ before do
36
+ config.storage.directories.create(
37
+ :key => config.storage_directory,
38
+ :public => true
39
+ )
40
+ end
41
+
42
+ context "syncing a new site" do
43
+
44
+ describe "#sync" do
39
45
 
40
- config.storage.directories.get(config.storage_directory).files.map(&:key).should == [
41
- "assets/javascripts/jquery.min.js",
42
- "assets/stylesheets/screen.css",
43
- "index.html"
44
- ]
46
+ it "sets a unique key for each uploaded file" do
47
+ subject.sync
48
+
49
+ config.storage.directories.get(config.storage_directory).files.map(&:key).should == [
50
+ "assets/images/spinner.gif",
51
+ "assets/javascripts/jquery.min.js",
52
+ "assets/stylesheets/screen.css",
53
+ "assets/stylesheets/screen.scss",
54
+ "cat.com/index.html",
55
+ "index.html"
56
+ ]
57
+ end
58
+ end
45
59
  end
46
60
 
47
61
  end
48
-
49
62
  end
data/static_sync.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.version = StaticSync::VERSION
9
9
  gem.authors = ["Shanon McQuay"]
10
10
  gem.email = ["shanonmcquay@gmail.com"]
11
- gem.summary = %q{Uploads your static website to cloud storage.}
11
+ gem.summary = %q{Command line tool for uploading static websites to amazon/rackspace.}
12
12
 
13
13
  gem.files = `git ls-files`.split($/)
14
14
  gem.bindir = "bin"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: static_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-18 00:00:00.000000000 Z
12
+ date: 2013-02-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -60,11 +60,18 @@ files:
60
60
  - lib/static_sync.rb
61
61
  - lib/static_sync/config.rb
62
62
  - lib/static_sync/meta.rb
63
+ - lib/static_sync/meta/caching.rb
64
+ - lib/static_sync/meta/compression.rb
63
65
  - lib/static_sync/storage.rb
64
66
  - lib/static_sync/version.rb
67
+ - spec/fixtures/site/assets/images/spinner.gif
65
68
  - spec/fixtures/site/assets/javascripts/jquery.min.js
66
69
  - spec/fixtures/site/assets/stylesheets/screen.css
70
+ - spec/fixtures/site/assets/stylesheets/screen.scss
71
+ - spec/fixtures/site/cat.com/index.html
67
72
  - spec/fixtures/site/index.html
73
+ - spec/meta/caching_spec.rb
74
+ - spec/meta/compression_spec.rb
68
75
  - spec/meta_spec.rb
69
76
  - spec/storage_spec.rb
70
77
  - static_sync.gemspec
@@ -91,8 +98,9 @@ rubyforge_project:
91
98
  rubygems_version: 1.8.23
92
99
  signing_key:
93
100
  specification_version: 3
94
- summary: Uploads your static website to cloud storage.
101
+ summary: Command line tool for uploading static websites to amazon/rackspace.
95
102
  test_files:
103
+ - spec/meta/caching_spec.rb
104
+ - spec/meta/compression_spec.rb
96
105
  - spec/meta_spec.rb
97
106
  - spec/storage_spec.rb
98
- has_rdoc: false