Skip to content
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

Custom enum description attribute property names #277

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ EnumUnderTest.MemberWithoutDescriptionAttribute.Humanize().Transform(To.TitleCas
You are not limited to `DescriptionAttribute` for custom description. Any attribute applied on enum members with a `string Description` property counts.
This is to help with platforms with missing `DescriptionAttribute` and also for allowing subclasses of the `DescriptionAttribute`.

You can even configure the name of the property of attibute to use as description.

`Configurator.EnumDescriptionPropertyLocator = p => p.Name == "Info"`

Hopefully this will help avoid littering enums with unnecessary attributes!

###<a id="dehumanize-enums">Dehumanize Enums</a>
Expand Down
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
###In Development
- [#257](https://github.com/MehdiK/Humanizer/pull/277): Added support for custom enum description attribute property names

[Commits](https://github.com/MehdiK/Humanizer/compare/v1.26.1...master)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public class Configurator
{
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.CollectionFormatters.ICollectionFormatter> CollectionFormatters { get; }
public Humanizer.DateTimeHumanizeStrategy.IDateTimeHumanizeStrategy DateTimeHumanizeStrategy { get; set; }
public System.Func<System.Reflection.PropertyInfo, bool> EnumDescriptionPropertyLocator { get; set; }
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.Formatters.IFormatter> Formatters { get; }
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.NumberToWords.INumberToWordsConverter> NumberToWordsConverters { get; }
public Humanizer.Configuration.LocaliserRegistry<Humanizer.Localisation.Ordinalizers.IOrdinalizer> Ordinalizers { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using Humanizer.Configuration;
using Xunit;

namespace Humanizer.Tests
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly",
Justification = "This is a test only class, and doesn't need a 'proper' IDisposable implementation.")]
public class EnumHumanizeWithCustomDescriptionPropertyNamesTests : IDisposable
{
public EnumHumanizeWithCustomDescriptionPropertyNamesTests()
{
Configurator.EnumDescriptionPropertyLocator = p => p.Name == "Info";
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly",
Justification = "This is a test only class, and doesn't need a 'proper' IDisposable implementation.")]
public void Dispose()
{
Configurator.EnumDescriptionPropertyLocator = null;
}

[Fact]
public void HonorsCustomPropertyAttribute()
{
Assert.Equal(EnumTestsResources.MemberWithCustomPropertyAttribute, EnumUnderTest.MemberWithCustomPropertyAttribute.Humanize());
}

[Fact]
public void CanHumanizeMembersWithoutDescriptionAttribute()
{
Assert.Equal(EnumTestsResources.MemberWithoutDescriptionAttributeSentence, EnumUnderTest.MemberWithoutDescriptionAttribute.Humanize());
}
}
}
13 changes: 13 additions & 0 deletions src/Humanizer.Tests/EnumUnderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public enum EnumUnderTest
MemberWithCustomDescriptionAttribute,
[ImposterDescription(42)]
MemberWithImposterDescriptionAttribute,
[CustomProperty(EnumTestsResources.MemberWithCustomPropertyAttribute)]
MemberWithCustomPropertyAttribute,
MemberWithoutDescriptionAttribute,
ALLCAPITALS
}
Expand All @@ -23,6 +25,7 @@ public class EnumTestsResources
public const string MemberWithDescriptionAttributeSubclass = "Description in Description subclass";
public const string MemberWithCustomDescriptionAttribute = "Description in custom Description attribute";
public const string MemberWithImposterDescriptionAttribute = "Member with imposter description attribute";
public const string MemberWithCustomPropertyAttribute = "Description in custom property attribute";
public const string MemberWithoutDescriptionAttributeSentence = "Member without description attribute";
public const string MemberWithoutDescriptionAttributeTitle = "Member Without Description Attribute";
public const string MemberWithoutDescriptionAttributeLowerCase = "member without description attribute";
Expand Down Expand Up @@ -59,4 +62,14 @@ public override string Description
get { return "Overridden " + base.Description; }
}
}

public class CustomPropertyAttribute : Attribute
{
public string Info { get; set; }

public CustomPropertyAttribute(string info)
{
Info = info;
}
}
}
1 change: 1 addition & 0 deletions src/Humanizer.Tests/Humanizer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="CasingTests.cs" />
<Compile Include="DateHumanize.cs" />
<Compile Include="CollectionHumanizeTests.cs" />
<Compile Include="EnumHumanizeWithCustomDescriptionPropertyNamesTests.cs" />
<Compile Include="Localisation\bg\DateHumanizeTests.cs" />
<Compile Include="Localisation\bg\TimeSpanHumanizeTests.cs" />
<Compile Include="Localisation\cs\DateHumanizeTests.cs" />
Expand Down
14 changes: 14 additions & 0 deletions src/Humanizer/Configuration/Configurator.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Humanizer.DateTimeHumanizeStrategy;
using Humanizer.Localisation.Formatters;
using Humanizer.Localisation.NumberToWords;
Expand Down Expand Up @@ -98,5 +101,16 @@ public static IDateTimeHumanizeStrategy DateTimeHumanizeStrategy
get { return _dateTimeHumanizeStrategy; }
set { _dateTimeHumanizeStrategy = value; }
}

private static readonly Func<PropertyInfo, bool> DefaultEnumDescriptionPropertyLocator = p => p.Name == "Description";
private static Func<PropertyInfo, bool> _enumDescriptionPropertyLocator = DefaultEnumDescriptionPropertyLocator;
/// <summary>
/// A predicate function for description property of attribute to use for Enum.Humanize
/// </summary>
public static Func<PropertyInfo, bool> EnumDescriptionPropertyLocator
{
get { return _enumDescriptionPropertyLocator; }
set { _enumDescriptionPropertyLocator = value ?? DefaultEnumDescriptionPropertyLocator; }
}
}
}
13 changes: 9 additions & 4 deletions src/Humanizer/EnumHumanizeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Reflection;
using Humanizer.Configuration;

namespace Humanizer
{
Expand All @@ -9,7 +10,7 @@ namespace Humanizer
/// </summary>
public static class EnumHumanizeExtensions
{
private static readonly Func<PropertyInfo, bool> DescriptionProperty = p => p.Name == "Description" && p.PropertyType == typeof (string);
private static readonly Func<PropertyInfo, bool> StringTypedProperty = p => p.PropertyType == typeof(string);

/// <summary>
/// Turns an enum member into a human readable string; e.g. AnonymousUser -> Anonymous user. It also honors DescriptionAttribute data annotation
Expand All @@ -19,7 +20,8 @@ public static class EnumHumanizeExtensions
public static string Humanize(this Enum input)
{
Type type = input.GetType();
var memInfo = type.GetMember(input.ToString());
var caseName = input.ToString();
var memInfo = type.GetMember(caseName);

if (memInfo.Length > 0)
{
Expand All @@ -29,7 +31,7 @@ public static string Humanize(this Enum input)
return customDescription;
}

return input.ToString().Humanize();
return caseName.Humanize();
}

// I had to add this method because PCL doesn't have DescriptionAttribute & I didn't want two versions of the code & thus the reflection
Expand All @@ -40,7 +42,10 @@ private static string GetCustomDescription(MemberInfo memberInfo)
foreach (var attr in attrs)
{
var attrType = attr.GetType();
var descriptionProperty = attrType.GetProperties().FirstOrDefault(DescriptionProperty);
var descriptionProperty =
attrType.GetProperties()
.Where(StringTypedProperty)
.FirstOrDefault(Configurator.EnumDescriptionPropertyLocator);
if (descriptionProperty != null)
return descriptionProperty.GetValue(attr, null).ToString();
}
Expand Down