s3_website 2.6.1 → 2.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6bd443ae587cab4726f6ed2e3f11051bb62f7c4
4
- data.tar.gz: 4e7410d745fc3a59f26fdc83c16d182a0ba9396f
3
+ metadata.gz: 778dcd186e3206eaf5e03252a3926747dd8d7ba1
4
+ data.tar.gz: 74dd5fbc8caa9f73115e9fcba347301cf99eeb9d
5
5
  SHA512:
6
- metadata.gz: 6672d992af55beb7954846d8b47652d6c3918ab71616a68b54b4559a59705957572a99d87b364d15f92c81202401173608f26b35acc54f03d8711d0d60358e3e
7
- data.tar.gz: 4c2fed94893a54a957f895c3ca57d54f02de2517437a564cccf90320fb18ee7349b9dd37b84e9b1e308e67fe672d7b16f91e879cc2ab221ece55af3e947c040d
6
+ metadata.gz: 1cd34d83b87621b4cb05204c651a6af6c1fda88e2d7c0edfbfafa9d782217099650d7a3309d48c76b960328c0406f97df530861840a1e57bc10c859b17de5c23
7
+ data.tar.gz: 0f9260d64f41f661334de67f665b817245d15938abb3582443ab3f2b13915f17927a101c2c1581586196cd47203ef1e9147e54d5ef1fcf9e0ca9161268c4dbbd
data/README.md CHANGED
@@ -313,6 +313,20 @@ For more information on configuring redirects, see the documentation of the
313
313
  gem, which comes as a transitive dependency of the `s3_website` gem. (The
314
314
  command `s3_website cfg apply` internally calls the `configure-s3-website` gem.)
315
315
 
316
+ #### On skipping application of redirects
317
+
318
+ If your website has a lot of redirects, you may find the following setting
319
+ helpful:
320
+
321
+ treat_zero_length_objects_as_redirects: true
322
+
323
+ The setting allows `s3_website push` to infer whether a redirect exists or not.
324
+ You will experience faster `push` performance when this setting is `true`.
325
+ However, if this setting is enabled and you modify the `redirects` setting in
326
+ *s3_website.yml*, use `push --force` to apply the modified values.
327
+
328
+ For backward-compatibility reasons, this setting is `false` by default.
329
+
316
330
  ### Specifying custom concurrency level
317
331
 
318
332
  By default, `s3_website` does 3 operations in parallel. An operation can be an
