s3_website 2.6.1 → 2.7.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: 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