FluentPath 1.0

(c) Bertrand Le Roy 2010Last Sunday, I published version 1.0 of my little FluentPath library.

This library, which is a fluent wrapper around System.IO, started as a little experiment / code sample and has been met with some enthusiasm from some of you so I spent quite a bit of time over the last few months polishing it and trying to raise it to a level of quality where it can safely be used in real applications (although my lawyer is telling me that it’s still at your own risk, as specified in the license).

I’ve started using it myself on a few projects and it’s now my default way of accessing the file system.

So what’s in there and why should you use it?

It’s small!

36kB dll, no dependencies.

It’s a NuGet!

You can now install FluentPath with NuGet:Installing FluentPath with NuGet

It’s robust!

Since the first release of FluentPath, I’ve spent the most time on writing unit and functional tests for the library. At 143 tests and more than 80% coverage, I’m pretty confident that this can be safely used in real projects (even if my lawyer isn’t).

Kind of.

Well, it’s still really designed for the manipulation of relatively light files: it does not always use buffered streams and doesn’t do asynchrony. I think that’s all right for most uses but if you’re in a case where you can’t afford that, just continue to use System.IO instead.

Of course, if someone wants to contribute patches, that’s fine too.

It’s fun!

Consider the following piece of code:

Path.Current
    .Files(
        p => new[] {
            ".avi", ".m4v", ".wmv",
            ".mp4", ".dvr-ms", ".mpg", ".mkv"
        }.Contains(p.Extension))
    .CreateDirectories(
        p => p.Parent()
              .Combine(p.FileNameWithoutExtension))
    .End()
    .Move(
        p => p.Parent()
              .Combine(p.FileNameWithoutExtension)
              .Combine(p.FileName));

This selects all video files in the current directory and then moves them into their own directory. So for example if you have a WingsOfDesire.avi file, it will create a WingsOfDesire folder and move the file in there. And it will do so for each of the files in there.

It is left as an exercise to the reader to write the same code with just System.IO and to compare with this.

It’s extensible!

Now that’s nice but there is so much more you can do with files. Zipping and unzipping for example. There are good libraries for that, such as SharpZipLib. But SharpZipLib is rather low-level and it’s not fluent. Not that everything has to be, but wouldn’t it be neat to have a fluent API for zipping and unzipping, one that integrates well with FluentPath?

Well, as an experiment, I built exactly that around SharpZipLib, enabling that sort of thing:

Path.Current
    .Files("*.txt", true)
    .Zip((Path) "textfiles.zip", p => (Path) p.FileName);

Those extra methods are just extension methods for Fluent.IO.Path that can be imported by referencing the new Fluent.Zip.dll assembly and using the Fluent.Zip namespace. For example, here’s the code for that particular Zip overload:

/// <summary>
/// Zips all files in the path to the target.
/// </summary>
/// <param name="path">The files to compress.</param>
/// <param name="target">
/// The path of the target zip file.
/// If target has more than one file, only the first
/// one is used.
</param> /// <param name="fileSystemToZip"> /// A function that maps the paths of the files and
/// directories to zip into relative paths inside the zip.
/// </param> /// <returns>The zipped path.</returns> public static Path Zip(this Path path, Path target,
Func<Path, Path> fileSystemToZip) {
var files = path.AllFiles() .ToDictionary( fileSystemToZip, p => p); Zip(target, new Path(files.Keys), p => files[p].ReadBytes()); return target; }

This shows how it’s very easy to add your own extensions to FluentPath and for example capture in an easy method a specific process that you happen to repeat a lot in your code.

What’s new?

The main change in this new version is that instead of having one Path class to represent a single path, and a PathCollection class to represent a collection of Path objects, I’ve coalesced both classes, jQuery-style and now there is only Path, which behaves both like a single path and like a collection of paths. This makes it really natural to work with sets as if they were single items, which is where fluent APIs really shine.

Another important and breaking change is that Path and string are no longer implicitly cast to each other. Now you need to be explicit about switching from string to Path and back. It’s a little less convenient but the previous ambiguity actually wasn’t sustainable. This way, things are a lot clearer.

And of course, I’ve added lots of nice methods such as Grep. And lots and lots of bug fixes, which is what happens when you start writing tests…

What’s next?

The next big change I want to bring to FluentPath is an additional layer of abstraction in the form of a pluggable file system. This should enable you to test code that is using FluentPath without having to physically write to the file system but instead to write to a stub or a mock. Potentially, it also opens a few interesting scenarios where you manipulate a file-system-like structure using these same APIs and switch from one abstraction to the other without code modifications.

I also want smaller changes such as exposing all file attributes and metadata directly on Path, or change monitoring.

Where is it?

Some links and previous posts about FluentPath:

The original post:
http://weblogs.asp.net/bleroy/archive/2010/03/10/fluentpath-a-fluent-wrapper-around-system-io.aspx

Writing the tests:
http://weblogs.asp.net/bleroy/archive/2010/05/28/writing-the-tests-for-fluentpath.aspx

The project:
http://fluentpath.codeplex.com/

8 Comments

Comments have been disabled for this content.