data/changelog.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  This project uses [Semantic Versioning](http://semver.org).
4
4
 
5
+ ## 2.7.0
6
+
7
+ * Add setting `treat_zero_length_objects_as_redirects`
8
+
9
+ Before, `s3_website push` always uploaded one zero-length object for each
10
+ configured redirect. From now on, you can instruct s3\_website to treat
11
+ zero-length S3 objects as existing redirects. If you have a large amount of
12
+ redirects on your site, you may find that this feature decreases the duration
13
+ of `s3_website push`.
14
+
5
15
  ## 2.6.1
6
16
 
7
17
  * Always invalidate the object */index.html* if the setting `cloudfront_invalidate_root` is on
@@ -1,3 +1,3 @@
1
1
  module S3Website
2
- VERSION = '2.6.1'
2
+ VERSION = '2.7.0'
3
3
  end
@@ -1 +1 @@
1
- 7a47d1df31ffe3bb8093719149222afd
1
+ ddc9ee41507905b63f16ab01634e9e5a
@@ -1,11 +1,12 @@
1
1
  package s3.website
2
2
 
3
3
  import s3.website.model.Config.S3_website_yml
4
+ import s3.website.model.Redirect.{Redirects, resolveRedirects}
4
5
  import s3.website.model.Site._
5
6
  import scala.concurrent.{ExecutionContextExecutor, Future, Await}
6
7
  import scala.concurrent.duration._
7
8
  import scala.language.postfixOps
8
- import s3.website.UploadHelper.{resolveDeletes, resolveUploads}
9
+ import s3.website.UploadHelper.{FutureUploads, resolveDeletes, resolveUploads}
9
10
  import s3.website.S3._
10
11
  import scala.concurrent.ExecutionContext.fromExecutor
11
12
  import java.util.concurrent.Executors.newFixedThreadPool
@@ -80,29 +81,42 @@ object Push {
80
81
  pushOptions: PushOptions
81
82
  ): ExitCode = {
82
83
  logger.info(s"${Deploy.renderVerb} ${site.rootDirectory}/* to ${site.config.s3_bucket}")
83
- val redirects = Redirect.resolveRedirects
84
84
  val s3FilesFuture = resolveS3Files()
85
- val redirectReports: PushReports = redirects.map(S3 uploadRedirect _) map (Right(_))
86
-
87
- val pushReports: Future[PushReports] = for {
88
- errorOrUploads: Either[ErrorReport, Seq[Upload]] <- resolveUploads(s3FilesFuture)
85
+ val redirectsFuture: Redirects = resolveRedirects(s3FilesFuture)
86
+ val redirectReports: Future[Either[ErrorReport, Seq[Future[PushErrorOrSuccess]]]] =
87
+ redirectsFuture.map { (errOrRedirects: Either[ErrorReport, Seq[Redirect]]) =>
88
+ errOrRedirects.right.map(_.filter(_.needsUpload).map(S3 uploadRedirect _))
89
+ }
90
+
91
+ val uploadFutures: FutureUploads = resolveUploads(s3FilesFuture)
92
+ val uploadReports: Future[Either[ErrorReport, Seq[Future[PushErrorOrSuccess]]]] = for {
93
+ errorOrUploads: Either[ErrorReport, Seq[Upload]] <- uploadFutures
94
+ } yield errorOrUploads.right.map(_.map(S3 uploadFile _))
95
+
96
+ val deleteReports: Future[Either[ErrorReport, Seq[Future[PushErrorOrSuccess]]]] = for {
97
+ errorOrUploads: Either[ErrorReport, Seq[Upload]] <- uploadFutures
89
98
  } yield {
90
- val uploadReports: PushReports = errorOrUploads.fold(
91
- error => Left(error) :: Nil,
92
- uploads => {
93
- uploads.map(S3 uploadFile _).map(Right(_))
94
- }
95
- )
96
- val deleteReports =
97
- Await.result(resolveDeletes(s3FilesFuture, redirects), 1 day).right.map { keysToDelete =>
98
- keysToDelete map (S3 delete _)
99
- }.fold(
100
- error => Left(error) :: Nil,
101
- (pushResults: Seq[Future[PushErrorOrSuccess]]) => pushResults map (Right(_))
99
+ val errorsOrDeleteReports = redirectsFuture.flatMap { (errOrRedirects: Either[ErrorReport, Seq[Redirect]]) =>
100
+ errOrRedirects.fold(
101
+ err => Future(Left(err)),
102
+ redirects => resolveDeletes(s3FilesFuture, redirects)
102
103
  )
103
- uploadReports ++ deleteReports ++ redirectReports
104
+ }.map { (deletes: Either[ErrorReport, Seq[S3Key]]) =>
105
+ deletes.right.map(keysToDelete => keysToDelete.map(S3 delete _))
106
+ }
107
+ Await.result(errorsOrDeleteReports, 1 day)
108
+ }
109
+ val allReports = Future.sequence(redirectReports :: uploadReports :: deleteReports :: Nil) map { reports =>
110
+ reports.foldLeft(Nil: PushReports) { (memo, report: Either[ErrorReport, Seq[Future[PushErrorOrSuccess]]]) =>
111
+ report match {
112
+ case Left(err) =>
113
+ memo :+ Left(err)
114
+ case Right(pushResults: Seq[Future[PushErrorOrSuccess]]) =>
115
+ memo ++ pushResults.map(Right(_))
116
+ }
117
+ }
104
118
  }
105
- val finishedPushOps = awaitForResults(Await.result(pushReports, 1 day))
119
+ val finishedPushOps = awaitForResults(Await.result(allReports, 1 day))
106
120
  val invalidationSucceeded = invalidateCloudFrontItems(finishedPushOps)
107
121
 
108
122
  afterPushFinished(finishedPushOps, invalidationSucceeded)
@@ -23,7 +23,8 @@ case class Config(
23
23
  cloudfront_distribution_id: Option[String],
24
24
  cloudfront_invalidate_root: Option[Boolean],
25
25
  redirects: Option[Map[String, String]],
26
- concurrency_level: Int
26
+ concurrency_level: Int,
27
+ treat_zero_length_objects_as_redirects: Option[Boolean]
27
28
  )
28
29
 
29
30
  object Config {
@@ -50,6 +50,7 @@ object Site {
50
50
  cloudfront_invalidate_root <- loadOptionalBoolean("cloudfront_invalidate_root").right
51
51
  concurrency_level <- loadOptionalInt("concurrency_level").right
52
52
  redirects <- loadRedirects.right
53
+ treat_zero_length_objects_as_redirects <- loadOptionalBoolean("treat_zero_length_objects_as_redirects").right
53
54
  } yield {
54
55
  gzip_zopfli.foreach(_ => logger.info(
55
56
  """|Zopfli is not currently supported. Falling back to regular gzip.
@@ -73,7 +74,8 @@ object Site {
73
74
  cloudfront_distribution_id,
74
75
  cloudfront_invalidate_root,
75
76
  redirects,
76
- concurrency_level.fold(20)(_ max 20) // At minimum, run 20 concurrent operations
77
+ concurrency_level.fold(20)(_ max 20),
78
+ treat_zero_length_objects_as_redirects
77
79
  )
78
80
  }
79
81
  case Failure(error) =>
@@ -11,6 +11,7 @@ import s3.website.model.Upload.tika
11
11
  import s3.website.model.Encoding.encodingOnS3
12
12
  import java.io.File.createTempFile
13
13
  import org.apache.commons.io.IOUtils.copy
14
+ import scala.concurrent.{ExecutionContextExecutor, Future}
14
15
  import scala.util.Try
15
16
 
16
17
  object Encoding {
@@ -148,17 +149,39 @@ object Files {
148
149
  }
149
150
  }
150
151
 
151
- case class Redirect(s3Key: String, redirectTarget: String) {
152
+ case class Redirect(s3Key: String, redirectTarget: String, needsUpload: Boolean) {
152
153
  def uploadType = RedirectFile
153
154
  }
154
155
 
156
+ private case class RedirectSetting(source: String, target: String)
157
+
155
158
  object Redirect {
156
- def resolveRedirects(implicit config: Config): Seq[Redirect] =
157
- config.redirects.fold(Nil: Seq[Redirect]) { sourcesToTargets =>
158
- sourcesToTargets.foldLeft(Seq(): Seq[Redirect]) {
159
+ type Redirects = Future[Either[ErrorReport, Seq[Redirect]]]
160
+
161
+ def resolveRedirects(s3FileFutures: Future[Either[ErrorReport, Seq[S3File]]])
162
+ (implicit config: Config, executor: ExecutionContextExecutor, pushOptions: PushOptions): Redirects = {
163
+ val redirectSettings = config.redirects.fold(Nil: Seq[RedirectSetting]) { sourcesToTargets =>
164
+ sourcesToTargets.foldLeft(Seq(): Seq[RedirectSetting]) {
159
165
  (redirects, sourceToTarget) =>
160
- redirects :+ Redirect(sourceToTarget._1, applySlashIfNeeded(sourceToTarget._2))
166
+ redirects :+ RedirectSetting(sourceToTarget._1, applySlashIfNeeded(sourceToTarget._2))
161
167
  }
168
+ }
169
+ def redirectsWithExistsOnS3Info =
170
+ s3FileFutures.map(_.right.map { s3Files =>
171
+ val existingRedirectKeys = s3Files.filter(_.size == 0).map(_.s3Key).toSet
172
+ redirectSettings.map(redirectSetting =>
173
+ Redirect(redirectSetting, needsUpload = !existingRedirectKeys.contains(redirectSetting.source))
174
+ )
175
+ })
176
+ val uploadOnlyMissingRedirects =
177
+ config.treat_zero_length_objects_as_redirects.contains(true) && !pushOptions.force
178
+ val allConfiguredRedirects = Future(Right(redirectSettings.map(redirectSetting =>
179
+ Redirect(redirectSetting, needsUpload = true)
180
+ )))
181
+ if (uploadOnlyMissingRedirects)
182
+ redirectsWithExistsOnS3Info
183
+ else
184
+ allConfiguredRedirects
162
185
  }
163
186
 
164
187
  private def applySlashIfNeeded(redirectTarget: String) = {
@@ -169,10 +192,13 @@ object Redirect {
169
192
  else
170
193
  "/" + redirectTarget // let the user have redirect settings like "index.php: index.html" in s3_website.ml
171
194
  }
195
+
196
+ def apply(redirectSetting: RedirectSetting, needsUpload: Boolean): Redirect =
197
+ Redirect(redirectSetting.source, redirectSetting.target, needsUpload)
172
198
  }
173
199
 
174
- case class S3File(s3Key: String, md5: MD5)
200
+ case class S3File(s3Key: String, md5: MD5, size: Long)
175
201
 
176
202
  object S3File {
177
- def apply(summary: S3ObjectSummary): S3File = S3File(summary.getKey, summary.getETag)
203
+ def apply(summary: S3ObjectSummary): S3File = S3File(summary.getKey, summary.getETag, summary.getSize)
178
204
  }
@@ -35,7 +35,7 @@ class S3WebsiteSpec extends Specification {
35
35
  "update a gzipped S3 object if the contents has changed" in new BasicSetup {
36
36
  config = "gzip: true"
37
37
  setLocalFileWithContent(("styles.css", "<h1>hi again</h1>"))
38
- setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
38
+ setS3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
39
39
  push
40
40
  sentPutObjectRequest.getKey must equalTo("styles.css")
41
41
  }
@@ -43,7 +43,7 @@ class S3WebsiteSpec extends Specification {
43
43
  "not update a gzipped S3 object if the contents has not changed" in new BasicSetup {
44
44
  config = "gzip: true"
45
45
  setLocalFileWithContent(("styles.css", "<h1>hi</h1>"))
46
- setS3Files(S3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
46
+ setS3File("styles.css", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
47
47
  push
48
48
  noUploadsOccurred must beTrue
49
49
  }
@@ -59,7 +59,7 @@ class S3WebsiteSpec extends Specification {
59
59
  | - .xml
60
60
  """.stripMargin
61
61
  setLocalFileWithContent(("file.xml", "<h1>hi again</h1>"))
62
- setS3Files(S3File("file.xml", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
62
+ setS3File("file.xml", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
63
63
  push
64
64
  sentPutObjectRequest.getKey must equalTo("file.xml")
65
65
  }
@@ -68,14 +68,14 @@ class S3WebsiteSpec extends Specification {
68
68
  "push" should {
69
69
  "not upload a file if it has not changed" in new BasicSetup {
70
70
  setLocalFileWithContent(("index.html", "<div>hello</div>"))
71
- setS3Files(S3File("index.html", md5Hex("<div>hello</div>")))
71
+ setS3File("index.html", md5Hex("<div>hello</div>"))
72
72
  push
73
73
  noUploadsOccurred must beTrue
74
74
  }
75
75
 
76
76
  "update a file if it has changed" in new BasicSetup {
77
77
  setLocalFileWithContent(("index.html", "<h1>old text</h1>"))
78
- setS3Files(S3File("index.html", md5Hex("<h1>new text</h1>")))
78
+ setS3File("index.html", md5Hex("<h1>new text</h1>"))
79
79
  push
80
80
  sentPutObjectRequest.getKey must equalTo("index.html")
81
81
  }
@@ -87,7 +87,7 @@ class S3WebsiteSpec extends Specification {
87
87
  }
88
88
 
89
89
  "delete files that are on S3 but not on local file system" in new BasicSetup {
90
- setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
90
+ setS3File("old.html", md5Hex("<h1>old text</h1>"))
91
91
  push
92
92
  sentDelete must equalTo("old.html")
93
93
  }
@@ -131,14 +131,14 @@ class S3WebsiteSpec extends Specification {
131
131
  }
132
132
 
133
133
  "try again if the delete fails" in new BasicSetup {
134
- setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
134
+ setS3File("old.html", md5Hex("<h1>old text</h1>"))
135
135
  deleteFailsAndThenSucceeds(howManyFailures = 5)
136
136
  push
137
137
  verify(amazonS3Client, times(6)).deleteObject(Matchers.anyString(), Matchers.anyString())
138
138
  }
139
139
 
140
140
  "try again if the object listing fails" in new BasicSetup {
141
- setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
141
+ setS3File("old.html", md5Hex("<h1>old text</h1>"))
142
142
  objectListingFailsAndThenSucceeds(howManyFailures = 5)
143
143
  push
144
144
  verify(amazonS3Client, times(6)).listObjects(Matchers.any(classOf[ListObjectsRequest]))
@@ -292,7 +292,7 @@ class S3WebsiteSpec extends Specification {
292
292
  }
293
293
 
294
294
  "be 1 if an object listing fails" in new BasicSetup {
295
- setS3Files(S3File("old.html", md5Hex("<h1>old text</h1>")))
295
+ setS3File("old.html", md5Hex("<h1>old text</h1>"))
296
296
  objectListingFailsAndThenSucceeds(howManyFailures = 6)
297
297
  push must equalTo(1)
298
298
  }
@@ -343,13 +343,13 @@ class S3WebsiteSpec extends Specification {
343
343
  "ignore_on_server: value" should {
344
344
  "not delete the S3 objects that match the ignore value" in new BasicSetup {
345
345
  config = "ignore_on_server: logs"
346
- setS3Files(S3File("logs/log.txt", ""))
346
+ setS3File("logs/log.txt")
347
347
  push
348
348
  noDeletesOccurred must beTrue
349
349
  }
350
350
 
351
351
  "support non-US-ASCII files" in new BasicSetup {
352
- setS3Files(S3File("tags/笔记/test.html", ""))
352
+ setS3File("tags/笔记/test.html", "")
353
353
  config = "ignore_on_server: tags/笔记/test.html"
354
354
  push
355
355
  noDeletesOccurred must beTrue
@@ -361,7 +361,7 @@ class S3WebsiteSpec extends Specification {
361
361
  config = s"""
362
362
  |ignore_on_server: $DELETE_NOTHING_MAGIC_WORD
363
363
  """.stripMargin
364
- setS3Files(S3File("file.txt", ""))
364
+ setS3File("file.txt")
365
365
  push
366
366
  noDeletesOccurred
367
367
  }
@@ -377,13 +377,13 @@ class S3WebsiteSpec extends Specification {
377
377
  |ignore_on_server:
378
378
  | - .*txt
379
379
  """.stripMargin
380
- setS3Files(S3File("logs/log.txt", ""))
380
+ setS3File("logs/log.txt", "")
381
381
  push
382
382
  noDeletesOccurred must beTrue
383
383
  }
384
384
 
385
385
  "support non-US-ASCII files" in new BasicSetup {
386
- setS3Files(S3File("tags/笔记/test.html", ""))
386
+ setS3File("tags/笔记/test.html", "")
387
387
  config = """
388
388
  |ignore_on_server:
389
389
  | - tags/笔记/test.html
@@ -570,7 +570,7 @@ class S3WebsiteSpec extends Specification {
570
570
  | index.php: /index.html
571
571
  """.stripMargin
572
572
  setLocalFile("index.php")
573
- setS3Files(S3File("index.php", "md5"))
573
+ setS3File("index.php", "md5")
574
574
  push
575
575
  noDeletesOccurred must beTrue
576
576
  }
@@ -625,7 +625,7 @@ class S3WebsiteSpec extends Specification {
625
625
  "push --force" should {
626
626
  "push all the files whether they have changed or not" in new ForcePush {
627
627
  setLocalFileWithContent(("index.html", "<h1>hi</h1>"))
628
- setS3Files(S3File("index.html", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */))
628
+ setS3File("index.html", "1c5117e5839ad8fc00ce3c41296255a1" /* md5 of the gzip of the file contents */)
629
629
  push
630
630
  sentPutObjectRequest.getKey must equalTo("index.html")
631
631
  }
@@ -634,7 +634,7 @@ class S3WebsiteSpec extends Specification {
634
634
  "dry run" should {
635
635
  "not push updates" in new DryRun {
636
636
  setLocalFileWithContent(("index.html", "<div>new</div>"))
637
- setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
637
+ setS3File("index.html", md5Hex("<div>old</div>"))
638
638
  push
639
639
  noUploadsOccurred must beTrue
640
640
  }
@@ -650,7 +650,7 @@ class S3WebsiteSpec extends Specification {
650
650
  }
651
651
 
652
652
  "not push deletes" in new DryRun {
653
- setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
653
+ setS3File("index.html", md5Hex("<div>old</div>"))
654
654
  push
655
655
  noUploadsOccurred must beTrue
656
656
  }
@@ -663,7 +663,7 @@ class S3WebsiteSpec extends Specification {
663
663
 
664
664
  "not invalidate files" in new DryRun {
665
665
  config = "cloudfront_invalidation_id: AABBCC"
666
- setS3Files(S3File("index.html", md5Hex("<div>old</div>")))
666
+ setS3File("index.html", md5Hex("<div>old</div>"))
667
667
  push
668
668
  noInvalidationsOccurred must beTrue
669
669
  }
@@ -684,6 +684,55 @@ class S3WebsiteSpec extends Specification {
684
684
  sentPutObjectRequests.length must equalTo(1)
685
685
  }
686
686
  }
687
+
688
+ "the setting treat_zero_length_objects_as_redirects" should {
689
+ "skip application of redirect on a zero-length S3 object" in new BasicSetup {
690
+ config =
691
+ """
692
+ |treat_zero_length_objects_as_redirects: true
693
+ |redirects:
694
+ | index.php: /index.html
695
+ """.stripMargin
696
+ setRedirectObject("index.php")
697
+ push
698
+ noUploadsOccurred
699
+ }
700
+
701
+ "not delete the redirect objects on the bucket" in new BasicSetup {
702
+ config =
703
+ """
704
+ |treat_zero_length_objects_as_redirects: true
705
+ |redirects:
706
+ | index.php: /index.html
707
+ """.stripMargin
708
+ setRedirectObject("index.php")
709
+ push
710
+ noDeletesOccurred
711
+ }
712
+
713
+ "apply redirects that are missing from S3" in new BasicSetup {
714
+ config =
715
+ """
716
+ |treat_zero_length_objects_as_redirects: true
717
+ |redirects:
718
+ | index.php: /index.html
719
+ """.stripMargin
720
+ push
721
+ sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
722
+ }
723
+
724
+ "apply all redirects when the user invokes `push --force`" in new ForcePush {
725
+ config =
726
+ """
727
+ |treat_zero_length_objects_as_redirects: true
728
+ |redirects:
729
+ | index.php: /index.html
730
+ """.stripMargin
731
+ setRedirectObject("index.php")
732
+ push
733
+ sentPutObjectRequest.getRedirectLocation must equalTo("/index.html")
734
+ }
735
+ }
687
736
 
688
737
  trait BasicSetup extends SiteLocationFromCliArg with EmptySite with MockAWS with DefaultRunMode
689
738
 
@@ -768,22 +817,28 @@ class S3WebsiteSpec extends Specification {
768
817
  val s3ObjectListing = new ObjectListing
769
818
  when(amazonS3Client.listObjects(Matchers.any(classOf[ListObjectsRequest]))).thenReturn(s3ObjectListing)
770
819
 
820
+ // Simulate the situation where the file on S3 is outdated (as compared to the local file)
771
821
  def setOutdatedS3Keys(s3Keys: String*) {
772
- s3Keys
773
- .map(key =>
774
- S3File(key, md5Hex(Random.nextLong().toString)) // Simulate the situation where the file on S3 is outdated (as compared to the local file)
775
- )
776
- .foreach (setS3Files(_))
822
+ s3Keys.map(setS3File(_))
823
+ }
824
+
825
+ def setRedirectObject(s3Key: String) {
826
+ setS3File(s3Key, size = 0)
827
+ }
828
+
829
+ def setS3File(s3Key: String, md5: String = "", size: Int = 10) {
830
+ s3ObjectListing.getObjectSummaries.add({
831
+ val summary = new S3ObjectSummary
832
+ summary.setETag(md5)
833
+ summary.setKey(s3Key)
834
+ summary.setSize(size)
835
+ summary
836
+ })
777
837
  }
778
838
 
779
839
  def setS3Files(s3Files: S3File*) {
780
840
  s3Files.foreach { s3File =>
781
- s3ObjectListing.getObjectSummaries.add({
782
- val summary = new S3ObjectSummary
783
- summary.setETag(s3File.md5)
784
- summary.setKey(s3File.s3Key)
785
- summary
786
- })
841
+ setS3File(s3File.s3Key, s3File.md5)
787
842
  }
788
843
  }
789
844
 
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.6.1
4
+ version: 2.7.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: 2014-11-05 00:00:00.000000000 Z
11
+ date: 2014-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor