-
Notifications
You must be signed in to change notification settings - Fork 965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #263 Add huminization of collections #268
Changes from all commits
6e577b7
947e354
f7db30d
b662661
27583dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,8 +63,17 @@ public class CasingExtensions | |
public string ApplyCase(string input, Humanizer.LetterCasing casing) { } | ||
} | ||
|
||
public class CollectionHumanizeExtensions | ||
{ | ||
public string Humanize(System.Collections.Generic.IEnumerable<> collection) { } | ||
public string Humanize(System.Collections.Generic.IEnumerable<> collection, System.Func<, > displayFormatter) { } | ||
public string Humanize(System.Collections.Generic.IEnumerable<> collection, string separator) { } | ||
public string Humanize(System.Collections.Generic.IEnumerable<> collection, System.Func<, > displayFormatter, string separator) { } | ||
} | ||
|
||
public class Configurator | ||
{ | ||
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.CollectionFormatters.ICollectionFormatter> CollectionFormatters { get; } | ||
public Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; } | ||
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.Formatters.IFormatter> Formatters { get; } | ||
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.NumberToWords.INumberToWordsConverter> NumberToWordsConverters { get; } | ||
|
@@ -194,6 +203,14 @@ public enum LetterCasing | |
value__, | ||
} | ||
|
||
public interface ICollectionFormatter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the API |
||
{ | ||
string Humanize(System.Collections.Generic.IEnumerable<> collection); | ||
string Humanize(System.Collections.Generic.IEnumerable<> collection, System.Func<, > objectFormatter); | ||
string Humanize(System.Collections.Generic.IEnumerable<> collection, string separator); | ||
string Humanize(System.Collections.Generic.IEnumerable<> collection, System.Func<, > objectFormatter, string separator); | ||
} | ||
|
||
public class DefaultFormatter | ||
{ | ||
public DefaultFormatter() { } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
using System.Collections.Generic; | ||
using Xunit; | ||
|
||
namespace Humanizer.Tests | ||
{ | ||
public class SomeClass | ||
{ | ||
public string SomeString; | ||
public int SomeInt; | ||
public override string ToString() | ||
{ | ||
return "ToString"; | ||
} | ||
} | ||
|
||
public class CollectionHumanizeTests : AmbientCulture | ||
{ | ||
public CollectionHumanizeTests() : base("en") { } | ||
|
||
private readonly List<SomeClass> _testCollection = new List<SomeClass> | ||
{ | ||
new SomeClass | ||
{ | ||
SomeInt = 1, | ||
SomeString = "One" | ||
}, | ||
new SomeClass | ||
{ | ||
SomeInt = 2, | ||
SomeString = "Two" | ||
}, | ||
new SomeClass | ||
{ | ||
SomeInt = 3, | ||
SomeString = "Three" | ||
} | ||
}; | ||
|
||
[Fact] | ||
public void HumanizeReturnsOnlyNameWhenCollectionContainsOneItem() | ||
{ | ||
var collection = new List<string> { "A String" }; | ||
|
||
Assert.Equal("A String", collection.Humanize()); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeUsesSeparatorWhenMoreThanOneItemIsInCollection() | ||
{ | ||
var collection = new List<string> | ||
{ | ||
"A String", | ||
"Another String", | ||
}; | ||
|
||
Assert.Equal("A String or Another String", collection.Humanize("or")); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeDefaultsSeparatorToAnd() | ||
{ | ||
var collection = new List<string> | ||
{ | ||
"A String", | ||
"Another String", | ||
}; | ||
|
||
Assert.Equal("A String and Another String", collection.Humanize()); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeUsesOxfordComma() | ||
{ | ||
var collection = new List<string> | ||
{ | ||
"A String", | ||
"Another String", | ||
"A Third String", | ||
}; | ||
|
||
Assert.Equal("A String, Another String, or A Third String", collection.Humanize("or")); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeDefaultsToToString() | ||
{ | ||
|
||
Assert.Equal("ToString, ToString, or ToString", _testCollection.Humanize("or")); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeUsesObjectFormatter() | ||
{ | ||
|
||
Assert.Equal("SomeObject #1 - One, SomeObject #2 - Two, and SomeObject #3 - Three", | ||
_testCollection.Humanize(sc => string.Format("SomeObject #{0} - {1}", sc.SomeInt, sc.SomeString))); | ||
} | ||
|
||
[Fact] | ||
public void HumanizeUsesObjectFormatterWhenSeparatorIsProvided() | ||
{ | ||
|
||
Assert.Equal("SomeObject #1 - One, SomeObject #2 - Two, or SomeObject #3 - Three", | ||
_testCollection.Humanize(sc => string.Format("SomeObject #{0} - {1}", sc.SomeInt, sc.SomeString), | ||
"or")); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Humanizer.Configuration; | ||
|
||
|
||
namespace Humanizer | ||
{ | ||
/// <summary> | ||
/// Humanizes an IEnumerable into a human readable list | ||
/// </summary> | ||
public static class CollectionHumanizeExtensions | ||
{ | ||
/// <summary> | ||
/// Formats the collection for display, calling ToString() on each object and | ||
/// using the default separator for the current culture. | ||
/// </summary> | ||
/// <returns></returns> | ||
public static string Humanize<T>(this IEnumerable<T> collection) | ||
{ | ||
return Configurator.CollectionFormatter.Humanize(collection); | ||
} | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling `objectFormatter` on each object | ||
/// and using the default separator for the current culture. | ||
/// </summary> | ||
/// <returns></returns> | ||
public static string Humanize<T>(this IEnumerable<T> collection, Func<T, String> displayFormatter) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No tests for this :( |
||
{ | ||
if (displayFormatter == null) | ||
throw new ArgumentNullException("displayFormatter"); | ||
|
||
return Configurator.CollectionFormatter.Humanize(collection, displayFormatter); | ||
} | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling ToString() on each object | ||
/// and using the provided separator. | ||
/// </summary> | ||
/// <returns></returns> | ||
public static string Humanize<T>(this IEnumerable<T> collection, String separator) | ||
{ | ||
|
||
return Configurator.CollectionFormatter.Humanize(collection, separator); | ||
} | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling `objectFormatter` on each object | ||
/// and using the provided separator. | ||
/// </summary> | ||
/// <returns></returns> | ||
public static string Humanize<T>(this IEnumerable<T> collection, Func<T, String> displayFormatter, String separator) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or this!? |
||
{ | ||
if (displayFormatter == null) | ||
throw new ArgumentNullException("displayFormatter"); | ||
|
||
return Configurator.CollectionFormatter.Humanize(collection, displayFormatter, separator); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Humanizer.Localisation.CollectionFormatters; | ||
|
||
namespace Humanizer.Configuration | ||
{ | ||
internal class CollectionFormatterRegistry : LocaliserRegistry<ICollectionFormatter> | ||
{ | ||
public CollectionFormatterRegistry() | ||
: base(new DefaultCollectionFormatter()) | ||
{ | ||
Register<EnglishCollectionFormatter>("en"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
using Humanizer.Localisation.Formatters; | ||
using Humanizer.Localisation.NumberToWords; | ||
using Humanizer.Localisation.Ordinalizers; | ||
using Humanizer.Localisation.CollectionFormatters; | ||
|
||
namespace Humanizer.Configuration | ||
{ | ||
|
@@ -10,6 +11,16 @@ namespace Humanizer.Configuration | |
/// </summary> | ||
public static class Configurator | ||
{ | ||
private static readonly LocaliserRegistry<ICollectionFormatter> _collectionFormatters = new CollectionFormatterRegistry(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for considering the localisation needs and cleanly implementing it. |
||
|
||
/// <summary> | ||
/// A registry of formatters used to format collections based on the current locale | ||
/// </summary> | ||
public static LocaliserRegistry<ICollectionFormatter> CollectionFormatters | ||
{ | ||
get { return _collectionFormatters; } | ||
} | ||
|
||
private static readonly LocaliserRegistry<IFormatter> _formatters = new FormatterRegistry(); | ||
/// <summary> | ||
/// A registry of formatters used to format strings based on the current locale | ||
|
@@ -36,6 +47,14 @@ public static LocaliserRegistry<IOrdinalizer> Ordinalizers | |
{ | ||
get { return _ordinalizers; } | ||
} | ||
|
||
internal static ICollectionFormatter CollectionFormatter | ||
{ | ||
get | ||
{ | ||
return CollectionFormatters.ResolveForUiCulture(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// The formatter to be used | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,11 @@ | |
<AssemblyOriginatorKeyFile>Humanizer.snk</AssemblyOriginatorKeyFile> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="CollectionHumanizeExtensions.cs" /> | ||
<Compile Include="Configuration\CollectionFormatterRegistry.cs" /> | ||
<Compile Include="Localisation\CollectionFormatters\DefaultCollectionFormatter.cs" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah, it's cool for it to get its own folder. |
||
<Compile Include="Localisation\CollectionFormatters\EnglishCollectionFormatter.cs" /> | ||
<Compile Include="Localisation\CollectionFormatters\ICollectionFormatter.cs" /> | ||
<Compile Include="Localisation\Formatters\SerbianFormatter.cs" /> | ||
<Compile Include="Localisation\Formatters\SlovenianFormatter.cs" /> | ||
<Compile Include="Configuration\LocaliserRegistry.cs" /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Humanizer.Localisation.CollectionFormatters | ||
{ | ||
class DefaultCollectionFormatter : ICollectionFormatter | ||
{ | ||
protected String DefaultSeparator = ""; | ||
|
||
public virtual string Humanize<T>(IEnumerable<T> collection) | ||
{ | ||
return Humanize(collection, o => o.ToString(), DefaultSeparator); | ||
} | ||
|
||
public virtual string Humanize<T>(IEnumerable<T> collection, Func<T, String> objectFormatter) | ||
{ | ||
return Humanize(collection, objectFormatter, DefaultSeparator); | ||
} | ||
|
||
public virtual string Humanize<T>(IEnumerable<T> collection, String separator) | ||
{ | ||
return Humanize(collection, o => o.ToString(), separator); | ||
} | ||
|
||
public virtual string Humanize<T>(IEnumerable<T> collection, Func<T, String> objectFormatter, String separator) | ||
{ | ||
throw new NotImplementedException("A collection formatter for the current culture has not been implemented yet."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha, cool :) |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Humanizer.Localisation.CollectionFormatters | ||
{ | ||
internal class EnglishCollectionFormatter : DefaultCollectionFormatter | ||
{ | ||
public EnglishCollectionFormatter() | ||
{ | ||
DefaultSeparator = "and"; | ||
} | ||
|
||
public override string Humanize<T>(IEnumerable<T> collection, Func<T, String> objectFormatter, String separator) | ||
{ | ||
if (collection == null) | ||
throw new ArgumentException("collection"); | ||
|
||
var enumerable = collection as T[] ?? collection.ToArray(); | ||
|
||
int count = enumerable.Count(); | ||
|
||
if (count == 0) | ||
return ""; | ||
|
||
if (count == 1) | ||
return objectFormatter(enumerable.First()); | ||
|
||
string formatString = count > 2 ? "{0}, {1} {2}" : "{0} {1} {2}"; | ||
|
||
return String.Format(formatString, | ||
String.Join(", ", enumerable.Take(count - 1).Select(objectFormatter)), | ||
separator, | ||
objectFormatter(enumerable.Skip(count - 1).First())); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Humanizer.Localisation.CollectionFormatters | ||
{ | ||
/// <summary> | ||
/// An interface you should implement to localize Humanize for collections | ||
/// </summary> | ||
public interface ICollectionFormatter | ||
{ | ||
/// <summary> | ||
/// Formats the collection for display, calling ToString() on each object. | ||
/// </summary> | ||
/// <returns></returns> | ||
String Humanize<T>(IEnumerable<T> collection); | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling `objectFormatter` on each object. | ||
/// </summary> | ||
/// <returns></returns> | ||
String Humanize<T>(IEnumerable<T> collection, Func<T, String> objectFormatter); | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling ToString() on each object | ||
/// and using `separator` before the final item. | ||
/// </summary> | ||
/// <returns></returns> | ||
String Humanize<T>(IEnumerable<T> collection, String separator); | ||
|
||
/// <summary> | ||
/// Formats the collection for display, calling `objectFormatter` on each object | ||
/// and using `separator` before the final item. | ||
/// </summary> | ||
/// <returns></returns> | ||
String Humanize<T>(IEnumerable<T> collection, Func<T, String> objectFormatter, String separator); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have VS atm; but why did we need to create a
CollectionFormatters
? Could it not live in Formatters folder?