s3_website 2.9.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee583948eaac40a396bdd7dcce3ac0f17b026b53
4
- data.tar.gz: fb3b7ff28372c6844d2c637f59b8fbc1a08a112e
3
+ metadata.gz: a869e0bbb5276a123020d7ae96f52d729d56b166
4
+ data.tar.gz: b6b51944723aaf3f1c22f949a56261137cc74dcc
5
5
  SHA512:
6
- metadata.gz: a06324e37ab33faa10ca761537336020dc2c712b980facfad0aece7fc3c010fafb1c2782c4878a5b566a85d3f5ca357756abfe9f1207847f3cd1b93ec7b23336
7
- data.tar.gz: b7db3637d2582366540f0938f8fe3608340b1b74ff6ba308db40da17f794061fd382dedcdde8af090ac1713f182e27c2c5161bd8c7333dd25c993e3a025556d4
6
+ metadata.gz: 2f3705e5c621c1b44225e8b3f7070496d59d258780f268b3d4183d709e5d988041d26d47a9a8bffdaac89d6f86c5b8e3caff99afafd18d1cfaa1f3cceb7ea99b
7
+ data.tar.gz: d13a8bd0492c4cc10e92bc4bd08ecf71eded8f0bac47ffa7bee15b9bd4909de2dcae7ce3e92d80aead594fcbe0eb13d747dc0fdf11517beb4f5b62397a5351f9
data/README.md CHANGED
@@ -126,6 +126,15 @@ Here's an example:
126
126
  cache_control: public, no-transform, max-age=1200, s-maxage=1200
