Skip to content

Commit

Permalink
File.usingTemp util
Browse files Browse the repository at this point in the history
  • Loading branch information
pathikrit committed Feb 2, 2017
1 parent 6f95055 commit d3522e8
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## v3.0.0

* [File.usingTemp]()
* [Optional symbolic operations](https://github.com/pathikrit/better-files/issues/102)
* [PR #100](https://github.com/pathikrit/better-files/pull/100): Fix issue in unzip of parents
* [PR #101](https://github.com/pathikrit/better-files/pull/101): Removed File.Type
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
0. [Java compatibility](#java-interoperability)
0. [Globbing](#globbing)
0. [File system operations](#file-system-operations)
0. [Temporary files](#temporary-files)
0. [UNIX DSL](#unix-dsl)
0. [File attributes](#file-attributes)
0. [File comparison](#file-comparison)
Expand Down Expand Up @@ -270,10 +271,29 @@ file.setGroup(group: String) // chgrp group file
Seq(file1, file2) `>:` file3 // same as cat file1 file2 > file3 (must import import better.files.Dsl.SymbolicOperations)
Seq(file1, file2) >>: file3 // same as cat file1 file2 >> file3 (must import import better.files.Dsl.SymbolicOperations)
file.isReadLocked; file.isWriteLocked; file.isLocked
File.newTemporaryDirectory() / File.newTemporaryFile() // create temp dir/file
File.numberOfOpenFileDescriptors // number of open file descriptors
```

### Temporary files
Utils to create temporary files:
```scala
File.newTemporaryDirectory()
File.newTemporaryFile()
```
The above APIs allow optional specifications of `prefix`, `suffix` and `parentDir`.
These files are [not deleted automatically on exit by the JVM](http://stackoverflow.com/questions/16691437/when-are-java-temporary-files-deleted) (you have to set `deleteOnExit` which adds to `shutdownHook`).

A cleaner alternative is to use self-deleting file contexts which deletes the file immediately when done:
```scala
File.usingTempFile() {tempFile =>
...
}

// or equivalently:

File.newTempFile().applyAndDelete(tempFile => ...)
```

### UNIX DSL
All the above can also be expressed using [methods](http://pathikrit.github.io/better-files/latest/api/better/files/Dsl$.html) reminiscent of the command line:
```scala
Expand Down
25 changes: 25 additions & 0 deletions core/src/main/scala/better/files/File.scala
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ class File private(val path: Path) {
this
}

def deleteOnExit(): this.type = {
toJava.deleteOnExit()
this
}

override def hashCode =
path.hashCode()

Expand Down Expand Up @@ -828,6 +833,20 @@ class File private(val path: Path) {
def unzip(zipFilter: ZipEntry => Boolean = _ => true)(implicit codec: Codec): File =
unzipTo(destination = File.newTemporaryDirectory(name), zipFilter)(codec)

/**
* Applies the given function on this and then deletes this file
*
* @param f
* @tparam U
* @return
*/
def applyAndDelete[U](f: File => U): U =
try {
f(this)
} finally {
val _ = delete(swallowIOExceptions = true)
}

//TODO: add features from https://github.com/sbt/io
}

Expand All @@ -839,13 +858,19 @@ object File {
}
}

def usingTemporaryDirectory[U](prefix: String = "", parent: Option[File] = None, attributes: Attributes = Attributes.default)(f: File => U): U =
newTemporaryDirectory(prefix, parent)(attributes).applyAndDelete(f)

def newTemporaryFile(prefix: String = "", suffix: String = "", parent: Option[File] = None)(implicit attributes: Attributes = Attributes.default): File = {
parent match {
case Some(dir) => Files.createTempFile(dir.path, prefix, suffix, attributes: _*)
case _ => Files.createTempFile(prefix, suffix, attributes: _*)
}
}

def usingTemporaryFile[U](prefix: String = "", suffix: String = "", parent: Option[File] = None, attributes: Attributes = Attributes.default)(f: File => U): U =
newTemporaryFile(prefix, suffix, parent)(attributes).applyAndDelete(f)

implicit def apply(path: Path): File =
new File(path.toAbsolutePath.normalize())

Expand Down
21 changes: 11 additions & 10 deletions core/src/test/scala/better/files/FileSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,17 @@ class FileSpec extends FlatSpec with BeforeAndAfterEach with Matchers {
}

it should "detect file locks" in {
val file = File.newTemporaryFile()
def lockInfo() = file.isReadLocked() -> file.isWriteLocked()
// TODO: Why is file.isReadLocked() should be false?
lockInfo() shouldBe (true -> false)
val channel = file.newRandomAccess(File.RandomAccessMode.readWrite).getChannel
val lock = channel.tryLock()
lockInfo() shouldBe (true -> true)
lock.release()
channel.close()
lockInfo() shouldBe (true -> false)
File.usingTemporaryFile() {file =>
def lockInfo() = file.isReadLocked() -> file.isWriteLocked()
// TODO: Why is file.isReadLocked() should be false?
lockInfo() shouldBe (true -> false)
val channel = file.newRandomAccess(File.RandomAccessMode.readWrite).getChannel
val lock = channel.tryLock()
lockInfo() shouldBe (true -> true)
lock.release()
channel.close()
lockInfo() shouldBe (true -> false)
}
}

it should "support ln/cp/mv" in {
Expand Down

0 comments on commit d3522e8

Please sign in to comment.