# What's in `PeanutButter.Utils`, exactly? `PeanutButter.Utils` is a package which pretty-much evolved as I had common problems that I was solving day-to-day. People joining a team that I was working on would be exposed to bits of it and, like a virus, those bits would propagate across other code-bases. Some people asked for documentation, which I answered with a middle-ground of `xmldoc`, which most agreed was good enough. People around me got to know of the more useful bits in `PeanutButter.Utils` or would ask me questions like "Does PeanutButter.Utils have something which can do [X]?". I kind of took the ubiquity amongst my team-mates for granted. Fast-forward a little bit, and I've moved on to another company, where people don't know anything about the time-savers in `PeanutButter.Utils` -- and it occurs to me that that statement probably applies to pretty-much most people -- so I thought it might be worthwhile to have some kind of primer on what you can expect to find in there. An introduction, if you will. I think there's enough to break the content down into sections, so we can start with: ## Disposables One of the patterns I like most in the .net world is that of `IDisposable`. It's a neat way to ensure that something happens at the end of a block of code irrespective of what goes on _inside_ that code. The code could throw or return early -- it doesn't matter: whatever happens in the `Dispose` method of the `IDisposable` declared at the top of a `using` block will be run. Usually, we use this for clearing up managed resources (eg on database connections), but it struck me that there were some other convenient places to use it. Most generically, if you wanted to run something simple at the start of a block of code and run something else at the end (think of toggling something on for the duration of a block of code), you could use the convienient `AutoResetter` class: ```csharp using (new AutoResetter( () => ToggleFeatureOn(), () => ToggleFeatureOff())) { // code inside here has the feature toggled on } // code over here doesn't -- and the feature is // toggled back off again even if the code // above throws an exception. ``` It's very simple -- but it means that you can get the functionality of an `IDisposable` by writing two little lambda methods. You can also have a variant where the result from the first lambda is fed into the second: ```csharp using (new AutoResetter( () => GetCounterAndResetToZero(), originalCount => ResetCounterTo(originalCount))) { // counter is zero here } // counter is reset to original value here ``` Cool. Other common problems that can be solved with `IDisposable` are: ### Ensuring mutexes / semaphores are reset, even if an exception is encountered For this, we can use `AutoLocker`: ```csharp using (new AutoLocker(someMutex)) { } using (new AutoLocker(someSemaphore)) { } using (new AutoLocker(someSemaphoreLite)) { } ``` ### Temporary files in tests ```csharp using (var tempFile = new AutoTempFile()) { File.WriteAllBytes( Encoding.UTF8.GetBytes("moo, said the cow"), tempFile.Path ); // we can run testing code against the file here } // file is gone here, like magick! ``` This uses the `Path.GetTempFileName()` system call by default -- so you don't have to care about where the file actually exists. Of course, there are constructor overloads to: - create the file populated with data (string or bytes) - create the file in a different location (not the system temp data location) - create the file with a specific name `AutoTempFile` also exposes the files contents via properties: - `StringData` for string contents - `BinaryData` for a byte[] array There is also an `AutoTempFolder` if you want a scratch area to work in for a period of time. When it is disposed, it and all it's contents are deleted. Similarly, `AutoDeleter` is an `IDisposable` which can keep track of multiple files you'd like to delete when it is disposed: ```csharp using (var deleter = new AutoDeleter()) { // some files are created, then we can do: deleter.Add("C:\Some\File"); deleter.Add("C:\Some\Other\File"); } // and here, those files are deleted. If they can't be // deleted, (eg they are locked by some process), // then the error is quietly suppressed. // `AutoDeleter` works for folders too. ``` ### Other disposables As much as I love the `using` pattern, it can lead to some "arrow code", like this venerable ADO.NET code: ```csharp using (var conn = CreateDbConnection()) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "select * from users"; using (var reader = cmd.ExecuteReader()) { // read from the reader here } } } ``` Sure, many people don't use ADO.NET "raw" like this any more -- it's just an easy example which comes to mind. I've seen far worse "nest-denting" of `using` blocks too. This can be flattened out a bit with `AutoDisposer`: ```csharp using (var disposer = new AutoDisposer()) { var conn = disposer.Add(CreateDbConnection()); conn.Open(); var cmd = disposer.Add(conn.CreateCommand()); cmd.CommandText = "select * from users"; var reader = disposer.Add(conn.ExecuteReader()); // read from the db here } // reader is disposed // cmd is disposed // conn is disposed ``` `AutoDisposer` disposes of items in reverse-order in case of any disposing dependencies. So that's part 1 of "What's in `PeanutButter.Utils`?". There are other interesting bits, like: - extension methods to - make some operations more convenient - do conversions - working with `Stream` objects - DateTime utilities - facilitate more functional code (eg `.ForEach` for collections) - `SelectAsync` and `WhereAsync` let you use async lambdas in your LINQ - test and manipulate strings - the `DeepEqualityTester`, which is at the heart of `NExpect`s `.Deep` and `.Intersection` equality testing - `MemberExpression` helpers - Reflection tidbits - reading and writing arbitrary metadata for any object you encounter (think like adding property data to any object) - Some pythonic methods (`Range` and a (imo) more useful `Zip` than the one bundled in LINQ) - dictionaries - `DictionaryWrappingObject` lets you treat any object like you would in Javascript, with text property indexes - `DefaultDictionary` returns default values for unknown keys - `MergeDictionary` allows layering multiple dictionaries into one "view" - `CaseWarpingDictionary` provides a decorator dictionary for when the dictionary you have does indexing with inconvenient case rules I hope to tackle these in individual posts (:
Thursday, 12 April 2018
Subscribe to:
Post Comments (Atom)
What's new in PeanutButter?
Retrieving the post... Please hold. If the post doesn't load properly, you can check it out here: https://github.com/fluffynuts/blog/...
-
So here's a neat little thing that I learned tonight: how to run a script after a specific package is installed or updated. Why? Bec...
-
There was a time when INI files ruled the world of configuration. Since then, we've been told on numerous occasions by many people that ...
-
Introducing... Peanut Butter! I've been writing code for about 15 years now. It's not really that long, considering the amount of...
No comments:
Post a Comment