127
127
  ```
128
128
 
129
+ You can also specify a hash of globs, and all files matching those globs will have
130
+ the specified cache-control string:
131
+
132
+ ```yaml
133
+ cache_control:
134
+ "assets/*": public, max-age=3600
135
+ "*": no-cache, no-store
136
+ ```
137
+
129
138
  After changing the `cache_control` setting, push with the `--force` option.
130
139
  Force-pushing allows you to update the S3 object metadata of existing files.
131
140
 
data/changelog.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  This project uses [Semantic Versioning](http://semver.org).
4
4
 
5
+ ## 2.10.0
6
+
7
+ * Support glob hashes in `cache_control`
8
+
5
9
  ## 2.9.0
6
10
 
7
11
  * Add setting `cache_control`
@@ -1,3 +1,3 @@
1
1
  module S3Website
2
- VERSION = '2.9.0'
2
+ VERSION = '2.10.0'
3
3
  end
@@ -72,7 +72,7 @@ object S3 {
72
72
  md setContentLength uploadFile.length
73
73
  md setContentType contentType
74
74
  upload.encodingOnS3.map(_ => "gzip") foreach md.setContentEncoding
75
- val cacheControl: Option[String] = (upload.maxAge, config.cache_control) match {
75
+ val cacheControl: Option[String] = (upload.maxAge, upload.cacheControl) match {
76
76
  case (maxAge: Some[Int], cacheCtrl: Some[String]) =>
77
77
  logger.warn("Overriding the max_age setting with the cache_control setting")
78
78
  cacheCtrl
@@ -15,7 +15,7 @@ case class Config(
15
15
  s3_endpoint: S3Endpoint,
16
16
  site: Option[String],
17
17
  max_age: Option[Either[Int, Map[String, Int]]],
18
- cache_control: Option[String],
18
+ cache_control: Option[Either[String, Map[String, String]]],
19
19
  gzip: Option[Either[Boolean, Seq[String]]],
20
20
  gzip_zopfli: Option[Boolean],
21
21
  ignore_on_server: Option[Either[String, Seq[String]]],
@@ -83,6 +83,20 @@ object Config {
83
83
  yamlValue getOrElse Left(ErrorReport(s"The key $key has to have an int or (string -> int) value"))
84
84
  }
85
85
 
86
+ def loadCacheControl(implicit unsafeYaml: UnsafeYaml): Either[ErrorReport, Option[Either[String, Map[String, String]]]] = {
87
+ val key = "cache_control"
88
+ val yamlValue = for {
89
+ cacheControlOption <- loadOptionalValue(key)
90
+ } yield {
91
+ Right(cacheControlOption.map {
92
+ case cacheControl if cacheControl.isInstanceOf[String] => Left(cacheControl.asInstanceOf[String])
93
+ case cacheControl if cacheControl.isInstanceOf[java.util.Map[_,_]] => Right(cacheControl.asInstanceOf[java.util.Map[String,String]].toMap) // TODO an unsafe call to asInstanceOf
94
+ })
95
+ }
96
+
97
+ yamlValue getOrElse Left(ErrorReport(s"The key $key has to have a string or (string -> string) value"))
98
+ }
99
+
86
100
  def loadEndpoint(implicit unsafeYaml: UnsafeYaml): Either[ErrorReport, Option[S3Endpoint]] =
87
101
  loadOptionalString("s3_endpoint").right flatMap { endpointString =>
88
102
  endpointString.map(S3Endpoint.forString) match {
@@ -40,7 +40,7 @@ object Site {
40
40
  s3_endpoint <- loadEndpoint.right
41
41
  site <- loadOptionalString("site").right
42
42
  max_age <- loadMaxAge.right
43
- cache_control <- loadOptionalString("cache_control").right
43
+ cache_control <- loadCacheControl.right
44
44
  gzip <- loadOptionalBooleanOrStringSeq("gzip").right
45
45
  gzip_zopfli <- loadOptionalBoolean("gzip_zopfli").right
46
46
  extensionless_mime_type <- loadOptionalString("extensionless_mime_type").right
@@ -94,6 +94,29 @@ case class Upload(originalFile: File, uploadType: UploadType)(implicit site: Sit
94
94
  }
95
95
  }
96
96
 
97
+ lazy val cacheControl: Option[String] = {
98
+ type GlobsMap = Map[String, String]
99
+ site.config.cache_control.flatMap { (intOrGlobs: Either[String, GlobsMap]) =>
100
+ type GlobsSeq = Seq[(String, String)]
101
+ def respectMostSpecific(globs: GlobsMap): GlobsSeq = globs.toSeq.sortBy(_._1.length).reverse
102
+ intOrGlobs
103
+ .right.map(respectMostSpecific)
104
+ .fold(
105
+ (cacheCtrl: String) => Some(cacheCtrl),
106
+ (globs: GlobsSeq) => {
107
+ val matchingCacheControl = (glob: String, cacheControl: String) =>
108
+ rubyRuntime.evalScriptlet(
109
+ s"""|# encoding: utf-8
110
+ |File.fnmatch('$glob', "$s3Key")""".stripMargin)
111
+ .toJava(classOf[Boolean])
112
+ .asInstanceOf[Boolean]
113
+ val fileGlobMatch = globs find Function.tupled(matchingCacheControl)
114
+ fileGlobMatch map (_._2)
115
+ }
116
+ )
117
+ }
118
+ }
119
+
97
120
  /**
98
121
  * May throw an exception, so remember to call this in a Try or Future monad
99
122
  */
@@ -507,6 +507,56 @@ class S3WebsiteSpec extends Specification {
507
507
  ))
508
508
  logEntries must contain("[\u001B[33mwarn\u001B[0m] Overriding the max_age setting with the cache_control settin")
509
509
  }
510
+
511
+ "supports all valid URI characters in the glob setting" in new BasicSetup {
512
+ config = """
513
+ |cache_control:
514
+ | "*.html": public, max-age=120
515
+ """.stripMargin
516
+ val allValidUrlCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=" // See http://stackoverflow.com/a/1547940/990356 for discussion
517
+ setLocalFile(s"$allValidUrlCharacters.html")
518
+ push()
519
+ sentPutObjectRequest.getMetadata.getCacheControl must equalTo("public, max-age=120")
520
+ }
521
+
522
+ "be applied to files that match the glob" in new BasicSetup {
523
+ config = """
524
+ |cache_control:
525
+ | "*.html": no-store
526
+ """.stripMargin
527
+ setLocalFile("index.html")
528
+ push()
529
+ sentPutObjectRequest.getMetadata.getCacheControl must equalTo("no-store")
530
+ }
531
+
532
+ "be applied to directories that match the glob" in new BasicSetup {
533
+ config = """
534
+ |cache_control:
535
+ | "assets/**/*.js": no-cache, no-store
536
+ """.stripMargin
537
+ setLocalFile("assets/lib/jquery.js")
538
+ push()
539
+ sentPutObjectRequest.getMetadata.getCacheControl must equalTo("no-cache, no-store")
540
+ }
541
+
542
+ "not be applied if the glob doesn't match" in new BasicSetup {
543
+ config = """
544
+ |cache_control:
545
+ | "*.js": max-age=120
546
+ """.stripMargin
547
+ setLocalFile("index.html")
548
+ push()
549
+ sentPutObjectRequest.getMetadata.getCacheControl must beNull
550
+ }
551
+
552
+ "support non-US-ASCII directory names" in new BasicSetup {
553
+ config = """
554
+ |cache_control:
555
+ | "*": no-cache
556
+ """.stripMargin
557
+ setLocalFile("tags/笔记/index.html")
558
+ push() must equalTo(0)
559
+ }
510
560
  }
511
561
 
512
562
  "cache control" can {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3_website
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lauri Lehmijoki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-03 00:00:00.000000000 Z
11
+ date: 2015-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor